@mohasinac/appkit 4.42.7 → 4.42.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,5 +8,15 @@ export interface AdminStoreEditorViewProps {
8
8
  currentIsVerified?: boolean;
9
9
  currentIsFeatured?: boolean;
10
10
  currentCapabilities?: string[];
11
+ /**
12
+ * 🛑 Required for the note to DISPLAY. Without it `adminNotes` was hardcoded
13
+ * `""` at both the initial-state and the reopen-reset sites, so a stored note
14
+ * could never appear — and because the payload sent `parsed.adminNotes ||
15
+ * undefined`, an always-empty box meant `"" || undefined === undefined`, the
16
+ * route skipped the key, and an existing note could never be CLEARED either.
17
+ * Verified on production 2026-10-02: the document held `adminNotes:
18
+ * "RT3-probe"` while `textarea[name="adminNotes"]` read `""`.
19
+ */
20
+ currentAdminNotes?: string;
11
21
  }
12
- export declare function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities, }: AdminStoreEditorViewProps): React.JSX.Element;
22
+ export declare function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities, currentAdminNotes, }: AdminStoreEditorViewProps): React.JSX.Element;
@@ -69,10 +69,10 @@ const STATUS_OPTIONS = [
69
69
  * right that it is unreadable there.
70
70
  */
71
71
  function CapabilityPicker({ selected, onToggle, }) {
72
- return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Span, { size: "sm", weight: "medium", color: "muted", children: ["Capabilities", _jsxs(Span, { size: "xs", weight: "normal", className: "ml-2", color: "muted", children: ["(", selected.size, " active)"] })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)]", rounded: "xl", border: "default", children: CAPABILITY_GROUPS.map((group) => (_jsxs(Details, { className: "group", children: [_jsxs(Summary, { paddingX: "x-sm", paddingY: "y-xs", weight: "semibold", color: "muted", layout: "flex", align: "center", justify: "between", className: "uppercase tracking-wide hover:bg-[var(--appkit-color-bg)] transition-colors", children: [_jsx(Span, { size: "xs", children: group.label }), _jsxs(Span, { size: "xs", weight: "normal", className: "normal-case", color: "faint", children: [group.caps.filter((c) => selected.has(c.key)).length, "/", group.caps.length] })] }), _jsx(Div, { layout: "grid", paddingY: "y-xs-tall", className: "grid-cols-2 gap-x-2 gap-y-1.5", surface: "muted", padding: "x-sm", children: group.caps.map((cap) => (_jsxs("label", { className: "flex items-center gap-[var(--appkit-space-2)] cursor-pointer text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]", children: [_jsx(Checkbox, { bare: true, checked: selected.has(cap.key), onChange: () => onToggle(cap.key), className: "h-3.5 w-3.5 rounded border-[var(--appkit-color-border)] accent-primary" }), cap.label] }, cap.key))) })] }, group.label))) })] }));
72
+ return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Span, { size: "sm", weight: "medium", color: "muted", children: ["Capabilities", _jsxs(Span, { size: "xs", weight: "normal", className: "ml-2", color: "muted", children: ["(", selected.size, " active)"] })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)]", rounded: "xl", border: "default", children: CAPABILITY_GROUPS.map((group) => (_jsxs(Details, { className: "group", children: [_jsxs(Summary, { paddingX: "x-sm", paddingY: "y-xs", weight: "semibold", color: "muted", layout: "flex", align: "center", justify: "between", className: "uppercase tracking-wide hover:bg-[var(--appkit-color-bg)] transition-colors", children: [_jsx(Span, { size: "xs", children: group.label }), _jsxs(Span, { size: "xs", weight: "normal", className: "normal-case", color: "faint", children: [group.caps.filter((c) => selected.has(c.key)).length, "/", group.caps.length] })] }), _jsx(Div, { layout: "grid", paddingY: "y-xs-tall", className: "grid-cols-2 gap-x-2 gap-y-1.5", surface: "muted", padding: "x-sm", children: group.caps.map((cap) => (_jsx(Checkbox, { label: cap.label, checked: selected.has(cap.key), onChange: () => onToggle(cap.key), wrapperClassName: "flex items-center gap-[var(--appkit-space-2)] cursor-pointer text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]" }, cap.key))) })] }, group.label))) })] }));
73
73
  }
74
74
  // --- Component ---------------------------------------------------------------
75
- export function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities, }) {
75
+ export function AdminStoreEditorView({ open, onClose, storeId, storeName, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities, currentAdminNotes, }) {
76
76
  const queryClient = useQueryClient();
77
77
  const { showToast } = useToast();
78
78
  const [values, setValues] = React.useState(() => ({
@@ -81,7 +81,7 @@ export function AdminStoreEditorView({ open, onClose, storeId, storeName, curren
81
81
  isFeatured: currentIsFeatured ?? false,
82
82
  suspensionReason: "",
83
83
  capabilities: currentCapabilities ?? DEFAULT_CAPABILITIES,
84
- adminNotes: "",
84
+ adminNotes: currentAdminNotes ?? "",
85
85
  }));
86
86
  const handleChange = React.useCallback((partial) => {
87
87
  setValues((prev) => Object.assign({}, prev, partial));
@@ -95,9 +95,9 @@ export function AdminStoreEditorView({ open, onClose, storeId, storeName, curren
95
95
  isFeatured: currentIsFeatured ?? false,
96
96
  suspensionReason: "",
97
97
  capabilities: currentCapabilities ?? DEFAULT_CAPABILITIES,
98
- adminNotes: "",
98
+ adminNotes: currentAdminNotes ?? "",
99
99
  });
100
- }, [open, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities]);
100
+ }, [open, currentStatus, currentIsVerified, currentIsFeatured, currentCapabilities, currentAdminNotes]);
101
101
  const saveMutation = useApiMutation({
102
102
  errorMessage: "Failed to update store.",
103
103
  mutationFn: async () => {
@@ -114,7 +114,9 @@ export function AdminStoreEditorView({ open, onClose, storeId, storeName, curren
114
114
  const parsed = adminStoreUpdateSchema.parse(values);
115
115
  await apiClient.patch(ADMIN_ENDPOINTS.STORE_BY_ID(storeId), {
116
116
  storeStatus: parsed.storeStatus,
117
- adminNotes: parsed.adminNotes || undefined,
117
+ // `?? ""`, NOT `|| undefined` — sending undefined makes the route skip
118
+ // the key, so clearing a note was impossible.
119
+ adminNotes: parsed.adminNotes ?? "",
118
120
  isFeatured: parsed.isFeatured,
119
121
  isVerified: parsed.isVerified,
120
122
  suspensionReason: parsed.suspensionReason || undefined,
@@ -127,5 +127,15 @@ export function AdminStoresView({ children, ...props }) {
127
127
  };
128
128
  return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminStoreEditorView, { open: Boolean(editorRow), onClose: () => setEditorRow(null), storeId: editorRow?.id, storeName: editorRow?.primary, currentStatus: editorRow?.status?.toLowerCase(), currentIsVerified: Boolean(editorRow?._raw?.isVerified), currentIsFeatured: Boolean(editorRow?._raw?.isFeatured), currentCapabilities: Array.isArray(editorRow?._raw?.capabilities)
129
129
  ? editorRow._raw.capabilities
130
+ : undefined,
131
+ /*
132
+ * The list serializer DOES emit `adminNotes`
133
+ * (src/app/api/admin/stores/route.ts), so the row already carries it —
134
+ * it was simply never passed down, which is why the textbox could not
135
+ * display a stored note. Root Cause #38's shape, one hop later: the
136
+ * field survives the API and dies at the prop.
137
+ */
138
+ currentAdminNotes: typeof editorRow?._raw?.adminNotes === "string"
139
+ ? editorRow._raw.adminNotes
130
140
  : undefined })] }));
131
141
  }
@@ -129,6 +129,23 @@ const OWN_STORAGE_BUCKET = process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET ?? ""
129
129
  export function resolveMediaUrl(url, opts) {
130
130
  if (!url)
131
131
  return undefined;
132
+ /*
133
+ * 🛑 Same type-guard as `isStoredMediaRef`, and for the same reason: this is a
134
+ * RENDER-path helper called from `MediaImage`, every avatar, every logo and
135
+ * every card, with whatever the document happens to hold. `!url` catches
136
+ * null/undefined/""; an object or array is truthy and falls straight through
137
+ * to `.startsWith`, throwing `TypeError: e.startsWith is not a function` and
138
+ * taking out the whole subtree it was rendering.
139
+ *
140
+ * Returning undefined is the documented behaviour for a falsy input and is the
141
+ * right answer here too — the caller renders its placeholder instead of
142
+ * crashing. The warn names the shape so the real caller stays findable.
143
+ */
144
+ if (typeof url !== "string") {
145
+ // eslint-disable-next-line no-console -- render path; a throw here kills the subtree
146
+ console.warn("[resolveMediaUrl] expected a string, received", Object.prototype.toString.call(url));
147
+ return undefined;
148
+ }
132
149
  if (url.startsWith(PROXY_PREFIX))
133
150
  return url;
134
151
  // A blob:/data: URI is only ever valid in the tab that created it (e.g. a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "4.42.7",
3
+ "version": "4.42.9",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"