@emdash-cms/admin 0.6.0 → 1.0.0
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.
- package/dist/{LocaleDirectionProvider-BS3RseHk.js → LocaleDirectionProvider-B43-InGI.js} +26 -13
- package/dist/LocaleDirectionProvider-B43-InGI.js.map +1 -0
- package/dist/{config-NP8UHbds.d.ts → config-h7tWQRka.d.ts} +1 -1
- package/dist/{config-NP8UHbds.d.ts.map → config-h7tWQRka.d.ts.map} +1 -1
- package/dist/index.d.ts +96 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +948 -381
- package/dist/index.js.map +1 -1
- package/dist/locales/ar/messages.mjs +1 -1
- package/dist/locales/de/messages.mjs +1 -1
- package/dist/locales/en/messages.mjs +1 -1
- package/dist/locales/es-419/messages.mjs +1 -1
- package/dist/locales/eu/messages.mjs +1 -1
- package/dist/locales/fa/messages.mjs +1 -0
- package/dist/locales/fr/messages.mjs +1 -1
- package/dist/locales/index.d.ts +1 -1
- package/dist/locales/index.js +1 -1
- package/dist/locales/ja/messages.mjs +1 -1
- package/dist/locales/ko/messages.mjs +1 -1
- package/dist/locales/pl/messages.mjs +1 -0
- package/dist/locales/pseudo/messages.mjs +1 -1
- package/dist/locales/pt-BR/messages.mjs +1 -1
- package/dist/locales/zh-CN/messages.mjs +1 -1
- package/dist/locales/zh-TW/messages.mjs +1 -1
- package/dist/messages-B6ZguVfS.js +6 -0
- package/dist/messages-B6ZguVfS.js.map +1 -0
- package/dist/messages-BOnMi1Vb.js +6 -0
- package/dist/messages-BOnMi1Vb.js.map +1 -0
- package/dist/messages-BZuRVH-D.js +6 -0
- package/dist/messages-BZuRVH-D.js.map +1 -0
- package/dist/messages-Bc_PDiKw.js +6 -0
- package/dist/messages-Bc_PDiKw.js.map +1 -0
- package/dist/messages-Bddg68eH.js +6 -0
- package/dist/messages-Bddg68eH.js.map +1 -0
- package/dist/messages-BoP-RHEG.js +6 -0
- package/dist/messages-BoP-RHEG.js.map +1 -0
- package/dist/messages-C4hNtyU5.js +6 -0
- package/dist/messages-C4hNtyU5.js.map +1 -0
- package/dist/messages-CMwt5Gv-.js +6 -0
- package/dist/messages-CMwt5Gv-.js.map +1 -0
- package/dist/messages-CnliZpid.js +6 -0
- package/dist/messages-CnliZpid.js.map +1 -0
- package/dist/messages-CseAZVO2.js +6 -0
- package/dist/messages-CseAZVO2.js.map +1 -0
- package/dist/messages-DRyNt1qC.js +6 -0
- package/dist/messages-DRyNt1qC.js.map +1 -0
- package/dist/messages-DTehFUkD.js +6 -0
- package/dist/messages-DTehFUkD.js.map +1 -0
- package/dist/messages-DeQMOYvT.js +6 -0
- package/dist/messages-DeQMOYvT.js.map +1 -0
- package/dist/messages-Do3BokvI.js +6 -0
- package/dist/messages-Do3BokvI.js.map +1 -0
- package/dist/{plugins-XhZqfegd.js → plugins-DUJY_8HN.js} +9 -2
- package/dist/plugins-DUJY_8HN.js.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/dist/LocaleDirectionProvider-BS3RseHk.js.map +0 -1
- package/dist/messages-57qx1FXa.js +0 -6
- package/dist/messages-57qx1FXa.js.map +0 -1
- package/dist/messages-8uPA5VDB.js +0 -6
- package/dist/messages-8uPA5VDB.js.map +0 -1
- package/dist/messages-B60-1lfB.js +0 -6
- package/dist/messages-B60-1lfB.js.map +0 -1
- package/dist/messages-Bb5acOxw.js +0 -6
- package/dist/messages-Bb5acOxw.js.map +0 -1
- package/dist/messages-Bc3iE3Xx.js +0 -6
- package/dist/messages-Bc3iE3Xx.js.map +0 -1
- package/dist/messages-BxPoHitx.js +0 -6
- package/dist/messages-BxPoHitx.js.map +0 -1
- package/dist/messages-CAhWq719.js +0 -6
- package/dist/messages-CAhWq719.js.map +0 -1
- package/dist/messages-CLJeZfc0.js +0 -6
- package/dist/messages-CLJeZfc0.js.map +0 -1
- package/dist/messages-CjWV7b4D.js +0 -6
- package/dist/messages-CjWV7b4D.js.map +0 -1
- package/dist/messages-DPxhCxJU.js +0 -6
- package/dist/messages-DPxhCxJU.js.map +0 -1
- package/dist/messages-DYWnQ-qc.js +0 -6
- package/dist/messages-DYWnQ-qc.js.map +0 -1
- package/dist/messages-J31OZzeY.js +0 -6
- package/dist/messages-J31OZzeY.js.map +0 -1
- package/dist/plugins-XhZqfegd.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as
|
|
2
|
-
import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-
|
|
1
|
+
import { c as fetchAuthMode, d as throwResponseError, i as fetchPlugins, l as fetchManifest, n as enablePlugin, o as API_BASE, r as fetchPlugin, s as apiFetch, t as disablePlugin, u as parseApiResponse } from "./plugins-DUJY_8HN.js";
|
|
2
|
+
import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-B43-InGI.js";
|
|
3
3
|
import "./locales/index.js";
|
|
4
4
|
import { Badge, Button, Checkbox, CommandPalette, Dialog, Input, InputArea, Label, LinkButton, Loader, Popover, Select, Sidebar as KumoSidebar, Switch, Tabs, Toast, Toasty, Tooltip, buttonVariants, useSidebar } from "@cloudflare/kumo";
|
|
5
5
|
import { i18n } from "@lingui/core";
|
|
@@ -10,7 +10,7 @@ import * as React from "react";
|
|
|
10
10
|
import { createContext, useCallback, useContext, useEffect, useState } from "react";
|
|
11
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { Dialog as Dialog$1 } from "@cloudflare/kumo/primitives";
|
|
13
|
-
import { ArrowCounterClockwise, ArrowLeft, ArrowRight, ArrowSquareOut, ArrowUUpLeft, ArrowUUpRight, ArrowsClockwise, ArrowsInSimple, ArrowsLeftRight, ArrowsOutSimple, BracketsCurly, Calendar, CalendarBlank, CaretDown, CaretLeft, CaretRight, CaretUp, ChatCircle, Check, CheckCircle, CircleDashed, CircleNotch, ClockCounterClockwise, Cloud, Code, CodeBlock, Copy, Cube, Database, DeviceMobile, DotsSixVertical, DownloadSimple, Envelope, EnvelopeSimple, Eye, EyeSlash, File, FileArrowDown, FileText, FloppyDisk, FolderOpen, Gear, GithubLogo, Globe, GlobeSimple, GridFour, HardDrive, Hash, Image as Image$1, ImageSquare, Info, Key, Link as Link$2, LinkBreak, LinkSimple, List, ListBullets, ListChecks, ListNumbers, MagnifyingGlass, Minus, Monitor, Moon, Palette, PaperPlaneTilt, Paragraph, Pencil, PencilSimple, PlugsConnected, Plus, Prohibit, PuzzlePiece, Quotes, Rows, Ruler, ShareNetwork, Shield, ShieldCheck, ShieldWarning, SignOut, SlidersHorizontal, Sparkle, SquaresFour, Stack, Storefront, Sun, TextAlignCenter, TextAlignLeft, TextAlignRight, TextB, TextHOne, TextHThree, TextHTwo, TextItalic, TextStrikethrough, TextT, TextUnderline, ToggleLeft, Trash, Upload, User, UserCircle, UserPlus, Users, Warning, WarningCircle, WebhooksLogo, X, YoutubeLogo } from "@phosphor-icons/react";
|
|
13
|
+
import { ArrowCounterClockwise, ArrowLeft, ArrowRight, ArrowSquareOut, ArrowUUpLeft, ArrowUUpRight, ArrowsClockwise, ArrowsInSimple, ArrowsLeftRight, ArrowsOutSimple, BracketsCurly, Calendar, CalendarBlank, CaretDown, CaretLeft, CaretRight, CaretUp, CaretUpDown, ChatCircle, Check, CheckCircle, CircleDashed, CircleNotch, ClockCounterClockwise, Cloud, Code, CodeBlock, Copy, Cube, Database, DeviceMobile, DotsSixVertical, DownloadSimple, Envelope, EnvelopeSimple, Eye, EyeSlash, File, FileArrowDown, FileText, FloppyDisk, FolderOpen, Gear, GithubLogo, Globe, GlobeSimple, GridFour, HardDrive, Hash, Image as Image$1, ImageSquare, Info, Key, Link as Link$2, LinkBreak, LinkSimple, List, ListBullets, ListChecks, ListNumbers, MagnifyingGlass, Minus, Monitor, Moon, Palette, PaperPlaneTilt, Paragraph, Pencil, PencilSimple, PlugsConnected, Plus, Prohibit, PuzzlePiece, Quotes, Rows, Ruler, ShareNetwork, Shield, ShieldCheck, ShieldWarning, SignOut, SlidersHorizontal, Sparkle, SquaresFour, Stack, Storefront, Sun, TextAlignCenter, TextAlignLeft, TextAlignRight, TextB, TextHOne, TextHThree, TextHTwo, TextItalic, TextStrikethrough, TextT, TextUnderline, ToggleLeft, Trash, Upload, User, UserCircle, UserPlus, Users, Warning, WarningCircle, WebhooksLogo, X, YoutubeLogo } from "@phosphor-icons/react";
|
|
14
14
|
import { clsx } from "clsx";
|
|
15
15
|
import { twMerge } from "tailwind-merge";
|
|
16
16
|
import { DndContext, DragOverlay, KeyboardSensor, PointerSensor, closestCenter, rectIntersection, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
|
|
@@ -100,6 +100,34 @@ function useTheme() {
|
|
|
100
100
|
return context;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/lib/auth-provider-context.tsx
|
|
105
|
+
const AuthProviderContext = createContext({});
|
|
106
|
+
/**
|
|
107
|
+
* Provider that makes auth provider components available to all descendants
|
|
108
|
+
*/
|
|
109
|
+
function AuthProviderProvider({ children, authProviders }) {
|
|
110
|
+
return /* @__PURE__ */ jsx(AuthProviderContext.Provider, {
|
|
111
|
+
value: authProviders,
|
|
112
|
+
children
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get all auth provider modules
|
|
117
|
+
*/
|
|
118
|
+
function useAuthProviders() {
|
|
119
|
+
return useContext(AuthProviderContext);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get auth providers as an ordered array (buttons first, then forms)
|
|
123
|
+
*/
|
|
124
|
+
function useAuthProviderList() {
|
|
125
|
+
const providers = useContext(AuthProviderContext);
|
|
126
|
+
return Object.values(providers).toSorted((a, b) => {
|
|
127
|
+
return (a.LoginForm ? 1 : 0) - (b.LoginForm ? 1 : 0);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
103
131
|
//#endregion
|
|
104
132
|
//#region src/lib/plugin-context.tsx
|
|
105
133
|
const PluginAdminContext = createContext({});
|
|
@@ -1105,6 +1133,8 @@ async function fetchContentList(collection, options) {
|
|
|
1105
1133
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
1106
1134
|
if (options?.status) params.set("status", options.status);
|
|
1107
1135
|
if (options?.locale) params.set("locale", options.locale);
|
|
1136
|
+
if (options?.orderBy) params.set("orderBy", options.orderBy);
|
|
1137
|
+
if (options?.order) params.set("order", options.order);
|
|
1108
1138
|
return parseApiResponse(await apiFetch(`${API_BASE}/content/${collection}${params.toString() ? `?${params}` : ""}`), "Failed to fetch content");
|
|
1109
1139
|
}
|
|
1110
1140
|
/**
|
|
@@ -2261,6 +2291,10 @@ const API_TOKEN_SCOPES = {
|
|
|
2261
2291
|
MediaWrite: "media:write",
|
|
2262
2292
|
SchemaRead: "schema:read",
|
|
2263
2293
|
SchemaWrite: "schema:write",
|
|
2294
|
+
TaxonomiesManage: "taxonomies:manage",
|
|
2295
|
+
MenusManage: "menus:manage",
|
|
2296
|
+
SettingsRead: "settings:read",
|
|
2297
|
+
SettingsManage: "settings:manage",
|
|
2264
2298
|
Admin: "admin"
|
|
2265
2299
|
};
|
|
2266
2300
|
/**
|
|
@@ -2921,7 +2955,7 @@ var PluginFieldErrorBoundary = class extends React.Component {
|
|
|
2921
2955
|
* Each item is a collapsible card containing the defined sub-fields.
|
|
2922
2956
|
* Items can be added, removed, and reordered via drag-and-drop.
|
|
2923
2957
|
*/
|
|
2924
|
-
function ensureKeys(items) {
|
|
2958
|
+
function ensureKeys$1(items) {
|
|
2925
2959
|
return items.map((item, i) => {
|
|
2926
2960
|
const obj = typeof item === "object" && item !== null ? item : {};
|
|
2927
2961
|
return {
|
|
@@ -2930,20 +2964,28 @@ function ensureKeys(items) {
|
|
|
2930
2964
|
};
|
|
2931
2965
|
});
|
|
2932
2966
|
}
|
|
2933
|
-
function stripKeys(items) {
|
|
2967
|
+
function stripKeys$1(items) {
|
|
2934
2968
|
return items.map(({ _key, ...rest }) => rest);
|
|
2935
2969
|
}
|
|
2936
2970
|
function RepeaterField({ label, id, value, onChange, subFields, minItems = 0, maxItems }) {
|
|
2937
2971
|
const { _: _t } = useLingui();
|
|
2938
2972
|
const rawItems = Array.isArray(value) ? value : [];
|
|
2939
|
-
const [items, setItems] = React.useState(() => ensureKeys(rawItems));
|
|
2973
|
+
const [items, setItems] = React.useState(() => ensureKeys$1(rawItems));
|
|
2940
2974
|
const [collapsedItems, setCollapsedItems] = React.useState(/* @__PURE__ */ new Set());
|
|
2941
2975
|
React.useEffect(() => {
|
|
2942
|
-
|
|
2976
|
+
const incoming = Array.isArray(value) ? value : [];
|
|
2977
|
+
setItems((prev) => incoming.map((item, i) => {
|
|
2978
|
+
const obj = typeof item === "object" && item !== null ? item : {};
|
|
2979
|
+
const existingKey = obj._key || prev[i]?._key;
|
|
2980
|
+
return {
|
|
2981
|
+
...obj,
|
|
2982
|
+
_key: existingKey || `item-${i}-${Date.now()}`
|
|
2983
|
+
};
|
|
2984
|
+
}));
|
|
2943
2985
|
}, [value]);
|
|
2944
2986
|
const emitChange = (updated) => {
|
|
2945
2987
|
setItems(updated);
|
|
2946
|
-
onChange(stripKeys(updated));
|
|
2988
|
+
onChange(stripKeys$1(updated));
|
|
2947
2989
|
};
|
|
2948
2990
|
const handleAdd = () => {
|
|
2949
2991
|
if (maxItems && items.length >= maxItems) return;
|
|
@@ -3109,14 +3151,16 @@ function SubFieldInput({ subField, value, onChange }) {
|
|
|
3109
3151
|
label: subField.label,
|
|
3110
3152
|
value: typeof value === "string" ? value : "",
|
|
3111
3153
|
onChange: (e) => onChange(e.target.value),
|
|
3112
|
-
required: subField.required
|
|
3154
|
+
required: subField.required,
|
|
3155
|
+
dir: "auto"
|
|
3113
3156
|
});
|
|
3114
3157
|
case "text": return /* @__PURE__ */ jsx(InputArea, {
|
|
3115
3158
|
label: subField.label,
|
|
3116
3159
|
value: typeof value === "string" ? value : "",
|
|
3117
3160
|
onChange: (e) => onChange(e.target.value),
|
|
3118
3161
|
required: subField.required,
|
|
3119
|
-
rows: 3
|
|
3162
|
+
rows: 3,
|
|
3163
|
+
dir: "auto"
|
|
3120
3164
|
});
|
|
3121
3165
|
case "number":
|
|
3122
3166
|
case "integer": return /* @__PURE__ */ jsx(Input, {
|
|
@@ -6630,6 +6674,24 @@ function SlashCommandMenu({ state, onCommand, onClose: _onClose, setSelectedInde
|
|
|
6630
6674
|
}, item.id))
|
|
6631
6675
|
}), document.body);
|
|
6632
6676
|
}
|
|
6677
|
+
function getPluginBlockDefaultValues(fields) {
|
|
6678
|
+
const defaults = {};
|
|
6679
|
+
for (const field of fields ?? []) {
|
|
6680
|
+
const initialValue = "initial_value" in field ? field.initial_value : void 0;
|
|
6681
|
+
if (initialValue !== void 0) defaults[field.action_id] = initialValue;
|
|
6682
|
+
}
|
|
6683
|
+
return defaults;
|
|
6684
|
+
}
|
|
6685
|
+
function buildPluginBlockFormValues(block, initialValues) {
|
|
6686
|
+
const defaults = getPluginBlockDefaultValues(block?.fields);
|
|
6687
|
+
return initialValues ? {
|
|
6688
|
+
...defaults,
|
|
6689
|
+
...initialValues
|
|
6690
|
+
} : defaults;
|
|
6691
|
+
}
|
|
6692
|
+
function hasPluginBlockFormData(values) {
|
|
6693
|
+
return Object.values(values).some((value) => value !== void 0 && value !== null && value !== "");
|
|
6694
|
+
}
|
|
6633
6695
|
/**
|
|
6634
6696
|
* Plugin block insertion/editing modal.
|
|
6635
6697
|
* When the block has `fields`, renders Block Kit elements.
|
|
@@ -6640,8 +6702,7 @@ function PluginBlockModal({ block, initialValues, onClose, onInsert }) {
|
|
|
6640
6702
|
const inputRef = React.useRef(null);
|
|
6641
6703
|
React.useEffect(() => {
|
|
6642
6704
|
if (block) {
|
|
6643
|
-
|
|
6644
|
-
else setFormValues({});
|
|
6705
|
+
setFormValues(buildPluginBlockFormValues(block, initialValues));
|
|
6645
6706
|
if (!block.fields || block.fields.length === 0) setTimeout(() => inputRef.current?.focus(), 0);
|
|
6646
6707
|
}
|
|
6647
6708
|
}, [block, initialValues]);
|
|
@@ -6661,7 +6722,7 @@ function PluginBlockModal({ block, initialValues, onClose, onInsert }) {
|
|
|
6661
6722
|
};
|
|
6662
6723
|
const isEditing = !!initialValues;
|
|
6663
6724
|
const hasFields = block?.fields && block.fields.length > 0;
|
|
6664
|
-
const canSubmit = hasFields ?
|
|
6725
|
+
const canSubmit = hasFields ? hasPluginBlockFormData(formValues) : typeof formValues.id === "string" && formValues.id.trim().length > 0;
|
|
6665
6726
|
return /* @__PURE__ */ jsx(Dialog.Root, {
|
|
6666
6727
|
open: !!block,
|
|
6667
6728
|
onOpenChange: (open) => !open && onClose(),
|
|
@@ -6694,7 +6755,7 @@ function PluginBlockModal({ block, initialValues, onClose, onInsert }) {
|
|
|
6694
6755
|
}), /* @__PURE__ */ jsxs("form", {
|
|
6695
6756
|
onSubmit: handleSubmit,
|
|
6696
6757
|
children: [/* @__PURE__ */ jsx("div", {
|
|
6697
|
-
className: "py-4 space-y-4",
|
|
6758
|
+
className: "py-4 space-y-4 max-h-[70vh] overflow-y-auto -mx-1 px-1",
|
|
6698
6759
|
children: hasFields ? block.fields.map((field) => /* @__PURE__ */ jsx(BlockKitField, {
|
|
6699
6760
|
field,
|
|
6700
6761
|
pluginId: block.pluginId,
|
|
@@ -6781,12 +6842,244 @@ function BlockKitField({ field, pluginId, value, onChange }) {
|
|
|
6781
6842
|
children: field.label
|
|
6782
6843
|
})]
|
|
6783
6844
|
});
|
|
6845
|
+
case "repeater": return /* @__PURE__ */ jsx(BlockKitRepeater, {
|
|
6846
|
+
field,
|
|
6847
|
+
pluginId,
|
|
6848
|
+
value,
|
|
6849
|
+
onChange
|
|
6850
|
+
});
|
|
6784
6851
|
default: return /* @__PURE__ */ jsxs("div", {
|
|
6785
6852
|
className: "text-sm text-kumo-subtle",
|
|
6786
6853
|
children: ["Unknown field type: ", field.type]
|
|
6787
6854
|
});
|
|
6788
6855
|
}
|
|
6789
6856
|
}
|
|
6857
|
+
function ensureKeys(items) {
|
|
6858
|
+
return items.map((item, i) => {
|
|
6859
|
+
const obj = typeof item === "object" && item !== null ? item : {};
|
|
6860
|
+
return {
|
|
6861
|
+
...obj,
|
|
6862
|
+
_key: obj._key || `item-${i}-${Date.now()}`
|
|
6863
|
+
};
|
|
6864
|
+
});
|
|
6865
|
+
}
|
|
6866
|
+
function stripKeys(items) {
|
|
6867
|
+
return items.map(({ _key, ...rest }) => rest);
|
|
6868
|
+
}
|
|
6869
|
+
function BlockKitRepeater({ field, pluginId, value, onChange }) {
|
|
6870
|
+
const { _: _t2 } = useLingui();
|
|
6871
|
+
const rawItems = React.useMemo(() => Array.isArray(value) ? value : [], [value]);
|
|
6872
|
+
const [items, setItems] = React.useState(() => ensureKeys(rawItems));
|
|
6873
|
+
const [expanded, setExpanded] = React.useState(/* @__PURE__ */ new Set());
|
|
6874
|
+
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
6875
|
+
const lastEmittedRef = React.useRef(null);
|
|
6876
|
+
React.useEffect(() => {
|
|
6877
|
+
if (lastEmittedRef.current === rawItems) return;
|
|
6878
|
+
setItems((prev) => rawItems.map((item, i) => {
|
|
6879
|
+
const obj = typeof item === "object" && item !== null ? item : {};
|
|
6880
|
+
const existingKey = obj._key || prev[i]?._key;
|
|
6881
|
+
return {
|
|
6882
|
+
...obj,
|
|
6883
|
+
_key: existingKey || `item-${i}-${Date.now()}`
|
|
6884
|
+
};
|
|
6885
|
+
}));
|
|
6886
|
+
}, [rawItems]);
|
|
6887
|
+
const minItems = field.min_items ?? 0;
|
|
6888
|
+
const maxItems = field.max_items;
|
|
6889
|
+
const canAdd = maxItems === void 0 || items.length < maxItems;
|
|
6890
|
+
const canRemove = items.length > minItems;
|
|
6891
|
+
const addButtonLabel = field.item_label ? _t2({
|
|
6892
|
+
id: "3nLdaX",
|
|
6893
|
+
message: "Add {0}",
|
|
6894
|
+
values: { 0: field.item_label }
|
|
6895
|
+
}) : _t2({
|
|
6896
|
+
id: "NQ8llq",
|
|
6897
|
+
message: "Add item"
|
|
6898
|
+
});
|
|
6899
|
+
const emit = (next) => {
|
|
6900
|
+
setItems(next);
|
|
6901
|
+
const stripped = stripKeys(next);
|
|
6902
|
+
lastEmittedRef.current = stripped;
|
|
6903
|
+
onChange(field.action_id, stripped);
|
|
6904
|
+
};
|
|
6905
|
+
const handleAdd = () => {
|
|
6906
|
+
if (!canAdd) return;
|
|
6907
|
+
const newItem = { _key: `item-${Date.now()}-${Math.random().toString(36).slice(2, 7)}` };
|
|
6908
|
+
for (const sf of field.fields) switch (sf.type) {
|
|
6909
|
+
case "toggle":
|
|
6910
|
+
newItem[sf.action_id] = false;
|
|
6911
|
+
break;
|
|
6912
|
+
case "number_input":
|
|
6913
|
+
newItem[sf.action_id] = void 0;
|
|
6914
|
+
break;
|
|
6915
|
+
default: newItem[sf.action_id] = "";
|
|
6916
|
+
}
|
|
6917
|
+
setExpanded((prev) => {
|
|
6918
|
+
const next = new Set(prev);
|
|
6919
|
+
next.add(newItem._key);
|
|
6920
|
+
return next;
|
|
6921
|
+
});
|
|
6922
|
+
emit([...items, newItem]);
|
|
6923
|
+
};
|
|
6924
|
+
const handleRemove = (key) => {
|
|
6925
|
+
if (!canRemove) return;
|
|
6926
|
+
setExpanded((prev) => {
|
|
6927
|
+
if (!prev.has(key)) return prev;
|
|
6928
|
+
const next = new Set(prev);
|
|
6929
|
+
next.delete(key);
|
|
6930
|
+
return next;
|
|
6931
|
+
});
|
|
6932
|
+
emit(items.filter((it) => it._key !== key));
|
|
6933
|
+
};
|
|
6934
|
+
const handleItemChange = (key, subActionId, subValue) => {
|
|
6935
|
+
emit(items.map((it) => it._key === key ? {
|
|
6936
|
+
...it,
|
|
6937
|
+
[subActionId]: subValue
|
|
6938
|
+
} : it));
|
|
6939
|
+
};
|
|
6940
|
+
const handleDragEnd = (event) => {
|
|
6941
|
+
const { active, over } = event;
|
|
6942
|
+
if (!over || active.id === over.id) return;
|
|
6943
|
+
const oldIndex = items.findIndex((it) => it._key === active.id);
|
|
6944
|
+
const newIndex = items.findIndex((it) => it._key === over.id);
|
|
6945
|
+
if (oldIndex === -1 || newIndex === -1) return;
|
|
6946
|
+
emit(arrayMove(items, oldIndex, newIndex));
|
|
6947
|
+
};
|
|
6948
|
+
const toggleExpanded = (key) => {
|
|
6949
|
+
setExpanded((prev) => {
|
|
6950
|
+
const next = new Set(prev);
|
|
6951
|
+
if (next.has(key)) next.delete(key);
|
|
6952
|
+
else next.add(key);
|
|
6953
|
+
return next;
|
|
6954
|
+
});
|
|
6955
|
+
};
|
|
6956
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
6957
|
+
className: "space-y-2",
|
|
6958
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
6959
|
+
className: "flex items-center justify-between",
|
|
6960
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
6961
|
+
className: "text-sm font-medium",
|
|
6962
|
+
children: [field.label, items.length > 0 && /* @__PURE__ */ jsxs("span", {
|
|
6963
|
+
className: "ms-2 text-kumo-subtle font-normal",
|
|
6964
|
+
children: [
|
|
6965
|
+
"(",
|
|
6966
|
+
items.length,
|
|
6967
|
+
")"
|
|
6968
|
+
]
|
|
6969
|
+
})]
|
|
6970
|
+
}), canAdd && /* @__PURE__ */ jsx(Button, {
|
|
6971
|
+
variant: "outline",
|
|
6972
|
+
size: "sm",
|
|
6973
|
+
icon: /* @__PURE__ */ jsx(Plus, {}),
|
|
6974
|
+
onClick: handleAdd,
|
|
6975
|
+
type: "button",
|
|
6976
|
+
children: addButtonLabel
|
|
6977
|
+
})]
|
|
6978
|
+
}), items.length === 0 ? /* @__PURE__ */ jsxs("div", {
|
|
6979
|
+
className: "border-2 border-dashed rounded-lg p-6 text-center text-kumo-subtle",
|
|
6980
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
6981
|
+
className: "text-sm",
|
|
6982
|
+
children: _t2({
|
|
6983
|
+
id: "bWmJ0A",
|
|
6984
|
+
message: "No items yet"
|
|
6985
|
+
})
|
|
6986
|
+
}), canAdd && /* @__PURE__ */ jsx(Button, {
|
|
6987
|
+
variant: "outline",
|
|
6988
|
+
size: "sm",
|
|
6989
|
+
className: "mt-2",
|
|
6990
|
+
icon: /* @__PURE__ */ jsx(Plus, {}),
|
|
6991
|
+
onClick: handleAdd,
|
|
6992
|
+
type: "button",
|
|
6993
|
+
children: addButtonLabel
|
|
6994
|
+
})]
|
|
6995
|
+
}) : /* @__PURE__ */ jsx(DndContext, {
|
|
6996
|
+
sensors,
|
|
6997
|
+
collisionDetection: closestCenter,
|
|
6998
|
+
onDragEnd: handleDragEnd,
|
|
6999
|
+
children: /* @__PURE__ */ jsx(SortableContext, {
|
|
7000
|
+
items: items.map((it) => it._key),
|
|
7001
|
+
strategy: verticalListSortingStrategy,
|
|
7002
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
7003
|
+
className: "space-y-2",
|
|
7004
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx(BlockKitRepeaterItem, {
|
|
7005
|
+
item,
|
|
7006
|
+
index,
|
|
7007
|
+
fields: field.fields,
|
|
7008
|
+
pluginId,
|
|
7009
|
+
isCollapsed: !expanded.has(item._key),
|
|
7010
|
+
onToggleCollapse: () => toggleExpanded(item._key),
|
|
7011
|
+
onRemove: canRemove ? () => handleRemove(item._key) : void 0,
|
|
7012
|
+
onChange: (subActionId, v) => handleItemChange(item._key, subActionId, v)
|
|
7013
|
+
}, item._key))
|
|
7014
|
+
})
|
|
7015
|
+
})
|
|
7016
|
+
})]
|
|
7017
|
+
});
|
|
7018
|
+
}
|
|
7019
|
+
function BlockKitRepeaterItem({ item, index, fields, pluginId, isCollapsed, onToggleCollapse, onRemove, onChange }) {
|
|
7020
|
+
const { _: _t3 } = useLingui();
|
|
7021
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: item._key });
|
|
7022
|
+
const style = {
|
|
7023
|
+
transform: CSS.Transform.toString(transform),
|
|
7024
|
+
transition
|
|
7025
|
+
};
|
|
7026
|
+
const summaryField = fields.find((f) => f.type === "text_input");
|
|
7027
|
+
const summaryLabel = (summaryField && typeof item[summaryField.action_id] === "string" ? item[summaryField.action_id] : "").trim() || _t3({
|
|
7028
|
+
id: "y/5CE8",
|
|
7029
|
+
message: "Item {0}",
|
|
7030
|
+
values: { 0: index + 1 }
|
|
7031
|
+
});
|
|
7032
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
7033
|
+
ref: setNodeRef,
|
|
7034
|
+
style,
|
|
7035
|
+
className: cn("border border-kumo-line rounded-lg bg-kumo-base", isDragging && "opacity-50 ring-2 ring-kumo-brand"),
|
|
7036
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
7037
|
+
className: "flex items-center gap-2 px-3 py-2 border-b border-kumo-line",
|
|
7038
|
+
children: [
|
|
7039
|
+
/* @__PURE__ */ jsx("span", {
|
|
7040
|
+
className: "inline-flex h-4 w-4 text-kumo-subtle cursor-grab shrink-0",
|
|
7041
|
+
"aria-label": _t3({
|
|
7042
|
+
id: "KGi3u9",
|
|
7043
|
+
message: "Drag to reorder"
|
|
7044
|
+
}),
|
|
7045
|
+
...attributes,
|
|
7046
|
+
...listeners,
|
|
7047
|
+
children: /* @__PURE__ */ jsx(DotsSixVertical, { className: "h-4 w-4" })
|
|
7048
|
+
}),
|
|
7049
|
+
/* @__PURE__ */ jsxs("button", {
|
|
7050
|
+
type: "button",
|
|
7051
|
+
className: "flex items-center gap-2 flex-1 min-w-0 text-left cursor-pointer",
|
|
7052
|
+
onClick: onToggleCollapse,
|
|
7053
|
+
"aria-expanded": !isCollapsed,
|
|
7054
|
+
children: [isCollapsed ? /* @__PURE__ */ jsx(CaretRight, { className: "h-4 w-4 text-kumo-subtle shrink-0" }) : /* @__PURE__ */ jsx(CaretDown, { className: "h-4 w-4 text-kumo-subtle shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
7055
|
+
className: "text-sm font-medium flex-1 truncate",
|
|
7056
|
+
children: summaryLabel
|
|
7057
|
+
})]
|
|
7058
|
+
}),
|
|
7059
|
+
onRemove && /* @__PURE__ */ jsx(Button, {
|
|
7060
|
+
variant: "ghost",
|
|
7061
|
+
shape: "square",
|
|
7062
|
+
type: "button",
|
|
7063
|
+
onClick: onRemove,
|
|
7064
|
+
"aria-label": _t3({
|
|
7065
|
+
id: "mU3j1b",
|
|
7066
|
+
message: "Remove item {0}",
|
|
7067
|
+
values: { 0: index + 1 }
|
|
7068
|
+
}),
|
|
7069
|
+
children: /* @__PURE__ */ jsx(Trash, { className: "h-3.5 w-3.5 text-kumo-danger" })
|
|
7070
|
+
})
|
|
7071
|
+
]
|
|
7072
|
+
}), !isCollapsed && /* @__PURE__ */ jsx("div", {
|
|
7073
|
+
className: "p-3 space-y-3",
|
|
7074
|
+
children: fields.map((sf) => /* @__PURE__ */ jsx(BlockKitField, {
|
|
7075
|
+
field: sf,
|
|
7076
|
+
pluginId,
|
|
7077
|
+
value: item[sf.action_id],
|
|
7078
|
+
onChange: (actionId, v) => onChange(actionId, v)
|
|
7079
|
+
}, sf.action_id))
|
|
7080
|
+
})]
|
|
7081
|
+
});
|
|
7082
|
+
}
|
|
6790
7083
|
/**
|
|
6791
7084
|
* Select field that supports loading options dynamically via `optionsRoute`.
|
|
6792
7085
|
* When `optionsRoute` is set, fetches `{ items: [{ id, name }] }` from the plugin route.
|
|
@@ -6885,7 +7178,7 @@ function EditorFooter({ editor }) {
|
|
|
6885
7178
|
* Portable Text Editor Component
|
|
6886
7179
|
*/
|
|
6887
7180
|
function PortableTextEditor({ value, onChange, placeholder = "Start writing...", className, editable = true, "aria-labelledby": ariaLabelledby, pluginBlocks = [], focusMode: controlledFocusMode, onFocusModeChange, onEditorReady, minimal = false, onBlockSidebarOpen, onBlockSidebarClose }) {
|
|
6888
|
-
const { _:
|
|
7181
|
+
const { _: _t4 } = useLingui();
|
|
6889
7182
|
const onChangeRef = React.useRef(onChange);
|
|
6890
7183
|
React.useEffect(() => {
|
|
6891
7184
|
onChangeRef.current = onChange;
|
|
@@ -6969,7 +7262,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
|
|
|
6969
7262
|
for (const block of pluginBlocks) cmds.push({
|
|
6970
7263
|
id: `plugin-${block.pluginId}-${block.type}`,
|
|
6971
7264
|
title: block.label,
|
|
6972
|
-
description: block.description ??
|
|
7265
|
+
description: block.description ?? _t4({
|
|
6973
7266
|
id: "g0zqOw",
|
|
6974
7267
|
message: "Embed a {0}",
|
|
6975
7268
|
values: { 0: block.label }
|
|
@@ -6986,7 +7279,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
|
|
|
6986
7279
|
}
|
|
6987
7280
|
});
|
|
6988
7281
|
return cmds;
|
|
6989
|
-
}, [pluginBlocks,
|
|
7282
|
+
}, [pluginBlocks, _t4]);
|
|
6990
7283
|
const filterCommandsRef = React.useRef((_q) => []);
|
|
6991
7284
|
filterCommandsRef.current = (query) => {
|
|
6992
7285
|
if (!query) return slashCommands;
|
|
@@ -6994,8 +7287,8 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
|
|
|
6994
7287
|
const titleMatches = [];
|
|
6995
7288
|
const otherMatches = [];
|
|
6996
7289
|
for (const item of slashCommands) {
|
|
6997
|
-
const titleStr = typeof item.title === "string" ? item.title :
|
|
6998
|
-
const descStr = typeof item.description === "string" ? item.description :
|
|
7290
|
+
const titleStr = typeof item.title === "string" ? item.title : _t4(item.title);
|
|
7291
|
+
const descStr = typeof item.description === "string" ? item.description : _t4(item.description);
|
|
6999
7292
|
if (titleStr.toLowerCase().includes(searchText)) titleMatches.push(item);
|
|
7000
7293
|
else if (descStr.toLowerCase().includes(searchText) || item.aliases?.some((alias) => alias.toLowerCase().includes(searchText))) otherMatches.push(item);
|
|
7001
7294
|
}
|
|
@@ -7047,7 +7340,10 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
|
|
|
7047
7340
|
content: initialContent,
|
|
7048
7341
|
editable,
|
|
7049
7342
|
immediatelyRender: true,
|
|
7050
|
-
editorProps: React.useMemo(() => ({ attributes: {
|
|
7343
|
+
editorProps: React.useMemo(() => ({ attributes: {
|
|
7344
|
+
class: "prose prose-sm sm:prose-base dark:prose-invert max-w-none focus:outline-none min-h-[200px] p-4",
|
|
7345
|
+
dir: "auto"
|
|
7346
|
+
} }), []),
|
|
7051
7347
|
onUpdate: ({ editor: updatedEditor }) => {
|
|
7052
7348
|
const cb = onChangeRef.current;
|
|
7053
7349
|
if (cb) cb(prosemirrorToPortableText(updatedEditor.getJSON()));
|
|
@@ -8398,7 +8694,8 @@ function SeoPanel({ contentKey, seo, onChange }) {
|
|
|
8398
8694
|
value: draft.title,
|
|
8399
8695
|
onChange: (e) => {
|
|
8400
8696
|
updateDraft({ title: e.target.value });
|
|
8401
|
-
}
|
|
8697
|
+
},
|
|
8698
|
+
dir: "auto"
|
|
8402
8699
|
}),
|
|
8403
8700
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(InputArea, {
|
|
8404
8701
|
label: _t({
|
|
@@ -8417,7 +8714,8 @@ function SeoPanel({ contentKey, seo, onChange }) {
|
|
|
8417
8714
|
onChange: (e) => {
|
|
8418
8715
|
updateDraft({ description: e.target.value });
|
|
8419
8716
|
},
|
|
8420
|
-
rows: 3
|
|
8717
|
+
rows: 3,
|
|
8718
|
+
dir: "auto"
|
|
8421
8719
|
}) }),
|
|
8422
8720
|
/* @__PURE__ */ jsx(Input, {
|
|
8423
8721
|
label: _t({
|
|
@@ -8455,6 +8753,65 @@ function SeoPanel({ contentKey, seo, onChange }) {
|
|
|
8455
8753
|
});
|
|
8456
8754
|
}
|
|
8457
8755
|
|
|
8756
|
+
//#endregion
|
|
8757
|
+
//#region src/lib/taxonomy-match.ts
|
|
8758
|
+
/**
|
|
8759
|
+
* Taxonomy term matching for the admin picker.
|
|
8760
|
+
*
|
|
8761
|
+
* The picker filters an editor's typed input against existing terms. A naive
|
|
8762
|
+
* `label.toLowerCase().includes(input.toLowerCase())` fails the accent case:
|
|
8763
|
+
* typing `"Mexico"` does not substring-match a term labeled `"México"`
|
|
8764
|
+
* because `"méxico".toLowerCase()` is still `"méxico"` and
|
|
8765
|
+
* `"méxico".includes("mexico")` is `false`. The editor then sees zero
|
|
8766
|
+
* suggestions and creates a duplicate `"Mexico"` term alongside the
|
|
8767
|
+
* canonical `"México"`, splitting the taxonomy.
|
|
8768
|
+
*
|
|
8769
|
+
* This module folds diacritics via NFD decomposition before substring
|
|
8770
|
+
* matching. No regexes are compiled from user input, so there is no ReDoS
|
|
8771
|
+
* surface.
|
|
8772
|
+
*/
|
|
8773
|
+
const DIACRITIC_RANGE = /[\u0300-\u036f]/g;
|
|
8774
|
+
/**
|
|
8775
|
+
* Case-fold + diacritic-fold normalization for substring matching.
|
|
8776
|
+
*
|
|
8777
|
+
* `"México"`, `"mexico"`, `"MÉXICO"` all collapse to `"mexico"`.
|
|
8778
|
+
*
|
|
8779
|
+
* NFD decomposes accented characters into a base + combining-diacritic
|
|
8780
|
+
* sequence; the regex drops the combiners. Greek tonos, Vietnamese
|
|
8781
|
+
* stacked diacritics, and other Latin-adjacent scripts are covered.
|
|
8782
|
+
* Combining marks used meaningfully in non-Latin scripts (Arabic harakat
|
|
8783
|
+
* U+064B–U+0652, Japanese dakuten U+3099) fall outside the U+0300–036F
|
|
8784
|
+
* block and are left untouched — stripping them would change meaning.
|
|
8785
|
+
*/
|
|
8786
|
+
function foldForMatch(value) {
|
|
8787
|
+
return value.normalize("NFD").replace(DIACRITIC_RANGE, "").toLowerCase();
|
|
8788
|
+
}
|
|
8789
|
+
/**
|
|
8790
|
+
* True if `input` is a substring of the term's label, ignoring case and
|
|
8791
|
+
* diacritics.
|
|
8792
|
+
*
|
|
8793
|
+
* Empty or whitespace-only input returns `false` — the caller decides
|
|
8794
|
+
* whether to show all terms or none in that state. The whitespace guard
|
|
8795
|
+
* matters: without it, a needle of `" "` would `.includes()`-match
|
|
8796
|
+
* every term whose label contains a space.
|
|
8797
|
+
*/
|
|
8798
|
+
function termMatches(term, input) {
|
|
8799
|
+
const needle = foldForMatch(input).trim();
|
|
8800
|
+
if (!needle) return false;
|
|
8801
|
+
return foldForMatch(term.label).includes(needle);
|
|
8802
|
+
}
|
|
8803
|
+
/**
|
|
8804
|
+
* True if `input` is an exact (fold-equal) match for the term's label.
|
|
8805
|
+
* Used to decide whether to show the "Create new term" button — if an
|
|
8806
|
+
* editor types `"Mexico"` and a term labeled `"México"` already exists,
|
|
8807
|
+
* Create must not appear or they'll produce a duplicate.
|
|
8808
|
+
*/
|
|
8809
|
+
function termExactMatches(term, input) {
|
|
8810
|
+
const needle = foldForMatch(input).trim();
|
|
8811
|
+
if (!needle) return false;
|
|
8812
|
+
return foldForMatch(term.label).trim() === needle;
|
|
8813
|
+
}
|
|
8814
|
+
|
|
8458
8815
|
//#endregion
|
|
8459
8816
|
//#region src/components/TaxonomySidebar.tsx
|
|
8460
8817
|
/**
|
|
@@ -8500,7 +8857,7 @@ function CategoryCheckboxTree({ term, level = 0, selectedIds, onToggle }) {
|
|
|
8500
8857
|
const isChecked = selectedIds.has(term.id);
|
|
8501
8858
|
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("label", {
|
|
8502
8859
|
className: "flex items-center py-1 cursor-pointer hover:bg-kumo-tint/50 rounded px-2",
|
|
8503
|
-
style: {
|
|
8860
|
+
style: { marginInlineStart: `${level}rem` },
|
|
8504
8861
|
children: [/* @__PURE__ */ jsx("input", {
|
|
8505
8862
|
type: "checkbox",
|
|
8506
8863
|
checked: isChecked,
|
|
@@ -8527,7 +8884,7 @@ function TagInput({ terms, selectedIds, onAdd, onRemove, onCreate, isCreating, l
|
|
|
8527
8884
|
const trimmedInput = input.trim();
|
|
8528
8885
|
const suggestions = React.useMemo(() => {
|
|
8529
8886
|
if (!trimmedInput) return [];
|
|
8530
|
-
return terms.filter((term) =>
|
|
8887
|
+
return terms.filter((term) => !selectedIds.has(term.id) && termMatches(term, trimmedInput)).slice(0, 5);
|
|
8531
8888
|
}, [
|
|
8532
8889
|
trimmedInput,
|
|
8533
8890
|
terms,
|
|
@@ -8535,7 +8892,7 @@ function TagInput({ terms, selectedIds, onAdd, onRemove, onCreate, isCreating, l
|
|
|
8535
8892
|
]);
|
|
8536
8893
|
const hasExactMatch = React.useMemo(() => {
|
|
8537
8894
|
if (!trimmedInput) return false;
|
|
8538
|
-
return terms.some((term) => term
|
|
8895
|
+
return terms.some((term) => termExactMatches(term, trimmedInput));
|
|
8539
8896
|
}, [trimmedInput, terms]);
|
|
8540
8897
|
const showCreateOption = trimmedInput.length > 0 && !hasExactMatch;
|
|
8541
8898
|
const handleSelect = (term) => {
|
|
@@ -8850,13 +9207,6 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
|
|
|
8850
9207
|
roleLabel: entry.roleLabel
|
|
8851
9208
|
})) ?? []);
|
|
8852
9209
|
const [portableTextEditor, setPortableTextEditor] = React.useState(null);
|
|
8853
|
-
const ptEditorClaimedRef = React.useRef(false);
|
|
8854
|
-
React.useCallback((editor) => {
|
|
8855
|
-
if (!ptEditorClaimedRef.current) {
|
|
8856
|
-
ptEditorClaimedRef.current = true;
|
|
8857
|
-
setPortableTextEditor(editor);
|
|
8858
|
-
}
|
|
8859
|
-
}, []);
|
|
8860
9210
|
const [blockSidebarPanel, setBlockSidebarPanel] = React.useState(null);
|
|
8861
9211
|
const handleBlockSidebarOpen = React.useCallback((panel) => {
|
|
8862
9212
|
setBlockSidebarPanel(panel);
|
|
@@ -9684,6 +10034,7 @@ function FieldRenderer({ name, field, value, onChange, onEditorReady, minimal, p
|
|
|
9684
10034
|
value: typeof value === "string" ? value : "",
|
|
9685
10035
|
onChange: (e) => handleChange(e.target.value),
|
|
9686
10036
|
required: field.required,
|
|
10037
|
+
dir: "auto",
|
|
9687
10038
|
className: minimal ? "border-0 bg-transparent px-0 text-lg font-medium focus-visible:ring-0 focus-visible:ring-offset-0" : void 0
|
|
9688
10039
|
});
|
|
9689
10040
|
case "number": return /* @__PURE__ */ jsx(Input, {
|
|
@@ -9734,6 +10085,7 @@ function FieldRenderer({ name, field, value, onChange, onEditorReady, minimal, p
|
|
|
9734
10085
|
value: typeof value === "string" ? value : "",
|
|
9735
10086
|
onChange: (e) => handleChange(e.target.value),
|
|
9736
10087
|
rows: 10,
|
|
10088
|
+
dir: "auto",
|
|
9737
10089
|
placeholder: _t2({
|
|
9738
10090
|
id: "dPFFy+",
|
|
9739
10091
|
message: "Enter markdown content..."
|
|
@@ -9830,7 +10182,8 @@ function FieldRenderer({ name, field, value, onChange, onEditorReady, minimal, p
|
|
|
9830
10182
|
id,
|
|
9831
10183
|
value: typeof value === "string" ? value : "",
|
|
9832
10184
|
onChange: (e) => handleChange(e.target.value),
|
|
9833
|
-
required: field.required
|
|
10185
|
+
required: field.required,
|
|
10186
|
+
dir: "auto"
|
|
9834
10187
|
});
|
|
9835
10188
|
}
|
|
9836
10189
|
}
|
|
@@ -10470,7 +10823,7 @@ function getItemTitle$1(item) {
|
|
|
10470
10823
|
/**
|
|
10471
10824
|
* Content list view with table display and trash tab
|
|
10472
10825
|
*/
|
|
10473
|
-
function ContentList({ collection, collectionLabel, items, trashedItems = [], isLoading, isTrashedLoading, onDelete, onDuplicate, onRestore, onPermanentDelete, onLoadMore, onLoadMoreTrashed, hasMore, hasMoreTrashed, trashedCount = 0, i18n: i18n$1, activeLocale, onLocaleChange, urlPattern }) {
|
|
10826
|
+
function ContentList({ collection, collectionLabel, items, trashedItems = [], isLoading, isTrashedLoading, onDelete, onDuplicate, onRestore, onPermanentDelete, onLoadMore, onLoadMoreTrashed, hasMore, hasMoreTrashed, trashedCount = 0, i18n: i18n$1, activeLocale, onLocaleChange, urlPattern, sort, onSortChange }) {
|
|
10474
10827
|
const { _: _t } = useLingui();
|
|
10475
10828
|
const [activeTab, setActiveTab] = React.useState("all");
|
|
10476
10829
|
const [searchQuery, setSearchQuery] = React.useState("");
|
|
@@ -10586,34 +10939,38 @@ function ContentList({ collection, collectionLabel, items, trashedItems = [], is
|
|
|
10586
10939
|
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
|
|
10587
10940
|
className: "border-b bg-kumo-tint/50",
|
|
10588
10941
|
children: [
|
|
10589
|
-
/* @__PURE__ */ jsx(
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10942
|
+
/* @__PURE__ */ jsx(SortableTh, {
|
|
10943
|
+
field: "title",
|
|
10944
|
+
sort,
|
|
10945
|
+
onSortChange,
|
|
10946
|
+
label: _t({
|
|
10593
10947
|
id: "MHrjPM",
|
|
10594
10948
|
message: "Title"
|
|
10595
10949
|
})
|
|
10596
10950
|
}),
|
|
10597
|
-
/* @__PURE__ */ jsx(
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10951
|
+
/* @__PURE__ */ jsx(SortableTh, {
|
|
10952
|
+
field: "status",
|
|
10953
|
+
sort,
|
|
10954
|
+
onSortChange,
|
|
10955
|
+
label: _t({
|
|
10601
10956
|
id: "uAQUqI",
|
|
10602
10957
|
message: "Status"
|
|
10603
10958
|
})
|
|
10604
10959
|
}),
|
|
10605
|
-
i18n$1 && /* @__PURE__ */ jsx(
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
|
|
10960
|
+
i18n$1 && /* @__PURE__ */ jsx(SortableTh, {
|
|
10961
|
+
field: "locale",
|
|
10962
|
+
sort,
|
|
10963
|
+
onSortChange,
|
|
10964
|
+
label: _t({
|
|
10609
10965
|
id: "8NbHF7",
|
|
10610
10966
|
message: "Locale"
|
|
10611
10967
|
})
|
|
10612
10968
|
}),
|
|
10613
|
-
/* @__PURE__ */ jsx(
|
|
10614
|
-
|
|
10615
|
-
|
|
10616
|
-
|
|
10969
|
+
/* @__PURE__ */ jsx(SortableTh, {
|
|
10970
|
+
field: "updatedAt",
|
|
10971
|
+
sort,
|
|
10972
|
+
onSortChange,
|
|
10973
|
+
label: _t({
|
|
10617
10974
|
id: "mYGY3B",
|
|
10618
10975
|
message: "Date"
|
|
10619
10976
|
})
|
|
@@ -10806,6 +11163,52 @@ function ContentList({ collection, collectionLabel, items, trashedItems = [], is
|
|
|
10806
11163
|
]
|
|
10807
11164
|
});
|
|
10808
11165
|
}
|
|
11166
|
+
/**
|
|
11167
|
+
* Table header that doubles as a sort control when the parent opted in by
|
|
11168
|
+
* passing `onSortChange`. When no callback is provided we fall back to a
|
|
11169
|
+
* plain `<th>` so legacy callers (and screen readers) see exactly the same
|
|
11170
|
+
* markup as before this change.
|
|
11171
|
+
*
|
|
11172
|
+
* The button's accessible name is just the column label — the sort state
|
|
11173
|
+
* is conveyed via `aria-sort` on the <th>, which screen readers announce
|
|
11174
|
+
* automatically. Adding a verbose aria-label would make each header re-read
|
|
11175
|
+
* the sort instruction on every focus, which is noisy.
|
|
11176
|
+
*/
|
|
11177
|
+
function SortableTh({ field, sort, onSortChange, label }) {
|
|
11178
|
+
const isActive = sort?.field === field;
|
|
11179
|
+
const direction = isActive ? sort?.direction : void 0;
|
|
11180
|
+
if (!onSortChange) return /* @__PURE__ */ jsx("th", {
|
|
11181
|
+
scope: "col",
|
|
11182
|
+
className: "px-4 py-3 text-start text-sm font-medium",
|
|
11183
|
+
children: label
|
|
11184
|
+
});
|
|
11185
|
+
const ariaSort = isActive ? direction === "asc" ? "ascending" : "descending" : "none";
|
|
11186
|
+
const handleClick = () => {
|
|
11187
|
+
if (isActive) onSortChange({
|
|
11188
|
+
field,
|
|
11189
|
+
direction: direction === "asc" ? "desc" : "asc"
|
|
11190
|
+
});
|
|
11191
|
+
else onSortChange({
|
|
11192
|
+
field,
|
|
11193
|
+
direction: "desc"
|
|
11194
|
+
});
|
|
11195
|
+
};
|
|
11196
|
+
const Icon = isActive ? direction === "asc" ? CaretUp : CaretDown : CaretUpDown;
|
|
11197
|
+
return /* @__PURE__ */ jsx("th", {
|
|
11198
|
+
scope: "col",
|
|
11199
|
+
"aria-sort": ariaSort,
|
|
11200
|
+
className: "px-4 py-3 text-start text-sm font-medium",
|
|
11201
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
11202
|
+
type: "button",
|
|
11203
|
+
onClick: handleClick,
|
|
11204
|
+
className: cn("inline-flex items-center gap-1 rounded hover:text-kumo-brand", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-kumo-brand", isActive ? "text-kumo-fg" : "text-kumo-subtle"),
|
|
11205
|
+
children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx(Icon, {
|
|
11206
|
+
className: "h-3 w-3",
|
|
11207
|
+
"aria-hidden": "true"
|
|
11208
|
+
})]
|
|
11209
|
+
})
|
|
11210
|
+
});
|
|
11211
|
+
}
|
|
10809
11212
|
function ContentListItem({ item, collection, onDelete, onDuplicate, showLocale, urlPattern }) {
|
|
10810
11213
|
const { _: _t2 } = useLingui();
|
|
10811
11214
|
const title = getItemTitle$1(item);
|
|
@@ -12027,7 +12430,7 @@ function ContentTypeEditor({ collection, isNew, isSaving, onSave, onAddField, on
|
|
|
12027
12430
|
const [labelSingular, setLabelSingular] = React.useState(collection?.labelSingular ?? "");
|
|
12028
12431
|
const [description, setDescription] = React.useState(collection?.description ?? "");
|
|
12029
12432
|
const [urlPattern, setUrlPattern] = React.useState(collection?.urlPattern ?? "");
|
|
12030
|
-
const [supports, setSupports] = React.useState((collection?.supports ?? ["drafts"]).filter((s) => s !== "seo"));
|
|
12433
|
+
const [supports, setSupports] = React.useState((collection?.supports ?? ["drafts", "revisions"]).filter((s) => s !== "seo"));
|
|
12031
12434
|
const [hasSeo, setHasSeo] = React.useState(collection?.hasSeo ?? false);
|
|
12032
12435
|
const [commentsEnabled, setCommentsEnabled] = React.useState(collection?.commentsEnabled ?? false);
|
|
12033
12436
|
const [commentsModeration, setCommentsModeration] = React.useState(collection?.commentsModeration ?? "first_time");
|
|
@@ -14119,6 +14522,24 @@ function LogoLockup({ className, ...props }) {
|
|
|
14119
14522
|
]
|
|
14120
14523
|
});
|
|
14121
14524
|
}
|
|
14525
|
+
function BrandLogo({ logoUrl, siteName, className }) {
|
|
14526
|
+
if (logoUrl) return /* @__PURE__ */ jsx("img", {
|
|
14527
|
+
src: logoUrl,
|
|
14528
|
+
alt: siteName || "",
|
|
14529
|
+
className,
|
|
14530
|
+
style: { objectFit: "contain" }
|
|
14531
|
+
});
|
|
14532
|
+
return /* @__PURE__ */ jsx(LogoLockup, { className });
|
|
14533
|
+
}
|
|
14534
|
+
function BrandIcon({ logoUrl, siteName, className }) {
|
|
14535
|
+
if (logoUrl) return /* @__PURE__ */ jsx("img", {
|
|
14536
|
+
src: logoUrl,
|
|
14537
|
+
alt: siteName || "",
|
|
14538
|
+
className,
|
|
14539
|
+
style: { objectFit: "contain" }
|
|
14540
|
+
});
|
|
14541
|
+
return /* @__PURE__ */ jsx(LogoIcon, { className });
|
|
14542
|
+
}
|
|
14122
14543
|
|
|
14123
14544
|
//#endregion
|
|
14124
14545
|
//#region src/components/InviteAcceptPage.tsx
|
|
@@ -14657,54 +15078,13 @@ function PasskeyLogin({ optionsEndpoint, verifyEndpoint, onSuccess, onError, sho
|
|
|
14657
15078
|
* It's a standalone page for authentication.
|
|
14658
15079
|
*
|
|
14659
15080
|
* Supports:
|
|
14660
|
-
* - Passkey authentication (
|
|
14661
|
-
* -
|
|
15081
|
+
* - Passkey authentication (always available)
|
|
15082
|
+
* - Pluggable auth providers (AT Protocol, GitHub, Google, etc.) when configured
|
|
14662
15083
|
* - Magic link (email) when configured
|
|
14663
15084
|
*
|
|
14664
15085
|
* When external auth (e.g., Cloudflare Access) is configured, this page
|
|
14665
15086
|
* redirects to the admin dashboard since authentication is handled externally.
|
|
14666
15087
|
*/
|
|
14667
|
-
function GitHubIcon({ className }) {
|
|
14668
|
-
return /* @__PURE__ */ jsx("svg", {
|
|
14669
|
-
className,
|
|
14670
|
-
viewBox: "0 0 24 24",
|
|
14671
|
-
fill: "currentColor",
|
|
14672
|
-
children: /* @__PURE__ */ jsx("path", { d: "M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" })
|
|
14673
|
-
});
|
|
14674
|
-
}
|
|
14675
|
-
function GoogleIcon({ className }) {
|
|
14676
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
14677
|
-
className,
|
|
14678
|
-
viewBox: "0 0 24 24",
|
|
14679
|
-
children: [
|
|
14680
|
-
/* @__PURE__ */ jsx("path", {
|
|
14681
|
-
fill: "#4285F4",
|
|
14682
|
-
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
14683
|
-
}),
|
|
14684
|
-
/* @__PURE__ */ jsx("path", {
|
|
14685
|
-
fill: "#34A853",
|
|
14686
|
-
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
14687
|
-
}),
|
|
14688
|
-
/* @__PURE__ */ jsx("path", {
|
|
14689
|
-
fill: "#FBBC05",
|
|
14690
|
-
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
14691
|
-
}),
|
|
14692
|
-
/* @__PURE__ */ jsx("path", {
|
|
14693
|
-
fill: "#EA4335",
|
|
14694
|
-
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
14695
|
-
})
|
|
14696
|
-
]
|
|
14697
|
-
});
|
|
14698
|
-
}
|
|
14699
|
-
const OAUTH_PROVIDERS = [{
|
|
14700
|
-
id: "github",
|
|
14701
|
-
name: "GitHub",
|
|
14702
|
-
icon: /* @__PURE__ */ jsx(GitHubIcon, { className: "h-5 w-5" })
|
|
14703
|
-
}, {
|
|
14704
|
-
id: "google",
|
|
14705
|
-
name: "Google",
|
|
14706
|
-
icon: /* @__PURE__ */ jsx(GoogleIcon, { className: "h-5 w-5" })
|
|
14707
|
-
}];
|
|
14708
15088
|
function MagicLinkForm({ onBack }) {
|
|
14709
15089
|
const { _: _t } = useLingui();
|
|
14710
15090
|
const [email, setEmail] = React.useState("");
|
|
@@ -14845,22 +15225,21 @@ function MagicLinkForm({ onBack }) {
|
|
|
14845
15225
|
]
|
|
14846
15226
|
});
|
|
14847
15227
|
}
|
|
14848
|
-
function handleOAuthClick(providerId) {
|
|
14849
|
-
window.location.href = `/_emdash/api/auth/oauth/${providerId}`;
|
|
14850
|
-
}
|
|
14851
15228
|
function LoginPage({ redirectUrl = "/_emdash/admin" }) {
|
|
14852
15229
|
const safeRedirectUrl = sanitizeRedirectUrl(redirectUrl);
|
|
14853
15230
|
const { _: _t2 } = useLingui();
|
|
14854
15231
|
const { locale, setLocale } = useLocale();
|
|
14855
15232
|
const [method, setMethod] = React.useState("passkey");
|
|
14856
15233
|
const [urlError, setUrlError] = React.useState(null);
|
|
14857
|
-
const
|
|
14858
|
-
|
|
14859
|
-
|
|
15234
|
+
const [activeProvider, setActiveProvider] = React.useState(null);
|
|
15235
|
+
const authProviderList = useAuthProviderList();
|
|
15236
|
+
const { data: authInfo, isLoading: authModeLoading } = useQuery({
|
|
15237
|
+
queryKey: ["authMode"],
|
|
15238
|
+
queryFn: fetchAuthMode
|
|
14860
15239
|
});
|
|
14861
15240
|
React.useEffect(() => {
|
|
14862
|
-
if (
|
|
14863
|
-
}, [
|
|
15241
|
+
if (authInfo?.authMode && authInfo.authMode !== "passkey") window.location.href = safeRedirectUrl;
|
|
15242
|
+
}, [authInfo, safeRedirectUrl]);
|
|
14864
15243
|
React.useEffect(() => {
|
|
14865
15244
|
const params = new URLSearchParams(window.location.search);
|
|
14866
15245
|
const error = params.get("error");
|
|
@@ -14877,11 +15256,12 @@ function LoginPage({ redirectUrl = "/_emdash/admin" }) {
|
|
|
14877
15256
|
const handleSuccess = () => {
|
|
14878
15257
|
window.location.href = safeRedirectUrl;
|
|
14879
15258
|
};
|
|
14880
|
-
|
|
15259
|
+
const buttonProviders = authProviderList.filter((p) => p.LoginButton);
|
|
15260
|
+
if (authModeLoading || authInfo?.authMode && authInfo.authMode !== "passkey") return /* @__PURE__ */ jsx("div", {
|
|
14881
15261
|
className: "min-h-screen flex items-center justify-center bg-kumo-base p-4",
|
|
14882
15262
|
children: /* @__PURE__ */ jsxs("div", {
|
|
14883
15263
|
className: "flex flex-col items-center",
|
|
14884
|
-
children: [/* @__PURE__ */ jsx(
|
|
15264
|
+
children: [/* @__PURE__ */ jsx(BrandLogo, { className: "h-10 mb-4" }), /* @__PURE__ */ jsx(Loader, {})]
|
|
14885
15265
|
})
|
|
14886
15266
|
});
|
|
14887
15267
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -14891,15 +15271,19 @@ function LoginPage({ redirectUrl = "/_emdash/admin" }) {
|
|
|
14891
15271
|
children: [
|
|
14892
15272
|
/* @__PURE__ */ jsxs("div", {
|
|
14893
15273
|
className: "text-center mb-8",
|
|
14894
|
-
children: [/* @__PURE__ */ jsx(
|
|
15274
|
+
children: [/* @__PURE__ */ jsx(BrandLogo, { className: "h-10 mx-auto mb-2" }), /* @__PURE__ */ jsx("h1", {
|
|
14895
15275
|
className: "text-2xl font-semibold text-kumo-default",
|
|
14896
|
-
children:
|
|
14897
|
-
id: "lE0wHD",
|
|
14898
|
-
message: "Sign in to your site"
|
|
14899
|
-
}), method === "magic-link" && _t2({
|
|
15276
|
+
children: method === "magic-link" ? _t2({
|
|
14900
15277
|
id: "me9L29",
|
|
14901
15278
|
message: "Sign in with email"
|
|
14902
|
-
})
|
|
15279
|
+
}) : activeProvider ? _t2({
|
|
15280
|
+
id: "e/ToF5",
|
|
15281
|
+
message: "Sign in with {0}",
|
|
15282
|
+
values: { 0: authProviderList.find((p) => p.id === activeProvider)?.label ?? activeProvider }
|
|
15283
|
+
}) : _t2({
|
|
15284
|
+
id: "lE0wHD",
|
|
15285
|
+
message: "Sign in to your site"
|
|
15286
|
+
})
|
|
14903
15287
|
})]
|
|
14904
15288
|
}),
|
|
14905
15289
|
urlError && /* @__PURE__ */ jsx("div", {
|
|
@@ -14908,71 +15292,94 @@ function LoginPage({ redirectUrl = "/_emdash/admin" }) {
|
|
|
14908
15292
|
}),
|
|
14909
15293
|
/* @__PURE__ */ jsxs("div", {
|
|
14910
15294
|
className: "bg-kumo-base border rounded-lg shadow-sm p-6",
|
|
14911
|
-
children: [
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
|
|
15295
|
+
children: [
|
|
15296
|
+
method === "passkey" && !activeProvider && /* @__PURE__ */ jsxs("div", {
|
|
15297
|
+
className: "space-y-6",
|
|
15298
|
+
children: [
|
|
15299
|
+
/* @__PURE__ */ jsx(PasskeyLogin, {
|
|
15300
|
+
optionsEndpoint: "/_emdash/api/auth/passkey/options",
|
|
15301
|
+
verifyEndpoint: "/_emdash/api/auth/passkey/verify",
|
|
15302
|
+
onSuccess: handleSuccess,
|
|
15303
|
+
buttonText: _t2({
|
|
15304
|
+
id: "QjsOMP",
|
|
15305
|
+
message: "Sign in with Passkey"
|
|
15306
|
+
})
|
|
15307
|
+
}),
|
|
15308
|
+
/* @__PURE__ */ jsxs("div", {
|
|
15309
|
+
className: "relative",
|
|
15310
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
15311
|
+
className: "absolute inset-0 flex items-center",
|
|
15312
|
+
children: /* @__PURE__ */ jsx("span", { className: "w-full border-t" })
|
|
15313
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
15314
|
+
className: "relative flex justify-center text-xs uppercase",
|
|
15315
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
15316
|
+
className: "bg-kumo-base px-2 text-kumo-subtle",
|
|
15317
|
+
children: _t2({
|
|
15318
|
+
id: "zW+FpA",
|
|
15319
|
+
message: "Or continue with"
|
|
15320
|
+
})
|
|
14935
15321
|
})
|
|
15322
|
+
})]
|
|
15323
|
+
}),
|
|
15324
|
+
buttonProviders.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
15325
|
+
className: `grid gap-3 ${buttonProviders.length === 1 ? "grid-cols-1" : "grid-cols-2"}`,
|
|
15326
|
+
children: buttonProviders.map((provider) => {
|
|
15327
|
+
const Btn = provider.LoginButton;
|
|
15328
|
+
const hasForm = !!provider.LoginForm;
|
|
15329
|
+
const selectProvider = () => setActiveProvider(provider.id);
|
|
15330
|
+
return /* @__PURE__ */ jsx("div", {
|
|
15331
|
+
onClick: hasForm ? selectProvider : void 0,
|
|
15332
|
+
children: /* @__PURE__ */ jsx(Btn, {})
|
|
15333
|
+
}, provider.id);
|
|
14936
15334
|
})
|
|
14937
|
-
})
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
className: "grid grid-cols-2 gap-3",
|
|
14941
|
-
children: OAUTH_PROVIDERS.map((provider) => /* @__PURE__ */ jsxs(Button, {
|
|
14942
|
-
variant: "outline",
|
|
14943
|
-
type: "button",
|
|
14944
|
-
onClick: () => handleOAuthClick(provider.id),
|
|
15335
|
+
}),
|
|
15336
|
+
/* @__PURE__ */ jsx(Button, {
|
|
15337
|
+
variant: "ghost",
|
|
14945
15338
|
className: "w-full justify-center",
|
|
14946
|
-
|
|
14947
|
-
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
14951
|
-
|
|
14952
|
-
/* @__PURE__ */ jsx(Button, {
|
|
14953
|
-
variant: "ghost",
|
|
14954
|
-
className: "w-full justify-center",
|
|
14955
|
-
type: "button",
|
|
14956
|
-
onClick: () => setMethod("magic-link"),
|
|
14957
|
-
children: _t2({
|
|
14958
|
-
id: "RxerEl",
|
|
14959
|
-
message: "Sign in with email link"
|
|
15339
|
+
type: "button",
|
|
15340
|
+
onClick: () => setMethod("magic-link"),
|
|
15341
|
+
children: _t2({
|
|
15342
|
+
id: "RxerEl",
|
|
15343
|
+
message: "Sign in with email link"
|
|
15344
|
+
})
|
|
14960
15345
|
})
|
|
14961
|
-
|
|
14962
|
-
|
|
14963
|
-
|
|
15346
|
+
]
|
|
15347
|
+
}),
|
|
15348
|
+
method === "passkey" && activeProvider && (() => {
|
|
15349
|
+
const provider = authProviderList.find((p) => p.id === activeProvider);
|
|
15350
|
+
if (!provider?.LoginForm) return null;
|
|
15351
|
+
const Form = provider.LoginForm;
|
|
15352
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
15353
|
+
className: "space-y-4",
|
|
15354
|
+
children: [/* @__PURE__ */ jsx(Form, {}), /* @__PURE__ */ jsx(Button, {
|
|
15355
|
+
type: "button",
|
|
15356
|
+
variant: "ghost",
|
|
15357
|
+
className: "w-full justify-center",
|
|
15358
|
+
onClick: () => setActiveProvider(null),
|
|
15359
|
+
children: _t2({
|
|
15360
|
+
id: "VCoEm+",
|
|
15361
|
+
message: "Back to login"
|
|
15362
|
+
})
|
|
15363
|
+
})]
|
|
15364
|
+
});
|
|
15365
|
+
})(),
|
|
15366
|
+
method === "magic-link" && /* @__PURE__ */ jsx(MagicLinkForm, { onBack: () => setMethod("passkey") })
|
|
15367
|
+
]
|
|
14964
15368
|
}),
|
|
14965
15369
|
/* @__PURE__ */ jsx("p", {
|
|
14966
15370
|
className: "text-center mt-6 text-sm text-kumo-subtle",
|
|
14967
|
-
children: method === "
|
|
14968
|
-
id: "+ET/av",
|
|
14969
|
-
message: "Use your registered passkey to sign in securely."
|
|
14970
|
-
}) : _t2({
|
|
15371
|
+
children: method === "magic-link" ? _t2({
|
|
14971
15372
|
id: "pv+wH3",
|
|
14972
15373
|
message: "We'll send you a link to sign in without a password."
|
|
15374
|
+
}) : activeProvider ? _t2({
|
|
15375
|
+
id: "TZGT4o",
|
|
15376
|
+
message: "Enter your handle to sign in."
|
|
15377
|
+
}) : _t2({
|
|
15378
|
+
id: "+ET/av",
|
|
15379
|
+
message: "Use your registered passkey to sign in securely."
|
|
14973
15380
|
})
|
|
14974
15381
|
}),
|
|
14975
|
-
|
|
15382
|
+
authInfo?.signupEnabled && /* @__PURE__ */ jsx("p", {
|
|
14976
15383
|
className: "text-center mt-4 text-sm text-kumo-subtle",
|
|
14977
15384
|
children: /* @__PURE__ */ jsx(Trans, {
|
|
14978
15385
|
id: "352VU2",
|
|
@@ -21366,6 +21773,50 @@ const API_TOKEN_SCOPE_VALUES = [
|
|
|
21366
21773
|
message: "Modify collection schemas"
|
|
21367
21774
|
}
|
|
21368
21775
|
},
|
|
21776
|
+
{
|
|
21777
|
+
scope: API_TOKEN_SCOPES.TaxonomiesManage,
|
|
21778
|
+
label: {
|
|
21779
|
+
id: "cfmuSI",
|
|
21780
|
+
message: "Taxonomies Manage"
|
|
21781
|
+
},
|
|
21782
|
+
description: {
|
|
21783
|
+
id: "mmBufY",
|
|
21784
|
+
message: "Create, update, and delete taxonomy terms"
|
|
21785
|
+
}
|
|
21786
|
+
},
|
|
21787
|
+
{
|
|
21788
|
+
scope: API_TOKEN_SCOPES.MenusManage,
|
|
21789
|
+
label: {
|
|
21790
|
+
id: "Wosws9",
|
|
21791
|
+
message: "Menus Manage"
|
|
21792
|
+
},
|
|
21793
|
+
description: {
|
|
21794
|
+
id: "DGhfmD",
|
|
21795
|
+
message: "Create, update, and delete navigation menus"
|
|
21796
|
+
}
|
|
21797
|
+
},
|
|
21798
|
+
{
|
|
21799
|
+
scope: API_TOKEN_SCOPES.SettingsRead,
|
|
21800
|
+
label: {
|
|
21801
|
+
id: "tYQSLw",
|
|
21802
|
+
message: "Settings Read"
|
|
21803
|
+
},
|
|
21804
|
+
description: {
|
|
21805
|
+
id: "fHKrk0",
|
|
21806
|
+
message: "Read site settings"
|
|
21807
|
+
}
|
|
21808
|
+
},
|
|
21809
|
+
{
|
|
21810
|
+
scope: API_TOKEN_SCOPES.SettingsManage,
|
|
21811
|
+
label: {
|
|
21812
|
+
id: "+K5A49",
|
|
21813
|
+
message: "Settings Manage"
|
|
21814
|
+
},
|
|
21815
|
+
description: {
|
|
21816
|
+
id: "RAXQwS",
|
|
21817
|
+
message: "Update site settings"
|
|
21818
|
+
}
|
|
21819
|
+
},
|
|
21369
21820
|
{
|
|
21370
21821
|
scope: API_TOKEN_SCOPES.Admin,
|
|
21371
21822
|
label: {
|
|
@@ -23423,8 +23874,9 @@ function SocialSettings() {
|
|
|
23423
23874
|
*
|
|
23424
23875
|
* Steps:
|
|
23425
23876
|
* 1. Site Configuration (title, tagline, sample content)
|
|
23426
|
-
* 2.
|
|
23427
|
-
*
|
|
23877
|
+
* 2. Create admin account — user picks any available auth method:
|
|
23878
|
+
* - Passkey (always available)
|
|
23879
|
+
* - Any configured auth provider (AT Protocol, GitHub, Google, etc.)
|
|
23428
23880
|
*/
|
|
23429
23881
|
async function fetchSetupStatus() {
|
|
23430
23882
|
return parseApiResponse(await apiFetch("/_emdash/api/setup/status"), "Failed to fetch setup status");
|
|
@@ -23445,8 +23897,8 @@ async function executeAdminSetup(data) {
|
|
|
23445
23897
|
}
|
|
23446
23898
|
function SiteStep({ seedInfo, onNext, isLoading, error }) {
|
|
23447
23899
|
const { _: _t } = useLingui();
|
|
23448
|
-
const [title, setTitle] = React.useState("");
|
|
23449
|
-
const [tagline, setTagline] = React.useState("");
|
|
23900
|
+
const [title, setTitle] = React.useState(seedInfo?.title ?? "");
|
|
23901
|
+
const [tagline, setTagline] = React.useState(seedInfo?.tagline ?? "");
|
|
23450
23902
|
const [includeContent, setIncludeContent] = React.useState(true);
|
|
23451
23903
|
const [errors, setErrors] = React.useState({});
|
|
23452
23904
|
const validate = () => {
|
|
@@ -23658,58 +24110,97 @@ function AdminStep({ onNext, onBack, isLoading, error }) {
|
|
|
23658
24110
|
]
|
|
23659
24111
|
});
|
|
23660
24112
|
}
|
|
23661
|
-
function
|
|
24113
|
+
function handleSetupSuccess() {
|
|
23662
24114
|
window.location.href = "/_emdash/admin";
|
|
23663
24115
|
}
|
|
23664
|
-
function
|
|
24116
|
+
function AuthMethodStep({ adminData, providers, onBack }) {
|
|
23665
24117
|
const { _: _t3 } = useLingui();
|
|
24118
|
+
const [activeProvider, setActiveProvider] = React.useState(null);
|
|
24119
|
+
const buttonProviders = providers.filter((p) => p.LoginButton);
|
|
24120
|
+
const hasProviders = buttonProviders.length > 0;
|
|
24121
|
+
if (activeProvider) {
|
|
24122
|
+
const provider = providers.find((p) => p.id === activeProvider);
|
|
24123
|
+
if (provider && (provider.SetupStep || provider.LoginForm)) return /* @__PURE__ */ jsxs("div", {
|
|
24124
|
+
className: "space-y-4",
|
|
24125
|
+
children: [
|
|
24126
|
+
/* @__PURE__ */ jsx("div", {
|
|
24127
|
+
className: "text-center mb-2",
|
|
24128
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
24129
|
+
className: "text-lg font-medium",
|
|
24130
|
+
children: _t3({
|
|
24131
|
+
id: "e/ToF5",
|
|
24132
|
+
message: "Sign in with {0}",
|
|
24133
|
+
values: { 0: provider.label }
|
|
24134
|
+
})
|
|
24135
|
+
})
|
|
24136
|
+
}),
|
|
24137
|
+
provider.SetupStep ? /* @__PURE__ */ jsx(provider.SetupStep, { onComplete: handleSetupSuccess }) : provider.LoginForm ? /* @__PURE__ */ jsx(provider.LoginForm, {}) : null,
|
|
24138
|
+
/* @__PURE__ */ jsx(Button, {
|
|
24139
|
+
type: "button",
|
|
24140
|
+
variant: "ghost",
|
|
24141
|
+
className: "w-full justify-center",
|
|
24142
|
+
onClick: () => setActiveProvider(null),
|
|
24143
|
+
children: _t3({
|
|
24144
|
+
id: "2ygf/L",
|
|
24145
|
+
message: "← Back"
|
|
24146
|
+
})
|
|
24147
|
+
})
|
|
24148
|
+
]
|
|
24149
|
+
});
|
|
24150
|
+
}
|
|
23666
24151
|
return /* @__PURE__ */ jsxs("div", {
|
|
23667
24152
|
className: "space-y-6",
|
|
23668
24153
|
children: [
|
|
23669
24154
|
/* @__PURE__ */ jsxs("div", {
|
|
23670
24155
|
className: "text-center",
|
|
23671
|
-
children: [
|
|
23672
|
-
|
|
23673
|
-
|
|
23674
|
-
|
|
23675
|
-
|
|
23676
|
-
fill: "none",
|
|
23677
|
-
stroke: "currentColor",
|
|
23678
|
-
viewBox: "0 0 24 24",
|
|
23679
|
-
children: /* @__PURE__ */ jsx("path", {
|
|
23680
|
-
strokeLinecap: "round",
|
|
23681
|
-
strokeLinejoin: "round",
|
|
23682
|
-
strokeWidth: 2,
|
|
23683
|
-
d: "M12 11c0 3.517-1.009 6.799-2.753 9.571m-3.44-2.04l.054-.09A13.916 13.916 0 008 11a4 4 0 118 0c0 1.017-.07 2.019-.203 3m-2.118 6.844A21.88 21.88 0 0015.171 17m3.839 1.132c.645-2.266.99-4.659.99-7.132A8 8 0 008 4.07M3 15.364c.64-1.319 1-2.8 1-4.364 0-1.457.39-2.823 1.07-4"
|
|
23684
|
-
})
|
|
23685
|
-
})
|
|
23686
|
-
}),
|
|
23687
|
-
/* @__PURE__ */ jsx("h3", {
|
|
23688
|
-
className: "text-lg font-medium",
|
|
23689
|
-
children: _t3({
|
|
23690
|
-
id: "fifQ8k",
|
|
23691
|
-
message: "Set up your passkey"
|
|
23692
|
-
})
|
|
23693
|
-
}),
|
|
23694
|
-
/* @__PURE__ */ jsx("p", {
|
|
23695
|
-
className: "text-sm text-kumo-subtle mt-1",
|
|
23696
|
-
children: _t3({
|
|
23697
|
-
id: "YdZ7D8",
|
|
23698
|
-
message: "Passkeys are more secure than passwords. You'll use your device's biometrics, PIN, or security key to sign in."
|
|
23699
|
-
})
|
|
24156
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
24157
|
+
className: "text-lg font-medium",
|
|
24158
|
+
children: _t3({
|
|
24159
|
+
id: "HAV363",
|
|
24160
|
+
message: "Choose how to sign in"
|
|
23700
24161
|
})
|
|
23701
|
-
|
|
24162
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
24163
|
+
className: "text-sm text-kumo-subtle mt-1",
|
|
24164
|
+
children: _t3({
|
|
24165
|
+
id: "NH33Oi",
|
|
24166
|
+
message: "Pick any method to create your admin account."
|
|
24167
|
+
})
|
|
24168
|
+
})]
|
|
23702
24169
|
}),
|
|
23703
24170
|
/* @__PURE__ */ jsx(PasskeyRegistration, {
|
|
23704
24171
|
optionsEndpoint: "/_emdash/api/setup/admin",
|
|
23705
24172
|
verifyEndpoint: "/_emdash/api/setup/admin/verify",
|
|
23706
|
-
onSuccess:
|
|
24173
|
+
onSuccess: handleSetupSuccess,
|
|
23707
24174
|
buttonText: _t3({
|
|
23708
24175
|
id: "AKqzR1",
|
|
23709
24176
|
message: "Create Passkey"
|
|
23710
24177
|
}),
|
|
23711
24178
|
additionalData: { ...adminData }
|
|
23712
24179
|
}),
|
|
24180
|
+
hasProviders && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
24181
|
+
className: "relative",
|
|
24182
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
24183
|
+
className: "absolute inset-0 flex items-center",
|
|
24184
|
+
children: /* @__PURE__ */ jsx("span", { className: "w-full border-t" })
|
|
24185
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
24186
|
+
className: "relative flex justify-center text-xs uppercase",
|
|
24187
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
24188
|
+
className: "bg-kumo-base px-2 text-kumo-subtle",
|
|
24189
|
+
children: "Or continue with"
|
|
24190
|
+
})
|
|
24191
|
+
})]
|
|
24192
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
24193
|
+
className: `grid gap-3 ${buttonProviders.length === 1 ? "grid-cols-1" : "grid-cols-2"}`,
|
|
24194
|
+
children: buttonProviders.map((provider) => {
|
|
24195
|
+
const Btn = provider.LoginButton;
|
|
24196
|
+
const hasForm = !!provider.LoginForm || !!provider.SetupStep;
|
|
24197
|
+
const selectProvider = () => setActiveProvider(provider.id);
|
|
24198
|
+
return /* @__PURE__ */ jsx("div", {
|
|
24199
|
+
onClick: hasForm ? selectProvider : void 0,
|
|
24200
|
+
children: /* @__PURE__ */ jsx(Btn, {})
|
|
24201
|
+
}, provider.id);
|
|
24202
|
+
})
|
|
24203
|
+
})] }),
|
|
23713
24204
|
/* @__PURE__ */ jsx(Button, {
|
|
23714
24205
|
type: "button",
|
|
23715
24206
|
variant: "ghost",
|
|
@@ -23749,8 +24240,8 @@ function StepIndicator$1({ currentStep, useAccessAuth }) {
|
|
|
23749
24240
|
{
|
|
23750
24241
|
key: "passkey",
|
|
23751
24242
|
label: _t4({
|
|
23752
|
-
id: "
|
|
23753
|
-
message: "
|
|
24243
|
+
id: "n1ekoW",
|
|
24244
|
+
message: "Sign In"
|
|
23754
24245
|
})
|
|
23755
24246
|
}
|
|
23756
24247
|
];
|
|
@@ -23788,11 +24279,26 @@ function SetupWizard() {
|
|
|
23788
24279
|
const [_siteData, setSiteData] = React.useState(null);
|
|
23789
24280
|
const [adminData, setAdminData] = React.useState(null);
|
|
23790
24281
|
const [error, setError] = React.useState();
|
|
24282
|
+
const [urlError, setUrlError] = React.useState(null);
|
|
24283
|
+
const authProviderList = useAuthProviderList();
|
|
24284
|
+
React.useEffect(() => {
|
|
24285
|
+
const params = new URLSearchParams(window.location.search);
|
|
24286
|
+
const errorParam = params.get("error");
|
|
24287
|
+
const message = params.get("message");
|
|
24288
|
+
if (errorParam) {
|
|
24289
|
+
setUrlError(message || `Authentication error: ${errorParam}`);
|
|
24290
|
+
window.history.replaceState({}, "", window.location.pathname);
|
|
24291
|
+
}
|
|
24292
|
+
}, []);
|
|
23791
24293
|
const { data: status, isLoading: statusLoading, error: statusError } = useQuery({
|
|
23792
24294
|
queryKey: ["setup", "status"],
|
|
23793
24295
|
queryFn: fetchSetupStatus,
|
|
23794
24296
|
retry: false
|
|
23795
24297
|
});
|
|
24298
|
+
const { data: manifest } = useQuery({
|
|
24299
|
+
queryKey: ["manifest"],
|
|
24300
|
+
queryFn: fetchManifest
|
|
24301
|
+
});
|
|
23796
24302
|
const useAccessAuth = status?.authMode === "cloudflare-access";
|
|
23797
24303
|
const siteMutation = useMutation({
|
|
23798
24304
|
mutationFn: executeSiteSetup,
|
|
@@ -23871,7 +24377,11 @@ function SetupWizard() {
|
|
|
23871
24377
|
/* @__PURE__ */ jsxs("div", {
|
|
23872
24378
|
className: "text-center mb-6",
|
|
23873
24379
|
children: [
|
|
23874
|
-
/* @__PURE__ */ jsx(
|
|
24380
|
+
/* @__PURE__ */ jsx(BrandLogo, {
|
|
24381
|
+
logoUrl: manifest?.admin?.logo,
|
|
24382
|
+
siteName: manifest?.admin?.siteName,
|
|
24383
|
+
className: "h-10 mx-auto mb-2"
|
|
24384
|
+
}),
|
|
23875
24385
|
/* @__PURE__ */ jsxs("h1", {
|
|
23876
24386
|
className: "text-2xl font-semibold text-kumo-default",
|
|
23877
24387
|
children: [
|
|
@@ -23898,6 +24408,10 @@ function SetupWizard() {
|
|
|
23898
24408
|
})
|
|
23899
24409
|
]
|
|
23900
24410
|
}),
|
|
24411
|
+
urlError && /* @__PURE__ */ jsx("div", {
|
|
24412
|
+
className: "mb-6 rounded-lg bg-kumo-danger/10 border border-kumo-danger/20 p-4 text-sm text-kumo-danger",
|
|
24413
|
+
children: urlError
|
|
24414
|
+
}),
|
|
23901
24415
|
/* @__PURE__ */ jsx(StepIndicator$1, {
|
|
23902
24416
|
currentStep,
|
|
23903
24417
|
useAccessAuth
|
|
@@ -23920,8 +24434,9 @@ function SetupWizard() {
|
|
|
23920
24434
|
isLoading: adminMutation.isPending,
|
|
23921
24435
|
error
|
|
23922
24436
|
}),
|
|
23923
|
-
currentStep === "passkey" && adminData && /* @__PURE__ */ jsx(
|
|
24437
|
+
currentStep === "passkey" && adminData && /* @__PURE__ */ jsx(AuthMethodStep, {
|
|
23924
24438
|
adminData,
|
|
24439
|
+
providers: authProviderList,
|
|
23925
24440
|
onBack: () => {
|
|
23926
24441
|
setError(void 0);
|
|
23927
24442
|
setCurrentStep("admin");
|
|
@@ -24655,12 +25170,14 @@ function SidebarNav({ manifest }) {
|
|
|
24655
25170
|
/* @__PURE__ */ jsx(KumoSidebar.Header, { children: /* @__PURE__ */ jsxs(Link$1, {
|
|
24656
25171
|
to: "/",
|
|
24657
25172
|
className: "emdash-brand-link flex w-full min-w-0 items-center gap-2 px-3 py-1",
|
|
24658
|
-
children: [/* @__PURE__ */ jsx(
|
|
25173
|
+
children: [/* @__PURE__ */ jsx(BrandIcon, {
|
|
25174
|
+
logoUrl: manifest.admin?.logo,
|
|
25175
|
+
siteName: manifest.admin?.siteName,
|
|
24659
25176
|
className: "size-5 shrink-0",
|
|
24660
25177
|
"aria-hidden": "true"
|
|
24661
25178
|
}), /* @__PURE__ */ jsx("span", {
|
|
24662
25179
|
className: "emdash-brand-text font-semibold truncate",
|
|
24663
|
-
children: "EmDash"
|
|
25180
|
+
children: manifest.admin?.siteName || "EmDash"
|
|
24664
25181
|
})]
|
|
24665
25182
|
}) }),
|
|
24666
25183
|
/* @__PURE__ */ jsxs(KumoSidebar.Content, { children: [
|
|
@@ -24726,7 +25243,8 @@ function SidebarNav({ manifest }) {
|
|
|
24726
25243
|
/* @__PURE__ */ jsx(KumoSidebar.Footer, { children: /* @__PURE__ */ jsxs("p", {
|
|
24727
25244
|
className: "emdash-nav-label px-3 py-2 text-[11px] text-white/30",
|
|
24728
25245
|
children: [
|
|
24729
|
-
"EmDash CMS
|
|
25246
|
+
manifest.admin?.siteName || "EmDash CMS",
|
|
25247
|
+
" v",
|
|
24730
25248
|
manifest.version || "0.0.0",
|
|
24731
25249
|
manifest.commit && ` (${manifest.commit})`
|
|
24732
25250
|
]
|
|
@@ -25684,7 +26202,7 @@ function TermRow({ term, level = 0, onEdit, onDelete }) {
|
|
|
25684
26202
|
className: "flex items-center gap-4 py-2 px-4 border-b hover:bg-kumo-tint/50",
|
|
25685
26203
|
children: [
|
|
25686
26204
|
/* @__PURE__ */ jsxs("div", {
|
|
25687
|
-
style: {
|
|
26205
|
+
style: { marginInlineStart: `${level * 1.5}rem` },
|
|
25688
26206
|
className: "flex-1",
|
|
25689
26207
|
children: [/* @__PURE__ */ jsx("span", {
|
|
25690
26208
|
className: "font-medium",
|
|
@@ -27028,7 +27546,20 @@ function Widgets() {
|
|
|
27028
27546
|
const [activeId, setActiveId] = React.useState(null);
|
|
27029
27547
|
const [activeDragData, setActiveDragData] = React.useState(null);
|
|
27030
27548
|
const [expandedWidgets, setExpandedWidgets] = React.useState(/* @__PURE__ */ new Set());
|
|
27549
|
+
const [blockSidebarPanel, setBlockSidebarPanel] = React.useState(null);
|
|
27031
27550
|
const draggingFromPaletteRef = React.useRef(false);
|
|
27551
|
+
const handleBlockSidebarOpen = React.useCallback((panel) => {
|
|
27552
|
+
setBlockSidebarPanel((prev) => {
|
|
27553
|
+
prev?.onClose();
|
|
27554
|
+
return panel;
|
|
27555
|
+
});
|
|
27556
|
+
}, []);
|
|
27557
|
+
const handleBlockSidebarClose = React.useCallback(() => {
|
|
27558
|
+
setBlockSidebarPanel((prev) => {
|
|
27559
|
+
prev?.onClose();
|
|
27560
|
+
return null;
|
|
27561
|
+
});
|
|
27562
|
+
}, []);
|
|
27032
27563
|
const { data: areas = [], isLoading } = useQuery({
|
|
27033
27564
|
queryKey: ["widget-areas"],
|
|
27034
27565
|
queryFn: fetchWidgetAreas
|
|
@@ -27166,174 +27697,189 @@ function Widgets() {
|
|
|
27166
27697
|
collisionDetection,
|
|
27167
27698
|
onDragStart: handleDragStart,
|
|
27168
27699
|
onDragEnd: handleDragEnd,
|
|
27169
|
-
children: [
|
|
27170
|
-
|
|
27171
|
-
|
|
27172
|
-
|
|
27173
|
-
|
|
27174
|
-
|
|
27175
|
-
|
|
27176
|
-
|
|
27177
|
-
|
|
27178
|
-
|
|
27179
|
-
|
|
27180
|
-
|
|
27181
|
-
|
|
27182
|
-
|
|
27183
|
-
|
|
27184
|
-
|
|
27185
|
-
|
|
27186
|
-
|
|
27187
|
-
|
|
27188
|
-
|
|
27189
|
-
|
|
27190
|
-
|
|
27191
|
-
|
|
27192
|
-
|
|
27193
|
-
|
|
27194
|
-
|
|
27195
|
-
|
|
27196
|
-
|
|
27197
|
-
|
|
27198
|
-
|
|
27199
|
-
render: (props) => /* @__PURE__ */ jsxs(Button, {
|
|
27200
|
-
...props,
|
|
27201
|
-
variant: "ghost",
|
|
27202
|
-
shape: "square",
|
|
27700
|
+
children: [
|
|
27701
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27702
|
+
className: "space-y-6",
|
|
27703
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
27704
|
+
className: "flex items-center justify-between",
|
|
27705
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", {
|
|
27706
|
+
className: "text-3xl font-bold",
|
|
27707
|
+
children: "Widgets"
|
|
27708
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
27709
|
+
className: "text-kumo-subtle",
|
|
27710
|
+
children: "Manage content widgets in your widget areas"
|
|
27711
|
+
})] }), /* @__PURE__ */ jsxs(Dialog.Root, {
|
|
27712
|
+
open: isCreateAreaOpen,
|
|
27713
|
+
onOpenChange: (open) => {
|
|
27714
|
+
setIsCreateAreaOpen(open);
|
|
27715
|
+
if (!open) setCreateAreaError(null);
|
|
27716
|
+
},
|
|
27717
|
+
children: [/* @__PURE__ */ jsx(Dialog.Trigger, { render: (props) => /* @__PURE__ */ jsx(Button, {
|
|
27718
|
+
...props,
|
|
27719
|
+
icon: /* @__PURE__ */ jsx(Plus, {}),
|
|
27720
|
+
children: "Add Widget Area"
|
|
27721
|
+
}) }), /* @__PURE__ */ jsxs(Dialog, {
|
|
27722
|
+
className: "p-6",
|
|
27723
|
+
size: "lg",
|
|
27724
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
27725
|
+
className: "flex items-start justify-between gap-4 mb-4",
|
|
27726
|
+
children: [/* @__PURE__ */ jsx(Dialog.Title, {
|
|
27727
|
+
className: "text-lg font-semibold leading-none tracking-tight",
|
|
27728
|
+
children: "Create Widget Area"
|
|
27729
|
+
}), /* @__PURE__ */ jsx(Dialog.Close, {
|
|
27203
27730
|
"aria-label": "Close",
|
|
27204
|
-
|
|
27205
|
-
|
|
27206
|
-
|
|
27207
|
-
|
|
27208
|
-
|
|
27209
|
-
|
|
27731
|
+
render: (props) => /* @__PURE__ */ jsxs(Button, {
|
|
27732
|
+
...props,
|
|
27733
|
+
variant: "ghost",
|
|
27734
|
+
shape: "square",
|
|
27735
|
+
"aria-label": "Close",
|
|
27736
|
+
className: "absolute end-4 top-4",
|
|
27737
|
+
children: [/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", {
|
|
27738
|
+
className: "sr-only",
|
|
27739
|
+
children: "Close"
|
|
27740
|
+
})]
|
|
27741
|
+
})
|
|
27742
|
+
})]
|
|
27743
|
+
}), /* @__PURE__ */ jsxs("form", {
|
|
27744
|
+
onSubmit: handleCreateArea,
|
|
27745
|
+
className: "space-y-4",
|
|
27746
|
+
children: [
|
|
27747
|
+
/* @__PURE__ */ jsx(Input, {
|
|
27748
|
+
label: "Name",
|
|
27749
|
+
name: "name",
|
|
27750
|
+
required: true,
|
|
27751
|
+
placeholder: "sidebar",
|
|
27752
|
+
pattern: "[a-z0-9-]+"
|
|
27753
|
+
}),
|
|
27754
|
+
/* @__PURE__ */ jsx(Input, {
|
|
27755
|
+
label: "Label",
|
|
27756
|
+
name: "label",
|
|
27757
|
+
required: true,
|
|
27758
|
+
placeholder: "Main Sidebar"
|
|
27759
|
+
}),
|
|
27760
|
+
/* @__PURE__ */ jsx(Input, {
|
|
27761
|
+
label: "Description",
|
|
27762
|
+
name: "description",
|
|
27763
|
+
placeholder: "Appears on posts and pages"
|
|
27764
|
+
}),
|
|
27765
|
+
/* @__PURE__ */ jsx(DialogError, { message: createAreaError || getMutationError(createAreaMutation.error) }),
|
|
27766
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27767
|
+
className: "flex justify-end gap-2",
|
|
27768
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
27769
|
+
type: "button",
|
|
27770
|
+
variant: "outline",
|
|
27771
|
+
onClick: () => setIsCreateAreaOpen(false),
|
|
27772
|
+
children: "Cancel"
|
|
27773
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
27774
|
+
type: "submit",
|
|
27775
|
+
disabled: createAreaMutation.isPending,
|
|
27776
|
+
children: "Create"
|
|
27777
|
+
})]
|
|
27778
|
+
})
|
|
27779
|
+
]
|
|
27210
27780
|
})]
|
|
27211
|
-
})
|
|
27212
|
-
|
|
27213
|
-
|
|
27781
|
+
})]
|
|
27782
|
+
})]
|
|
27783
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
27784
|
+
className: "grid grid-cols-12 gap-6",
|
|
27785
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
27786
|
+
className: "col-span-4",
|
|
27787
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
27788
|
+
className: "rounded-lg border bg-kumo-base p-6 space-y-4",
|
|
27214
27789
|
children: [
|
|
27215
|
-
/* @__PURE__ */ jsx(
|
|
27216
|
-
|
|
27217
|
-
|
|
27218
|
-
required: true,
|
|
27219
|
-
placeholder: "sidebar",
|
|
27220
|
-
pattern: "[a-z0-9-]+"
|
|
27221
|
-
}),
|
|
27222
|
-
/* @__PURE__ */ jsx(Input, {
|
|
27223
|
-
label: "Label",
|
|
27224
|
-
name: "label",
|
|
27225
|
-
required: true,
|
|
27226
|
-
placeholder: "Main Sidebar"
|
|
27790
|
+
/* @__PURE__ */ jsx("h2", {
|
|
27791
|
+
className: "text-xl font-semibold",
|
|
27792
|
+
children: "Available Widgets"
|
|
27227
27793
|
}),
|
|
27228
|
-
/* @__PURE__ */ jsx(
|
|
27229
|
-
|
|
27230
|
-
|
|
27231
|
-
placeholder: "Appears on posts and pages"
|
|
27794
|
+
/* @__PURE__ */ jsx("p", {
|
|
27795
|
+
className: "text-sm text-kumo-subtle",
|
|
27796
|
+
children: "Drag widgets into an area to add them"
|
|
27232
27797
|
}),
|
|
27233
|
-
/* @__PURE__ */ jsx(DialogError, { message: createAreaError || getMutationError(createAreaMutation.error) }),
|
|
27234
27798
|
/* @__PURE__ */ jsxs("div", {
|
|
27235
|
-
className: "
|
|
27236
|
-
children: [/* @__PURE__ */ jsx(
|
|
27237
|
-
|
|
27238
|
-
|
|
27239
|
-
|
|
27240
|
-
|
|
27241
|
-
|
|
27242
|
-
|
|
27243
|
-
|
|
27244
|
-
|
|
27245
|
-
|
|
27799
|
+
className: "space-y-2",
|
|
27800
|
+
children: [BUILTIN_WIDGETS.map((item) => /* @__PURE__ */ jsx(DraggablePaletteItem, {
|
|
27801
|
+
id: item.id,
|
|
27802
|
+
label: _t(item.label),
|
|
27803
|
+
description: _t(item.description),
|
|
27804
|
+
widgetInput: {
|
|
27805
|
+
...item.input,
|
|
27806
|
+
title: _t(item.label)
|
|
27807
|
+
}
|
|
27808
|
+
}, item.id)), components.map((comp) => /* @__PURE__ */ jsx(DraggablePaletteItem, {
|
|
27809
|
+
id: `palette-comp-${comp.id}`,
|
|
27810
|
+
label: comp.label,
|
|
27811
|
+
description: comp.description,
|
|
27812
|
+
widgetInput: {
|
|
27813
|
+
type: "component",
|
|
27814
|
+
title: comp.label,
|
|
27815
|
+
componentId: comp.id
|
|
27816
|
+
}
|
|
27817
|
+
}, `palette-comp-${comp.id}`))]
|
|
27246
27818
|
})
|
|
27247
27819
|
]
|
|
27248
|
-
})
|
|
27820
|
+
})
|
|
27821
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
27822
|
+
className: "col-span-8 space-y-4",
|
|
27823
|
+
children: areas.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
27824
|
+
className: "rounded-lg border bg-kumo-base p-12 text-center",
|
|
27825
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
27826
|
+
className: "text-kumo-subtle",
|
|
27827
|
+
children: "No widget areas yet. Create one to get started."
|
|
27828
|
+
})
|
|
27829
|
+
}) : areas.map((area) => /* @__PURE__ */ jsx(WidgetAreaPanel, {
|
|
27830
|
+
area,
|
|
27831
|
+
expandedWidgets,
|
|
27832
|
+
onToggleWidget: toggleWidget,
|
|
27833
|
+
isDraggingPalette: activeDragData !== null && isPaletteItem(activeDragData),
|
|
27834
|
+
components,
|
|
27835
|
+
pluginBlocks,
|
|
27836
|
+
onBlockSidebarOpen: handleBlockSidebarOpen,
|
|
27837
|
+
onBlockSidebarClose: handleBlockSidebarClose
|
|
27838
|
+
}, area.id))
|
|
27249
27839
|
})]
|
|
27250
27840
|
})]
|
|
27251
|
-
}),
|
|
27252
|
-
|
|
27253
|
-
|
|
27254
|
-
|
|
27841
|
+
}),
|
|
27842
|
+
/* @__PURE__ */ jsx(DragOverlay, {
|
|
27843
|
+
dropAnimation: draggingFromPaletteRef.current ? null : void 0,
|
|
27844
|
+
children: activePaletteLabel ? /* @__PURE__ */ jsx("div", {
|
|
27845
|
+
className: "rounded border bg-kumo-base p-3 shadow-lg opacity-90",
|
|
27846
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
27847
|
+
className: "font-medium",
|
|
27848
|
+
children: activePaletteLabel
|
|
27849
|
+
})
|
|
27850
|
+
}) : activeWidget ? /* @__PURE__ */ jsx("div", {
|
|
27851
|
+
className: "rounded border bg-kumo-base p-3 shadow-lg opacity-90",
|
|
27255
27852
|
children: /* @__PURE__ */ jsxs("div", {
|
|
27256
|
-
className: "
|
|
27853
|
+
className: "flex items-center gap-2",
|
|
27257
27854
|
children: [
|
|
27258
|
-
/* @__PURE__ */ jsx("
|
|
27259
|
-
|
|
27260
|
-
|
|
27261
|
-
|
|
27262
|
-
/* @__PURE__ */ jsx("p", {
|
|
27263
|
-
className: "text-sm text-kumo-subtle",
|
|
27264
|
-
children: "Drag widgets into an area to add them"
|
|
27855
|
+
/* @__PURE__ */ jsx(DotsSixVertical, { className: "h-4 w-4 text-kumo-subtle" }),
|
|
27856
|
+
/* @__PURE__ */ jsx("span", {
|
|
27857
|
+
className: "font-medium",
|
|
27858
|
+
children: activeWidget.title || "Untitled Widget"
|
|
27265
27859
|
}),
|
|
27266
|
-
/* @__PURE__ */ jsxs("
|
|
27267
|
-
className: "
|
|
27268
|
-
children: [
|
|
27269
|
-
|
|
27270
|
-
|
|
27271
|
-
|
|
27272
|
-
|
|
27273
|
-
...item.input,
|
|
27274
|
-
title: _t(item.label)
|
|
27275
|
-
}
|
|
27276
|
-
}, item.id)), components.map((comp) => /* @__PURE__ */ jsx(DraggablePaletteItem, {
|
|
27277
|
-
id: `palette-comp-${comp.id}`,
|
|
27278
|
-
label: comp.label,
|
|
27279
|
-
description: comp.description,
|
|
27280
|
-
widgetInput: {
|
|
27281
|
-
type: "component",
|
|
27282
|
-
title: comp.label,
|
|
27283
|
-
componentId: comp.id
|
|
27284
|
-
}
|
|
27285
|
-
}, `palette-comp-${comp.id}`))]
|
|
27860
|
+
/* @__PURE__ */ jsxs("span", {
|
|
27861
|
+
className: "text-xs text-kumo-subtle",
|
|
27862
|
+
children: [
|
|
27863
|
+
"(",
|
|
27864
|
+
activeWidget.type,
|
|
27865
|
+
")"
|
|
27866
|
+
]
|
|
27286
27867
|
})
|
|
27287
27868
|
]
|
|
27288
27869
|
})
|
|
27289
|
-
})
|
|
27290
|
-
|
|
27291
|
-
|
|
27292
|
-
|
|
27293
|
-
|
|
27294
|
-
|
|
27295
|
-
|
|
27296
|
-
|
|
27297
|
-
|
|
27298
|
-
|
|
27299
|
-
|
|
27300
|
-
|
|
27301
|
-
|
|
27302
|
-
components,
|
|
27303
|
-
pluginBlocks
|
|
27304
|
-
}, area.id))
|
|
27305
|
-
})]
|
|
27306
|
-
})]
|
|
27307
|
-
}), /* @__PURE__ */ jsx(DragOverlay, {
|
|
27308
|
-
dropAnimation: draggingFromPaletteRef.current ? null : void 0,
|
|
27309
|
-
children: activePaletteLabel ? /* @__PURE__ */ jsx("div", {
|
|
27310
|
-
className: "rounded border bg-kumo-base p-3 shadow-lg opacity-90",
|
|
27311
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
27312
|
-
className: "font-medium",
|
|
27313
|
-
children: activePaletteLabel
|
|
27314
|
-
})
|
|
27315
|
-
}) : activeWidget ? /* @__PURE__ */ jsx("div", {
|
|
27316
|
-
className: "rounded border bg-kumo-base p-3 shadow-lg opacity-90",
|
|
27317
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
27318
|
-
className: "flex items-center gap-2",
|
|
27319
|
-
children: [
|
|
27320
|
-
/* @__PURE__ */ jsx(DotsSixVertical, { className: "h-4 w-4 text-kumo-subtle" }),
|
|
27321
|
-
/* @__PURE__ */ jsx("span", {
|
|
27322
|
-
className: "font-medium",
|
|
27323
|
-
children: activeWidget.title || "Untitled Widget"
|
|
27324
|
-
}),
|
|
27325
|
-
/* @__PURE__ */ jsxs("span", {
|
|
27326
|
-
className: "text-xs text-kumo-subtle",
|
|
27327
|
-
children: [
|
|
27328
|
-
"(",
|
|
27329
|
-
activeWidget.type,
|
|
27330
|
-
")"
|
|
27331
|
-
]
|
|
27332
|
-
})
|
|
27333
|
-
]
|
|
27334
|
-
})
|
|
27335
|
-
}) : null
|
|
27336
|
-
})]
|
|
27870
|
+
}) : null
|
|
27871
|
+
}),
|
|
27872
|
+
blockSidebarPanel?.type === "image" && /* @__PURE__ */ jsx(ImageDetailPanel, {
|
|
27873
|
+
attributes: blockSidebarPanel.attrs,
|
|
27874
|
+
onUpdate: (attrs) => blockSidebarPanel.onUpdate(attrs),
|
|
27875
|
+
onReplace: (attrs) => blockSidebarPanel.onReplace(attrs),
|
|
27876
|
+
onDelete: () => {
|
|
27877
|
+
blockSidebarPanel.onDelete();
|
|
27878
|
+
setBlockSidebarPanel(null);
|
|
27879
|
+
},
|
|
27880
|
+
onClose: handleBlockSidebarClose
|
|
27881
|
+
})
|
|
27882
|
+
]
|
|
27337
27883
|
});
|
|
27338
27884
|
}
|
|
27339
27885
|
/** A draggable item in the available widgets palette */
|
|
@@ -27360,7 +27906,7 @@ function DraggablePaletteItem({ id, label, description, widgetInput }) {
|
|
|
27360
27906
|
})]
|
|
27361
27907
|
});
|
|
27362
27908
|
}
|
|
27363
|
-
function WidgetAreaPanel({ area, expandedWidgets, onToggleWidget, isDraggingPalette, components, pluginBlocks }) {
|
|
27909
|
+
function WidgetAreaPanel({ area, expandedWidgets, onToggleWidget, isDraggingPalette, components, pluginBlocks, onBlockSidebarOpen, onBlockSidebarClose }) {
|
|
27364
27910
|
const queryClient = useQueryClient();
|
|
27365
27911
|
const toastManager = Toast.useToastManager();
|
|
27366
27912
|
const [deleteAreaName, setDeleteAreaName] = React.useState(null);
|
|
@@ -27406,7 +27952,9 @@ function WidgetAreaPanel({ area, expandedWidgets, onToggleWidget, isDraggingPale
|
|
|
27406
27952
|
isExpanded: expandedWidgets.has(widget.id),
|
|
27407
27953
|
onToggle: () => onToggleWidget(widget.id),
|
|
27408
27954
|
components,
|
|
27409
|
-
pluginBlocks
|
|
27955
|
+
pluginBlocks,
|
|
27956
|
+
onBlockSidebarOpen,
|
|
27957
|
+
onBlockSidebarClose
|
|
27410
27958
|
}, widget.id))
|
|
27411
27959
|
}) : null,
|
|
27412
27960
|
isDraggingPalette && /* @__PURE__ */ jsx("div", {
|
|
@@ -27436,7 +27984,7 @@ function WidgetAreaPanel({ area, expandedWidgets, onToggleWidget, isDraggingPale
|
|
|
27436
27984
|
]
|
|
27437
27985
|
});
|
|
27438
27986
|
}
|
|
27439
|
-
function WidgetItem({ widget, areaName, isExpanded, onToggle, components, pluginBlocks }) {
|
|
27987
|
+
function WidgetItem({ widget, areaName, isExpanded, onToggle, components, pluginBlocks, onBlockSidebarOpen, onBlockSidebarClose }) {
|
|
27440
27988
|
const queryClient = useQueryClient();
|
|
27441
27989
|
const toastManager = Toast.useToastManager();
|
|
27442
27990
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
|
@@ -27528,12 +28076,14 @@ function WidgetItem({ widget, areaName, isExpanded, onToggle, components, plugin
|
|
|
27528
28076
|
components,
|
|
27529
28077
|
pluginBlocks,
|
|
27530
28078
|
onSave: (input) => updateMutation.mutate(input),
|
|
27531
|
-
isSaving: updateMutation.isPending
|
|
28079
|
+
isSaving: updateMutation.isPending,
|
|
28080
|
+
onBlockSidebarOpen,
|
|
28081
|
+
onBlockSidebarClose
|
|
27532
28082
|
})]
|
|
27533
28083
|
});
|
|
27534
28084
|
}
|
|
27535
28085
|
/** Inline editor form for a widget, rendered when the widget is expanded */
|
|
27536
|
-
function WidgetEditor({ widget, components, pluginBlocks, onSave, isSaving }) {
|
|
28086
|
+
function WidgetEditor({ widget, components, pluginBlocks, onSave, isSaving, onBlockSidebarOpen, onBlockSidebarClose }) {
|
|
27537
28087
|
const [title, setTitle] = React.useState(widget.title ?? "");
|
|
27538
28088
|
const [content, setContent] = React.useState(Array.isArray(widget.content) ? widget.content : []);
|
|
27539
28089
|
const [menuName, setMenuName] = React.useState(widget.menuName ?? "");
|
|
@@ -27572,7 +28122,9 @@ function WidgetEditor({ widget, components, pluginBlocks, onSave, isSaving }) {
|
|
|
27572
28122
|
onChange: (value) => setContent(value),
|
|
27573
28123
|
minimal: true,
|
|
27574
28124
|
placeholder: "Write widget content...",
|
|
27575
|
-
pluginBlocks
|
|
28125
|
+
pluginBlocks,
|
|
28126
|
+
onBlockSidebarOpen,
|
|
28127
|
+
onBlockSidebarClose
|
|
27576
28128
|
})] }),
|
|
27577
28129
|
widget.type === "menu" && /* @__PURE__ */ jsxs(Select, {
|
|
27578
28130
|
label: "Menu",
|
|
@@ -30825,8 +31377,8 @@ function UserDetail({ user, isLoading, isOpen, isSaving, isSendingRecovery, reco
|
|
|
30825
31377
|
return /* @__PURE__ */ jsx(Dialog$1.Root, {
|
|
30826
31378
|
open: isOpen,
|
|
30827
31379
|
onOpenChange: (open) => !open && stableOnClose(),
|
|
30828
|
-
children: /* @__PURE__ */ jsxs(Dialog$1.Portal, { children: [/* @__PURE__ */ jsx(Dialog$1.Backdrop, { className: cn("fixed inset-0
|
|
30829
|
-
className: cn("fixed top-0 end-0
|
|
31380
|
+
children: /* @__PURE__ */ jsxs(Dialog$1.Portal, { children: [/* @__PURE__ */ jsx(Dialog$1.Backdrop, { className: cn("fixed inset-0 bg-black/50 transition-opacity duration-200", "data-starting-style:opacity-0 data-ending-style:opacity-0") }), /* @__PURE__ */ jsxs(Dialog$1.Popup, {
|
|
31381
|
+
className: cn("fixed top-0 end-0 flex h-full w-full max-w-md flex-col bg-kumo-base shadow-xl outline-none", "transform transition-transform duration-200 ease-out", "data-starting-style:ltr:translate-x-full data-starting-style:rtl:-translate-x-full", "data-ending-style:ltr:translate-x-full data-ending-style:rtl:-translate-x-full"),
|
|
30830
31382
|
children: [
|
|
30831
31383
|
/* @__PURE__ */ jsxs("div", {
|
|
30832
31384
|
className: "flex items-center justify-between border-b px-6 py-4",
|
|
@@ -31790,16 +32342,25 @@ function ContentListPage() {
|
|
|
31790
32342
|
});
|
|
31791
32343
|
const i18n = manifest?.i18n;
|
|
31792
32344
|
const activeLocale = i18n ? localeParam ?? i18n.defaultLocale : void 0;
|
|
32345
|
+
const [sort, setSort] = React.useState({
|
|
32346
|
+
field: "updatedAt",
|
|
32347
|
+
direction: "desc"
|
|
32348
|
+
});
|
|
31793
32349
|
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, error } = useInfiniteQuery({
|
|
31794
32350
|
queryKey: [
|
|
31795
32351
|
"content",
|
|
31796
32352
|
collection,
|
|
31797
|
-
{
|
|
32353
|
+
{
|
|
32354
|
+
locale: activeLocale,
|
|
32355
|
+
sort
|
|
32356
|
+
}
|
|
31798
32357
|
],
|
|
31799
32358
|
queryFn: ({ pageParam }) => fetchContentList(collection, {
|
|
31800
32359
|
locale: activeLocale,
|
|
31801
32360
|
cursor: pageParam,
|
|
31802
|
-
limit: 100
|
|
32361
|
+
limit: 100,
|
|
32362
|
+
orderBy: sort.field,
|
|
32363
|
+
order: sort.direction
|
|
31803
32364
|
}),
|
|
31804
32365
|
initialPageParam: void 0,
|
|
31805
32366
|
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
@@ -31910,7 +32471,9 @@ function ContentListPage() {
|
|
|
31910
32471
|
i18n,
|
|
31911
32472
|
activeLocale,
|
|
31912
32473
|
onLocaleChange: handleLocaleChange,
|
|
31913
|
-
urlPattern: collectionConfig.urlPattern
|
|
32474
|
+
urlPattern: collectionConfig.urlPattern,
|
|
32475
|
+
sort,
|
|
32476
|
+
onSortChange: setSort
|
|
31914
32477
|
});
|
|
31915
32478
|
}
|
|
31916
32479
|
const contentNewRoute = createRoute({
|
|
@@ -32630,7 +33193,7 @@ function MarketplaceBrowsePage() {
|
|
|
32630
33193
|
const { data: plugins } = useQuery({
|
|
32631
33194
|
queryKey: ["plugins"],
|
|
32632
33195
|
queryFn: async () => {
|
|
32633
|
-
const { fetchPlugins } = await import("./plugins-
|
|
33196
|
+
const { fetchPlugins } = await import("./plugins-DUJY_8HN.js").then((n) => n.a);
|
|
32634
33197
|
return fetchPlugins();
|
|
32635
33198
|
}
|
|
32636
33199
|
});
|
|
@@ -32649,7 +33212,7 @@ function MarketplaceDetailPage() {
|
|
|
32649
33212
|
const { data: plugins } = useQuery({
|
|
32650
33213
|
queryKey: ["plugins"],
|
|
32651
33214
|
queryFn: async () => {
|
|
32652
|
-
const { fetchPlugins } = await import("./plugins-
|
|
33215
|
+
const { fetchPlugins } = await import("./plugins-DUJY_8HN.js").then((n) => n.a);
|
|
32653
33216
|
return fetchPlugins();
|
|
32654
33217
|
}
|
|
32655
33218
|
});
|
|
@@ -33062,7 +33625,8 @@ const router = createAdminRouter(queryClient);
|
|
|
33062
33625
|
* Main Admin Application
|
|
33063
33626
|
*/
|
|
33064
33627
|
const EMPTY_PLUGINS = {};
|
|
33065
|
-
|
|
33628
|
+
const EMPTY_AUTH_PROVIDERS = {};
|
|
33629
|
+
function AdminApp({ pluginAdmins = EMPTY_PLUGINS, authProviders = EMPTY_AUTH_PROVIDERS, locale = "en", messages = {} }) {
|
|
33066
33630
|
React.useEffect(() => {
|
|
33067
33631
|
document.getElementById("emdash-boot-loader")?.remove();
|
|
33068
33632
|
}, []);
|
|
@@ -33076,16 +33640,19 @@ function AdminApp({ pluginAdmins = EMPTY_PLUGINS, locale = "en", messages = {} }
|
|
|
33076
33640
|
}
|
|
33077
33641
|
return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(I18nProvider, {
|
|
33078
33642
|
i18n,
|
|
33079
|
-
children: /* @__PURE__ */ jsx(LocaleDirectionProvider, { children: /* @__PURE__ */ jsx(Toasty, { children: /* @__PURE__ */ jsx(
|
|
33080
|
-
|
|
33081
|
-
children: /* @__PURE__ */ jsx(
|
|
33082
|
-
|
|
33083
|
-
children: /* @__PURE__ */ jsx(
|
|
33643
|
+
children: /* @__PURE__ */ jsx(LocaleDirectionProvider, { children: /* @__PURE__ */ jsx(Toasty, { children: /* @__PURE__ */ jsx(AuthProviderProvider, {
|
|
33644
|
+
authProviders,
|
|
33645
|
+
children: /* @__PURE__ */ jsx(PluginAdminProvider, {
|
|
33646
|
+
pluginAdmins,
|
|
33647
|
+
children: /* @__PURE__ */ jsx(QueryClientProvider, {
|
|
33648
|
+
client: queryClient,
|
|
33649
|
+
children: /* @__PURE__ */ jsx(RouterProvider, { router })
|
|
33650
|
+
})
|
|
33084
33651
|
})
|
|
33085
33652
|
}) }) })
|
|
33086
33653
|
}) });
|
|
33087
33654
|
}
|
|
33088
33655
|
|
|
33089
33656
|
//#endregion
|
|
33090
|
-
export { API_BASE, API_TOKEN_SCOPES, AdminApp, AdminApp as App, CAPABILITY_LABELS, ContentEditor, ContentList, DEFAULT_LOCALE, Dashboard, Header, Link, LoginPage, MediaLibrary, MediaPickerModal, PasskeyLogin, PasskeyRegistration, PluginAdminProvider, PortableTextEditor, SUPPORTED_LOCALES, SUPPORTED_LOCALE_CODES, SaveButton, Settings, SetupWizard, Shell, KumoSidebar as Sidebar, SidebarNav, analyzeWpPluginSite, analyzeWxr, apiFetch, bulkCommentAction, checkPluginUpdates, cn, compareRevisions, completeSignup, createAdminRouter, createAllowedDomain, createApiToken, createByline, createCollection, createContent, createField, createMenu, createMenuItem, createRedirect, createSection, createTaxonomy, createTerm, createWidget, createWidgetArea, deleteAllowedDomain, deleteByline, deleteCollection, deleteComment, deleteContent, deleteField, deleteFromProvider, deleteMedia, deleteMenu, deleteMenuItem, deletePasskey, deleteRedirect, deleteSection, deleteTerm, deleteWidget, deleteWidgetArea, describeCapability, disablePlugin, disableUser, discardDraft, duplicateContent, enablePlugin, enableUser, executeWpPluginImport, executeWxrImport, fetch404Summary, fetchAllowedDomains, fetchApiTokens, fetchByline, fetchBylines, fetchCollection, fetchCollections, fetchComment, fetchCommentCounts, fetchComments, fetchContent, fetchContentList, fetchDashboardStats, fetchEmailSettings, fetchFields, fetchManifest, fetchMarketplacePlugin, fetchMediaList, fetchMediaProviders, fetchMenu, fetchMenus, fetchOrphanedTables, fetchPasskeys, fetchPlugin, fetchPlugins, fetchProviderMedia, fetchRedirects, fetchRevision, fetchRevisions, fetchSection, fetchSections, fetchSettings, fetchTaxonomyDef, fetchTaxonomyDefs, fetchTerms, fetchTheme, fetchTranslations, fetchTrashedContent, fetchUser, fetchUsers, fetchWidgetArea, fetchWidgetAreas, fetchWidgetComponents, generatePreviewUrl, getDraftStatus, getPreviewUrl, hasAllowedDomains, importWxrMedia, installMarketplacePlugin, inviteUser, parseApiResponse, permanentDeleteContent, prepareWxrImport, probeImportUrl, publishContent, registerOrphanedTable, renamePasskey, reorderFields, reorderMenuItems, reorderWidgets, requestSignup, resolveLocale, restoreContent, restoreRevision, revokeApiToken, rewriteContentUrls, scheduleContent, searchMarketplace, searchThemes, sendRecoveryLink, sendTestEmail, setSearchEnabled, throwResponseError, uninstallMarketplacePlugin, unpublishContent, unscheduleContent, updateAllowedDomain, updateByline, updateCollection, updateCommentStatus, updateContent, updateField, updateMarketplacePlugin, updateMedia, updateMenu, updateMenuItem, updateRedirect, updateSection, updateSettings, updateTerm, updateUser, updateWidget, uploadMedia, uploadToProvider, useCurrentUser, useLocale, useNavigate, useParams, usePluginAdmins, usePluginField, usePluginHasPages, usePluginHasWidgets, usePluginPage, usePluginWidget, validateInviteToken, verifySignupToken };
|
|
33657
|
+
export { API_BASE, API_TOKEN_SCOPES, AdminApp, AdminApp as App, AuthProviderProvider, CAPABILITY_LABELS, ContentEditor, ContentList, DEFAULT_LOCALE, Dashboard, Header, Link, LoginPage, MediaLibrary, MediaPickerModal, PasskeyLogin, PasskeyRegistration, PluginAdminProvider, PortableTextEditor, SUPPORTED_LOCALES, SUPPORTED_LOCALE_CODES, SaveButton, Settings, SetupWizard, Shell, KumoSidebar as Sidebar, SidebarNav, analyzeWpPluginSite, analyzeWxr, apiFetch, bulkCommentAction, checkPluginUpdates, cn, compareRevisions, completeSignup, createAdminRouter, createAllowedDomain, createApiToken, createByline, createCollection, createContent, createField, createMenu, createMenuItem, createRedirect, createSection, createTaxonomy, createTerm, createWidget, createWidgetArea, deleteAllowedDomain, deleteByline, deleteCollection, deleteComment, deleteContent, deleteField, deleteFromProvider, deleteMedia, deleteMenu, deleteMenuItem, deletePasskey, deleteRedirect, deleteSection, deleteTerm, deleteWidget, deleteWidgetArea, describeCapability, disablePlugin, disableUser, discardDraft, duplicateContent, enablePlugin, enableUser, executeWpPluginImport, executeWxrImport, fetch404Summary, fetchAllowedDomains, fetchApiTokens, fetchAuthMode, fetchByline, fetchBylines, fetchCollection, fetchCollections, fetchComment, fetchCommentCounts, fetchComments, fetchContent, fetchContentList, fetchDashboardStats, fetchEmailSettings, fetchFields, fetchManifest, fetchMarketplacePlugin, fetchMediaList, fetchMediaProviders, fetchMenu, fetchMenus, fetchOrphanedTables, fetchPasskeys, fetchPlugin, fetchPlugins, fetchProviderMedia, fetchRedirects, fetchRevision, fetchRevisions, fetchSection, fetchSections, fetchSettings, fetchTaxonomyDef, fetchTaxonomyDefs, fetchTerms, fetchTheme, fetchTranslations, fetchTrashedContent, fetchUser, fetchUsers, fetchWidgetArea, fetchWidgetAreas, fetchWidgetComponents, generatePreviewUrl, getDraftStatus, getPreviewUrl, hasAllowedDomains, importWxrMedia, installMarketplacePlugin, inviteUser, parseApiResponse, permanentDeleteContent, prepareWxrImport, probeImportUrl, publishContent, registerOrphanedTable, renamePasskey, reorderFields, reorderMenuItems, reorderWidgets, requestSignup, resolveLocale, restoreContent, restoreRevision, revokeApiToken, rewriteContentUrls, scheduleContent, searchMarketplace, searchThemes, sendRecoveryLink, sendTestEmail, setSearchEnabled, throwResponseError, uninstallMarketplacePlugin, unpublishContent, unscheduleContent, updateAllowedDomain, updateByline, updateCollection, updateCommentStatus, updateContent, updateField, updateMarketplacePlugin, updateMedia, updateMenu, updateMenuItem, updateRedirect, updateSection, updateSettings, updateTerm, updateUser, updateWidget, uploadMedia, uploadToProvider, useAuthProviderList, useAuthProviders, useCurrentUser, useLocale, useNavigate, useParams, usePluginAdmins, usePluginField, usePluginHasPages, usePluginHasWidgets, usePluginPage, usePluginWidget, validateInviteToken, verifySignupToken };
|
|
33091
33658
|
//# sourceMappingURL=index.js.map
|