@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) => (
|
|
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
|
-
|
|
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
|
}
|
package/dist/utils/media-url.js
CHANGED
|
@@ -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
|