style: format custom widget changes
This commit is contained in:
8 files changed
+74
-47
No files matched your search
@@ -6,23 +6,24 @@ p1_count: 3
|
||||
timestamp: 2026-07-21T11-32-50Z
|
||||
slug: apps-docs-src-components-workshop
|
||||
---
|
||||
|
||||
# Workshop Store Critique
|
||||
|
||||
## Design Health Score
|
||||
|
||||
| # | Heuristic | Score | Key issue |
|
||||
|---|---|---:|---|
|
||||
| 1 | Visibility of system status | 1/4 | Fetch failure can show beside an empty-store message, so the system communicates two incompatible states. |
|
||||
| 2 | Match between system and real world | 3/4 | Widget, CSS, voting, revisions, and comments use familiar language. |
|
||||
| 3 | User control and freedom | 2/4 | Filters are reversible, but the crashed route and failed fetch state offer no useful recovery. |
|
||||
| 4 | Consistency and standards | 2/4 | The submission editor uses CodeMirror, while revision editing falls back to a textarea; several tiny custom controls do not follow the shared button vocabulary. |
|
||||
| 5 | Error prevention | 2/4 | Submission validation exists, but disabled progression does not explain what is missing and destructive actions use a brief inline confirmation. |
|
||||
| 6 | Recognition rather than recall | 3/4 | Labels, type choices, and the submission stepper are understandable without documentation. |
|
||||
| 7 | Flexibility and efficiency | 2/4 | Search covers titles only, signed-out users can select Yours, and installation is reduced to ambiguous Copy and Download actions. |
|
||||
| 8 | Aesthetic and minimalist design | 3/4 | The restrained visual system is clean, but the browse toolbar and card action rows become dense. |
|
||||
| 9 | Help users recover from errors | 0/4 | The production route crashes with React error 130; the fetch error has no retry action or useful context. |
|
||||
| 10 | Help and documentation | 2/4 | The editor explains its fields, but the store does not teach the full inspect, trust, download, and install journey. |
|
||||
| **Total** | | **20/40** | **Needs work before release** |
|
||||
| # | Heuristic | Score | Key issue |
|
||||
| --------- | ----------------------------------- | --------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Visibility of system status | 1/4 | Fetch failure can show beside an empty-store message, so the system communicates two incompatible states. |
|
||||
| 2 | Match between system and real world | 3/4 | Widget, CSS, voting, revisions, and comments use familiar language. |
|
||||
| 3 | User control and freedom | 2/4 | Filters are reversible, but the crashed route and failed fetch state offer no useful recovery. |
|
||||
| 4 | Consistency and standards | 2/4 | The submission editor uses CodeMirror, while revision editing falls back to a textarea; several tiny custom controls do not follow the shared button vocabulary. |
|
||||
| 5 | Error prevention | 2/4 | Submission validation exists, but disabled progression does not explain what is missing and destructive actions use a brief inline confirmation. |
|
||||
| 6 | Recognition rather than recall | 3/4 | Labels, type choices, and the submission stepper are understandable without documentation. |
|
||||
| 7 | Flexibility and efficiency | 2/4 | Search covers titles only, signed-out users can select Yours, and installation is reduced to ambiguous Copy and Download actions. |
|
||||
| 8 | Aesthetic and minimalist design | 3/4 | The restrained visual system is clean, but the browse toolbar and card action rows become dense. |
|
||||
| 9 | Help users recover from errors | 0/4 | The production route crashes with React error 130; the fetch error has no retry action or useful context. |
|
||||
| 10 | Help and documentation | 2/4 | The editor explains its fields, but the store does not teach the full inspect, trust, download, and install journey. |
|
||||
| **Total** | | **20/40** | **Needs work before release** |
|
||||
|
||||
## Anti-Patterns Verdict
|
||||
|
||||
|
||||
@@ -6,23 +6,24 @@ p1_count: 0
|
||||
timestamp: 2026-07-21T12-29-41Z
|
||||
slug: apps-docs-src-components-workshop
|
||||
---
|
||||
|
||||
# Workshop Store Critique — Post-Polish
|
||||
|
||||
## Design Health Score
|
||||
|
||||
| # | Heuristic | Score | Evidence |
|
||||
|---|---|---:|---|
|
||||
| 1 | Visibility of system status | 4/4 | Loading, initial failure, stale refresh failure, empty, and results states are mutually exclusive; recovery actions are visible. |
|
||||
| 2 | Match between system and real world | 4/4 | Actions describe their result: Download widget JSON, Copy CSS, View details, and Install in Homarr. |
|
||||
| 3 | User control and freedom | 4/4 | Filters can be cleared, errors retried, dialogs cancelled, and destructive submission deletion requires explicit confirmation. |
|
||||
| 4 | Consistency and standards | 4/4 | Creation and revision editing share CodeMirror, buttons follow the shared system, and cards use a predictable responsive grid. |
|
||||
| 5 | Error prevention | 3/4 | Validation, disabled progression, pending states, RLS, and confirmations are strong; disabled form progression could explain unmet requirements more explicitly. |
|
||||
| 6 | Recognition rather than recall | 4/4 | Persistent labels, type descriptions, step status, source previews, and installation instructions minimize recall. |
|
||||
| 7 | Flexibility and efficiency | 4/4 | Search spans title, description, and author; signed-in filtering, sorting, responsive controls, and direct detail actions support repeat use. |
|
||||
| 8 | Aesthetic and minimalist design | 3/4 | The catalog is restrained and scannable; long source documents and the complete creator form necessarily remain dense on small screens. |
|
||||
| 9 | Help users recover from errors | 4/4 | Route, listing, edit, report, copy, OAuth, and detail failures remain local and provide a clear next action. |
|
||||
| 10 | Help and documentation | 4/4 | The detail page explains the exact Homarr destination and links to updated Workshop documentation. |
|
||||
| **Total** | | **38/40** | **Release-quality; minor polish remains** |
|
||||
| # | Heuristic | Score | Evidence |
|
||||
| --------- | ----------------------------------- | --------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Visibility of system status | 4/4 | Loading, initial failure, stale refresh failure, empty, and results states are mutually exclusive; recovery actions are visible. |
|
||||
| 2 | Match between system and real world | 4/4 | Actions describe their result: Download widget JSON, Copy CSS, View details, and Install in Homarr. |
|
||||
| 3 | User control and freedom | 4/4 | Filters can be cleared, errors retried, dialogs cancelled, and destructive submission deletion requires explicit confirmation. |
|
||||
| 4 | Consistency and standards | 4/4 | Creation and revision editing share CodeMirror, buttons follow the shared system, and cards use a predictable responsive grid. |
|
||||
| 5 | Error prevention | 3/4 | Validation, disabled progression, pending states, RLS, and confirmations are strong; disabled form progression could explain unmet requirements more explicitly. |
|
||||
| 6 | Recognition rather than recall | 4/4 | Persistent labels, type descriptions, step status, source previews, and installation instructions minimize recall. |
|
||||
| 7 | Flexibility and efficiency | 4/4 | Search spans title, description, and author; signed-in filtering, sorting, responsive controls, and direct detail actions support repeat use. |
|
||||
| 8 | Aesthetic and minimalist design | 3/4 | The catalog is restrained and scannable; long source documents and the complete creator form necessarily remain dense on small screens. |
|
||||
| 9 | Help users recover from errors | 4/4 | Route, listing, edit, report, copy, OAuth, and detail failures remain local and provide a clear next action. |
|
||||
| 10 | Help and documentation | 4/4 | The detail page explains the exact Homarr destination and links to updated Workshop documentation. |
|
||||
| **Total** | | **38/40** | **Release-quality; minor polish remains** |
|
||||
|
||||
## Anti-Patterns Verdict
|
||||
|
||||
|
||||
@@ -6,21 +6,22 @@ p1_count: 2
|
||||
timestamp: 2026-07-22T15-58-15Z
|
||||
slug: apps-docs-src-components-workshop
|
||||
---
|
||||
|
||||
## Design Health Score
|
||||
|
||||
| # | Heuristic | Score | Key Issue |
|
||||
|---|---|---:|---|
|
||||
| 1 | Visibility of System Status | 3 | Strong loading, retry, stale-data, pending, and copy states; successful report/edit actions still end silently. |
|
||||
| 2 | Match System / Real World | 3 | Core Workshop language is clear, but `networkScope`, query/action, and schema terminology assumes technical fluency. |
|
||||
| 3 | User Control and Freedom | 3 | Back, cancel, clear filters, vote toggling, and destructive confirmations are present; destructive actions have no undo. |
|
||||
| 4 | Consistency and Standards | 3 | Shared primitives are coherent, though Docusaurus chrome and the denser app-store vocabulary still feel slightly stitched together. |
|
||||
| 5 | Error Prevention | 2 | Validation and confirmations are good, but install safety and credential context appears after download/copy actions on small screens. |
|
||||
| 6 | Recognition Rather Than Recall | 3 | Author, revision, source, filters, and capabilities are visible; installation still asks users to remember a separate Homarr navigation path. |
|
||||
| 7 | Flexibility and Efficiency | 2 | Search, filters, sort, drag-and-drop, autofill, and comment shortcuts help; the install handoff remains manual. |
|
||||
| 8 | Aesthetic and Minimalist Design | 2 | Hierarchy is clear, but repeated panels, badges, uppercase metadata labels, and overlapping install actions add noise. |
|
||||
| 9 | Error Recognition and Recovery | 3 | Errors are plain-language and usually preserve work; the global action error can be detached from the failed control. |
|
||||
| 10 | Help and Documentation | 3 | The installation guide and source explanation are useful; security terminology lacks inline definitions. |
|
||||
| **Total** | | **27/40** | **Acceptable, significant improvements needed** |
|
||||
| # | Heuristic | Score | Key Issue |
|
||||
| --------- | ------------------------------- | --------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Visibility of System Status | 3 | Strong loading, retry, stale-data, pending, and copy states; successful report/edit actions still end silently. |
|
||||
| 2 | Match System / Real World | 3 | Core Workshop language is clear, but `networkScope`, query/action, and schema terminology assumes technical fluency. |
|
||||
| 3 | User Control and Freedom | 3 | Back, cancel, clear filters, vote toggling, and destructive confirmations are present; destructive actions have no undo. |
|
||||
| 4 | Consistency and Standards | 3 | Shared primitives are coherent, though Docusaurus chrome and the denser app-store vocabulary still feel slightly stitched together. |
|
||||
| 5 | Error Prevention | 2 | Validation and confirmations are good, but install safety and credential context appears after download/copy actions on small screens. |
|
||||
| 6 | Recognition Rather Than Recall | 3 | Author, revision, source, filters, and capabilities are visible; installation still asks users to remember a separate Homarr navigation path. |
|
||||
| 7 | Flexibility and Efficiency | 2 | Search, filters, sort, drag-and-drop, autofill, and comment shortcuts help; the install handoff remains manual. |
|
||||
| 8 | Aesthetic and Minimalist Design | 2 | Hierarchy is clear, but repeated panels, badges, uppercase metadata labels, and overlapping install actions add noise. |
|
||||
| 9 | Error Recognition and Recovery | 3 | Errors are plain-language and usually preserve work; the global action error can be detached from the failed control. |
|
||||
| 10 | Help and Documentation | 3 | The installation guide and source explanation are useful; security terminology lacks inline definitions. |
|
||||
| **Total** | | **27/40** | **Acceptable, significant improvements needed** |
|
||||
|
||||
## Anti-Patterns Verdict
|
||||
|
||||
|
||||
+20
-2
@@ -1,7 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Alert, Button, Card, Center, PasswordInput, Select, Stack, Text, TextInput, ThemeIcon, Title } from "@mantine/core";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Center,
|
||||
PasswordInput,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
ThemeIcon,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { IconCheck, IconKey, IconLock } from "@tabler/icons-react";
|
||||
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
@@ -89,7 +101,13 @@ export function CustomWidgetConfigurationEntry({ token }: { token: string }) {
|
||||
{t("source", { name: details.sourceName })}
|
||||
</Text>
|
||||
</Card>
|
||||
<TextInput label={t("baseUrl")} type="url" value={baseUrl} onChange={(event) => setBaseUrl(event.currentTarget.value)} required />
|
||||
<TextInput
|
||||
label={t("baseUrl")}
|
||||
type="url"
|
||||
value={baseUrl}
|
||||
onChange={(event) => setBaseUrl(event.currentTarget.value)}
|
||||
required
|
||||
/>
|
||||
<Select
|
||||
label={t("networkScope")}
|
||||
data={["public", "private", "loopback"]}
|
||||
|
||||
@@ -5,7 +5,11 @@ import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { applyCustomWidgetSourceSetup, getCustomWidgetSourceSetups, getImportReview } from "@homarr/custom-widgets/core";
|
||||
import {
|
||||
applyCustomWidgetSourceSetup,
|
||||
getCustomWidgetSourceSetups,
|
||||
getImportReview,
|
||||
} from "@homarr/custom-widgets/core";
|
||||
import type { CustomWidgetSecretKind, HomarrCustomWidgetV2 } from "@homarr/custom-widgets/core";
|
||||
import {
|
||||
createCustomWidgetSourceSetupValues,
|
||||
|
||||
@@ -81,9 +81,7 @@ onRecordAfterCreateSuccess((event) => {
|
||||
const submission = event.app.findRecordById("submissions", event.record.get("submission"));
|
||||
const reporter = event.app.findRecordById("users", event.record.get("reporter"));
|
||||
const publicOrigin = $os.getenv("WORKSHOP_PUBLIC_ORIGIN").replace(/\/$/, "");
|
||||
const admins = event.app
|
||||
.findAllRecords("users")
|
||||
.filter((user) => user.getBool("isAdmin") && Boolean(user.email()));
|
||||
const admins = event.app.findAllRecords("users").filter((user) => user.getBool("isAdmin") && Boolean(user.email()));
|
||||
|
||||
if (!publicOrigin || admins.length === 0) {
|
||||
event.next();
|
||||
|
||||
@@ -56,7 +56,9 @@ export async function getCustomWidgetConfigurationRequest(id: string) {
|
||||
const candidate = client ? await client.get(`${CONFIGURATION_REQUEST_PREFIX}${id}`) : localRequests.get(id);
|
||||
if (!candidate) return null;
|
||||
try {
|
||||
const request = (typeof candidate === "string" ? JSON.parse(candidate) : candidate) as CustomWidgetConfigurationRequest;
|
||||
const request = (
|
||||
typeof candidate === "string" ? JSON.parse(candidate) : candidate
|
||||
) as CustomWidgetConfigurationRequest;
|
||||
if (request.expiresAt <= Date.now()) {
|
||||
if (client) await client.del(`${CONFIGURATION_REQUEST_PREFIX}${id}`);
|
||||
else localRequests.delete(id);
|
||||
|
||||
@@ -26,7 +26,9 @@ describe("custom widget source configuration requests", () => {
|
||||
await releaseCustomWidgetConfigurationRequest(request.id);
|
||||
expect(await claimCustomWidgetConfigurationRequest(request.id)).not.toBeNull();
|
||||
await completeCustomWidgetConfigurationRequest(request.id);
|
||||
expect(await getCustomWidgetConfigurationRequestForUser(request.id, "user-1")).toMatchObject({ status: "completed" });
|
||||
expect(await getCustomWidgetConfigurationRequestForUser(request.id, "user-1")).toMatchObject({
|
||||
status: "completed",
|
||||
});
|
||||
expect(await claimCustomWidgetConfigurationRequest(request.id)).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user