convex-cms 0.0.5-alpha.2 → 0.0.5-alpha.4
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/README.md +44 -9
- package/admin/src/components/BulkActionBar.tsx +4 -4
- package/admin/src/components/ContentEntryEditor.tsx +8 -28
- package/admin/src/components/ContentTypeFormModal.tsx +2 -2
- package/admin/src/components/TaxonomyEditor.tsx +2 -2
- package/admin/src/components/TermTree.tsx +5 -5
- package/admin/src/components/VersionCompare.tsx +1 -1
- package/admin/src/components/VersionHistory.tsx +2 -2
- package/admin/src/components/fields/CategoryField.tsx +1 -1
- package/admin/src/components/fields/MediaField.tsx +4 -4
- package/admin/src/components/fields/ReferenceField.tsx +4 -4
- package/admin/src/components/fields/TagField.tsx +3 -3
- package/admin/src/components/filters/TaxonomyFilter.tsx +2 -2
- package/admin/src/components/media/MediaAssetEditDialog.tsx +2 -2
- package/admin/src/components/media/MediaFolderEditDialog.tsx +1 -1
- package/admin/src/components/media/MediaMoveModal.tsx +2 -2
- package/admin/src/components/media/MediaTaxonomyPicker.tsx +4 -4
- package/admin/src/contexts/SettingsConfigContext.tsx +2 -2
- package/admin/src/pages/MediaPage.tsx +1102 -8
- package/admin/src/pages/SettingsPage.tsx +171 -108
- package/admin/src/routes/entries/$entryId.tsx +2 -2
- package/admin/src/routes/entries/new.$contentTypeId.tsx +1 -1
- package/admin/src/routes/entries/type/$contentTypeId.tsx +6 -6
- package/admin/src/routes/media.tsx +23 -1094
- package/admin-dist/nitro.json +1 -1
- package/admin-dist/server/index.mjs +138 -138
- package/dist/cli/index.js +0 -0
- package/dist/client/admin/index.d.ts +75 -2
- package/dist/client/admin/index.d.ts.map +1 -1
- package/dist/client/admin/index.js +17 -1
- package/dist/client/admin/index.js.map +1 -1
- package/dist/client/admin/media.d.ts.map +1 -1
- package/dist/client/admin/media.js +3 -3
- package/dist/client/admin/media.js.map +1 -1
- package/dist/client/admin/settings.d.ts +50 -0
- package/dist/client/admin/settings.d.ts.map +1 -0
- package/dist/client/admin/settings.js +89 -0
- package/dist/client/admin/settings.js.map +1 -0
- package/dist/client/admin/taxonomies.d.ts.map +1 -1
- package/dist/client/admin/trash.d.ts.map +1 -1
- package/dist/client/admin/types.d.ts +40 -0
- package/dist/client/admin/types.d.ts.map +1 -1
- package/dist/client/admin/validators.d.ts +243 -3
- package/dist/client/admin/validators.d.ts.map +1 -1
- package/dist/client/admin/validators.js +18 -0
- package/dist/client/admin/validators.js.map +1 -1
- package/dist/client/adminConfig.d.ts +1 -1
- package/dist/client/config.d.ts +145 -0
- package/dist/client/config.d.ts.map +1 -0
- package/dist/client/config.js +132 -0
- package/dist/client/config.js.map +1 -0
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +13 -6
- package/dist/client/index.js.map +1 -1
- package/dist/client/schema/defineContentType.js +1 -1
- package/dist/client/schema/defineContentType.js.map +1 -1
- package/dist/client/wrapper.d.ts.map +1 -1
- package/dist/client/wrapper.js +0 -4
- package/dist/client/wrapper.js.map +1 -1
- package/dist/component/_generated/api.d.ts +2 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +42 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/contentEntries.d.ts.map +1 -1
- package/dist/component/contentEntries.js +0 -2
- package/dist/component/contentEntries.js.map +1 -1
- package/dist/component/contentEntryMutations.d.ts.map +1 -1
- package/dist/component/contentEntryMutations.js +0 -1
- package/dist/component/contentEntryMutations.js.map +1 -1
- package/dist/component/contentLock.d.ts.map +1 -1
- package/dist/component/contentLock.js +0 -2
- package/dist/component/contentLock.js.map +1 -1
- package/dist/component/index.d.ts +2 -1
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/index.js +3 -1
- package/dist/component/index.js.map +1 -1
- package/dist/component/schema.d.ts +18 -1
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +6 -1
- package/dist/component/schema.js.map +1 -1
- package/dist/component/settings.d.ts +60 -0
- package/dist/component/settings.d.ts.map +1 -0
- package/dist/component/settings.js +126 -0
- package/dist/component/settings.js.map +1 -0
- package/dist/component/validators.d.ts +36 -0
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +15 -0
- package/dist/component/validators.js.map +1 -1
- package/dist/test.d.ts +11 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +2 -5
- package/dist/test.js.map +1 -1
- package/package.json +12 -3
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* Used by both CLI routes and embed pages.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { useState, useEffect, useCallback } from "react";
|
|
8
|
+
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
9
9
|
import { useQuery, useMutation } from "convex/react";
|
|
10
10
|
import { RouteGuard } from "~/components";
|
|
11
11
|
import { usePermissions } from "~/hooks";
|
|
12
|
-
import {
|
|
12
|
+
import { useAdminConfig, useTheme } from "~/contexts";
|
|
13
13
|
import { CmsPageHeader } from "~/components/cmsds/CmsPageHeader";
|
|
14
14
|
import { CmsSurface } from "~/components/cmsds/CmsSurface";
|
|
15
15
|
import { CmsButton } from "~/components/cmsds/CmsButton";
|
|
@@ -25,24 +25,40 @@ import { Label } from "~/components/ui/label";
|
|
|
25
25
|
import { Badge } from "~/components/ui/badge";
|
|
26
26
|
import { Alert, AlertDescription } from "~/components/ui/alert";
|
|
27
27
|
import { cn } from "~/lib/cn";
|
|
28
|
-
import {
|
|
28
|
+
import { Check, X, Sun, Moon, Monitor, Lock, Info } from "lucide-react";
|
|
29
29
|
import type { AdminNavigation } from "~/lib/navigation";
|
|
30
30
|
import { CmsAdminApi } from "~/embed/contexts/ApiContext";
|
|
31
31
|
|
|
32
|
+
interface FeatureFlags {
|
|
33
|
+
versioning: boolean;
|
|
34
|
+
scheduling: boolean;
|
|
35
|
+
localization: boolean;
|
|
36
|
+
mediaManagement: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
interface Settings {
|
|
33
40
|
_id: string | null;
|
|
34
41
|
defaultLocale: string;
|
|
35
42
|
availableLocales: string[];
|
|
36
|
-
features:
|
|
37
|
-
versioning: boolean;
|
|
38
|
-
scheduling: boolean;
|
|
39
|
-
localization: boolean;
|
|
40
|
-
mediaManagement: boolean;
|
|
41
|
-
};
|
|
43
|
+
features: FeatureFlags;
|
|
42
44
|
updatedBy?: string;
|
|
43
45
|
_creationTime?: number;
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
const DEFAULT_FEATURES: FeatureFlags = {
|
|
49
|
+
versioning: true,
|
|
50
|
+
scheduling: true,
|
|
51
|
+
localization: false,
|
|
52
|
+
mediaManagement: true,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const DEFAULT_SETTINGS: Settings = {
|
|
56
|
+
_id: null,
|
|
57
|
+
defaultLocale: "en",
|
|
58
|
+
availableLocales: ["en", "es", "fr", "de"],
|
|
59
|
+
features: DEFAULT_FEATURES,
|
|
60
|
+
};
|
|
61
|
+
|
|
46
62
|
const LOCALE_OPTIONS = [
|
|
47
63
|
{ value: "en", label: "English (en)" },
|
|
48
64
|
{ value: "es", label: "Spanish (es)" },
|
|
@@ -116,28 +132,55 @@ export function SettingsPage({
|
|
|
116
132
|
}: SettingsPageProps) {
|
|
117
133
|
const { canManageSettings } = usePermissions();
|
|
118
134
|
const canEdit = canManageSettings();
|
|
119
|
-
const
|
|
135
|
+
const adminConfig = useAdminConfig();
|
|
136
|
+
|
|
137
|
+
const isConfigured = useMemo(() => {
|
|
138
|
+
return typeof api.getSettings === "function";
|
|
139
|
+
}, [api]);
|
|
120
140
|
|
|
121
|
-
const settings = useQuery(
|
|
122
|
-
|
|
123
|
-
|
|
141
|
+
const settings = useQuery(
|
|
142
|
+
isConfigured ? api.getSettings : ("skip" as unknown as typeof api.getSettings),
|
|
143
|
+
isConfigured ? {} : "skip"
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const updateSettingsMutation = useMutation(
|
|
147
|
+
api.updateSettings ?? ((() => {}) as unknown as typeof api.updateSettings)
|
|
148
|
+
);
|
|
149
|
+
const resetSettingsMutation = useMutation(
|
|
150
|
+
api.resetSettings ?? ((() => {}) as unknown as typeof api.resetSettings)
|
|
151
|
+
);
|
|
124
152
|
|
|
125
153
|
const [formData, setFormData] = useState<Settings | null>(null);
|
|
126
154
|
const [isDirty, setIsDirty] = useState(false);
|
|
127
155
|
const [feedbackStatus, setFeedbackStatus] = useState<FeedbackStatus>("idle");
|
|
128
156
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
129
157
|
|
|
158
|
+
const normalizedSettings = useMemo((): Settings | null => {
|
|
159
|
+
if (!settings) return null;
|
|
160
|
+
return {
|
|
161
|
+
_id: settings._id ?? null,
|
|
162
|
+
defaultLocale: settings.defaultLocale ?? DEFAULT_SETTINGS.defaultLocale,
|
|
163
|
+
availableLocales: settings.availableLocales ?? DEFAULT_SETTINGS.availableLocales,
|
|
164
|
+
features: {
|
|
165
|
+
...DEFAULT_FEATURES,
|
|
166
|
+
...(settings.features ?? {}),
|
|
167
|
+
},
|
|
168
|
+
updatedBy: settings.updatedBy,
|
|
169
|
+
_creationTime: settings._creationTime,
|
|
170
|
+
};
|
|
171
|
+
}, [settings]);
|
|
172
|
+
|
|
130
173
|
useEffect(() => {
|
|
131
|
-
if (
|
|
132
|
-
setFormData(
|
|
174
|
+
if (normalizedSettings && !formData) {
|
|
175
|
+
setFormData(normalizedSettings);
|
|
133
176
|
}
|
|
134
|
-
}, [
|
|
177
|
+
}, [normalizedSettings, formData]);
|
|
135
178
|
|
|
136
179
|
useEffect(() => {
|
|
137
|
-
if (
|
|
138
|
-
setFormData(
|
|
180
|
+
if (normalizedSettings && !isDirty) {
|
|
181
|
+
setFormData(normalizedSettings);
|
|
139
182
|
}
|
|
140
|
-
}, [
|
|
183
|
+
}, [normalizedSettings, isDirty]);
|
|
141
184
|
|
|
142
185
|
const handleLocaleChange = useCallback(
|
|
143
186
|
(value: string) => {
|
|
@@ -153,33 +196,15 @@ export function SettingsPage({
|
|
|
153
196
|
[formData],
|
|
154
197
|
);
|
|
155
198
|
|
|
156
|
-
const handleFeatureChange = useCallback(
|
|
157
|
-
(feature: keyof Settings["features"]) => {
|
|
158
|
-
if (!formData) return;
|
|
159
|
-
|
|
160
|
-
setFormData({
|
|
161
|
-
...formData,
|
|
162
|
-
features: {
|
|
163
|
-
...formData.features,
|
|
164
|
-
[feature]: !formData.features[feature],
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
setIsDirty(true);
|
|
168
|
-
setFeedbackStatus("idle");
|
|
169
|
-
},
|
|
170
|
-
[formData],
|
|
171
|
-
);
|
|
172
|
-
|
|
173
199
|
const handleSave = useCallback(async () => {
|
|
174
|
-
if (!formData || !isDirty) return;
|
|
200
|
+
if (!formData || !isDirty || !isConfigured || !api.updateSettings) return;
|
|
175
201
|
|
|
176
202
|
setFeedbackStatus("saving");
|
|
177
203
|
setErrorMessage(null);
|
|
178
204
|
|
|
179
205
|
try {
|
|
180
|
-
await
|
|
206
|
+
await updateSettingsMutation({
|
|
181
207
|
defaultLocale: formData.defaultLocale,
|
|
182
|
-
features: formData.features,
|
|
183
208
|
});
|
|
184
209
|
|
|
185
210
|
setFeedbackStatus("saved");
|
|
@@ -194,9 +219,11 @@ export function SettingsPage({
|
|
|
194
219
|
error instanceof Error ? error.message : "Failed to save settings",
|
|
195
220
|
);
|
|
196
221
|
}
|
|
197
|
-
}, [formData, isDirty, updateSettings]);
|
|
222
|
+
}, [formData, isDirty, isConfigured, api.updateSettings, updateSettingsMutation]);
|
|
198
223
|
|
|
199
224
|
const handleReset = useCallback(async () => {
|
|
225
|
+
if (!isConfigured || !api.resetSettings) return;
|
|
226
|
+
|
|
200
227
|
const confirmed = window.confirm(
|
|
201
228
|
"Are you sure you want to reset all settings to their defaults? This action cannot be undone.",
|
|
202
229
|
);
|
|
@@ -207,8 +234,17 @@ export function SettingsPage({
|
|
|
207
234
|
setErrorMessage(null);
|
|
208
235
|
|
|
209
236
|
try {
|
|
210
|
-
const newSettings = await
|
|
211
|
-
setFormData(
|
|
237
|
+
const newSettings = await resetSettingsMutation({});
|
|
238
|
+
setFormData({
|
|
239
|
+
...DEFAULT_SETTINGS,
|
|
240
|
+
_id: (newSettings as Settings)?._id ?? null,
|
|
241
|
+
defaultLocale: (newSettings as Settings)?.defaultLocale ?? DEFAULT_SETTINGS.defaultLocale,
|
|
242
|
+
availableLocales: (newSettings as Settings)?.availableLocales ?? DEFAULT_SETTINGS.availableLocales,
|
|
243
|
+
features: {
|
|
244
|
+
...DEFAULT_FEATURES,
|
|
245
|
+
...((newSettings as Settings)?.features ?? {}),
|
|
246
|
+
},
|
|
247
|
+
});
|
|
212
248
|
setFeedbackStatus("saved");
|
|
213
249
|
setIsDirty(false);
|
|
214
250
|
|
|
@@ -221,18 +257,18 @@ export function SettingsPage({
|
|
|
221
257
|
error instanceof Error ? error.message : "Failed to reset settings",
|
|
222
258
|
);
|
|
223
259
|
}
|
|
224
|
-
}, [resetSettings]);
|
|
260
|
+
}, [isConfigured, api.resetSettings, resetSettingsMutation]);
|
|
225
261
|
|
|
226
262
|
const handleDiscard = useCallback(() => {
|
|
227
|
-
if (
|
|
228
|
-
setFormData(
|
|
263
|
+
if (normalizedSettings) {
|
|
264
|
+
setFormData(normalizedSettings);
|
|
229
265
|
setIsDirty(false);
|
|
230
266
|
setFeedbackStatus("idle");
|
|
231
267
|
setErrorMessage(null);
|
|
232
268
|
}
|
|
233
|
-
}, [
|
|
269
|
+
}, [normalizedSettings]);
|
|
234
270
|
|
|
235
|
-
if (
|
|
271
|
+
if (!isConfigured) {
|
|
236
272
|
return (
|
|
237
273
|
<RouteGuard
|
|
238
274
|
requiredPermission={{ resource: "settings", action: "manage" }}
|
|
@@ -242,18 +278,62 @@ export function SettingsPage({
|
|
|
242
278
|
title="Settings"
|
|
243
279
|
description="Configure your CMS settings and preferences."
|
|
244
280
|
/>
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
281
|
+
|
|
282
|
+
<div className="space-y-6">
|
|
283
|
+
<AppearanceSection />
|
|
284
|
+
|
|
285
|
+
<Alert>
|
|
286
|
+
<Info className="size-4" />
|
|
287
|
+
<AlertDescription>
|
|
288
|
+
<strong>Settings not configured.</strong> To enable CMS settings, export{" "}
|
|
289
|
+
<code className="rounded bg-muted px-1 py-0.5 text-xs">getSettings</code>,{" "}
|
|
290
|
+
<code className="rounded bg-muted px-1 py-0.5 text-xs">updateSettings</code>, and{" "}
|
|
291
|
+
<code className="rounded bg-muted px-1 py-0.5 text-xs">resetSettings</code> from your{" "}
|
|
292
|
+
<code className="rounded bg-muted px-1 py-0.5 text-xs">convex/admin.ts</code> file.
|
|
293
|
+
</AlertDescription>
|
|
294
|
+
</Alert>
|
|
295
|
+
|
|
296
|
+
<CmsSurface elevation="base" className="p-6">
|
|
297
|
+
<div className="mb-4 flex items-center gap-2">
|
|
298
|
+
<h2 className="text-lg font-semibold text-foreground">Features</h2>
|
|
299
|
+
<Badge variant="secondary" className="gap-1">
|
|
300
|
+
<Lock className="size-3" />
|
|
301
|
+
Default values
|
|
302
|
+
</Badge>
|
|
303
|
+
</div>
|
|
304
|
+
<p className="mb-4 text-sm text-muted-foreground">
|
|
305
|
+
Showing default feature flags. Configure settings in your admin API to customize.
|
|
306
|
+
</p>
|
|
307
|
+
<div className="space-y-4">
|
|
308
|
+
{(["versioning", "scheduling", "localization", "mediaManagement"] as const).map((feature) => (
|
|
309
|
+
<div key={feature} className="flex items-center justify-between opacity-75">
|
|
310
|
+
<div>
|
|
311
|
+
<Label className="text-sm font-medium capitalize">{feature}</Label>
|
|
312
|
+
</div>
|
|
313
|
+
<Switch checked={DEFAULT_FEATURES[feature]} disabled={true} />
|
|
314
|
+
</div>
|
|
315
|
+
))}
|
|
316
|
+
</div>
|
|
317
|
+
</CmsSurface>
|
|
318
|
+
|
|
319
|
+
<CmsSurface elevation="base" className="p-6">
|
|
320
|
+
<h2 className="mb-4 text-lg font-semibold text-foreground">API</h2>
|
|
321
|
+
<div className="space-y-4">
|
|
322
|
+
<div>
|
|
323
|
+
<Label className="text-sm font-medium">Convex Deployment URL</Label>
|
|
324
|
+
<code className="mt-1 block rounded-md bg-muted px-3 py-2 text-sm">
|
|
325
|
+
{import.meta.env.VITE_CONVEX_URL || "Not configured"}
|
|
326
|
+
</code>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</CmsSurface>
|
|
250
330
|
</div>
|
|
251
331
|
</div>
|
|
252
332
|
</RouteGuard>
|
|
253
333
|
);
|
|
254
334
|
}
|
|
255
335
|
|
|
256
|
-
if (settings ===
|
|
336
|
+
if (settings === undefined) {
|
|
257
337
|
return (
|
|
258
338
|
<RouteGuard
|
|
259
339
|
requiredPermission={{ resource: "settings", action: "manage" }}
|
|
@@ -263,17 +343,20 @@ export function SettingsPage({
|
|
|
263
343
|
title="Settings"
|
|
264
344
|
description="Configure your CMS settings and preferences."
|
|
265
345
|
/>
|
|
266
|
-
<
|
|
267
|
-
<
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
</
|
|
271
|
-
</
|
|
346
|
+
<div className="flex flex-col items-center justify-center py-12">
|
|
347
|
+
<div className="size-8 animate-spin rounded-full border-2 border-muted border-t-primary" />
|
|
348
|
+
<p className="mt-4 text-sm text-muted-foreground">
|
|
349
|
+
Loading settings...
|
|
350
|
+
</p>
|
|
351
|
+
</div>
|
|
272
352
|
</div>
|
|
273
353
|
</RouteGuard>
|
|
274
354
|
);
|
|
275
355
|
}
|
|
276
356
|
|
|
357
|
+
const displayData = formData ?? normalizedSettings ?? DEFAULT_SETTINGS;
|
|
358
|
+
const features = displayData.features ?? DEFAULT_FEATURES;
|
|
359
|
+
|
|
277
360
|
return (
|
|
278
361
|
<RouteGuard requiredPermission={{ resource: "settings", action: "manage" }}>
|
|
279
362
|
<div className="space-y-6 p-6">
|
|
@@ -283,7 +366,7 @@ export function SettingsPage({
|
|
|
283
366
|
description="Configure your CMS settings and preferences."
|
|
284
367
|
/>
|
|
285
368
|
|
|
286
|
-
{canEdit && (
|
|
369
|
+
{canEdit && api.updateSettings && (
|
|
287
370
|
<div className="flex items-center gap-3">
|
|
288
371
|
{feedbackStatus === "saved" && (
|
|
289
372
|
<Badge
|
|
@@ -325,7 +408,7 @@ export function SettingsPage({
|
|
|
325
408
|
<div className="space-y-6">
|
|
326
409
|
<AppearanceSection />
|
|
327
410
|
|
|
328
|
-
{
|
|
411
|
+
{features.localization && (
|
|
329
412
|
<CmsSurface elevation="base" className="p-6">
|
|
330
413
|
<h2 className="mb-4 text-lg font-semibold text-foreground">
|
|
331
414
|
General
|
|
@@ -341,9 +424,9 @@ export function SettingsPage({
|
|
|
341
424
|
</p>
|
|
342
425
|
</div>
|
|
343
426
|
<Select
|
|
344
|
-
value={
|
|
427
|
+
value={displayData.defaultLocale}
|
|
345
428
|
onValueChange={handleLocaleChange}
|
|
346
|
-
disabled={!canEdit || feedbackStatus === "saving"}
|
|
429
|
+
disabled={!canEdit || feedbackStatus === "saving" || !api.updateSettings}
|
|
347
430
|
>
|
|
348
431
|
<SelectTrigger className="w-48">
|
|
349
432
|
<SelectValue />
|
|
@@ -362,76 +445,56 @@ export function SettingsPage({
|
|
|
362
445
|
)}
|
|
363
446
|
|
|
364
447
|
<CmsSurface elevation="base" className="p-6">
|
|
365
|
-
<
|
|
366
|
-
Features
|
|
367
|
-
|
|
448
|
+
<div className="mb-4 flex items-center gap-2">
|
|
449
|
+
<h2 className="text-lg font-semibold text-foreground">Features</h2>
|
|
450
|
+
<Badge variant="secondary" className="gap-1">
|
|
451
|
+
<Lock className="size-3" />
|
|
452
|
+
Configured in code
|
|
453
|
+
</Badge>
|
|
454
|
+
</div>
|
|
455
|
+
<p className="mb-4 text-sm text-muted-foreground">
|
|
456
|
+
Feature flags are defined in your Convex configuration and cannot be changed from the UI.
|
|
457
|
+
</p>
|
|
368
458
|
<div className="space-y-4">
|
|
369
|
-
<div className="flex items-center justify-between">
|
|
459
|
+
<div className="flex items-center justify-between opacity-75">
|
|
370
460
|
<div>
|
|
371
|
-
<Label className="text-sm font-medium">
|
|
372
|
-
Enable Versioning
|
|
373
|
-
</Label>
|
|
461
|
+
<Label className="text-sm font-medium">Versioning</Label>
|
|
374
462
|
<p className="text-sm text-muted-foreground">
|
|
375
|
-
Track content history and enable rollback to previous
|
|
376
|
-
versions
|
|
463
|
+
Track content history and enable rollback to previous versions
|
|
377
464
|
</p>
|
|
378
465
|
</div>
|
|
379
|
-
<Switch
|
|
380
|
-
checked={formData?.features.versioning ?? true}
|
|
381
|
-
onCheckedChange={() => handleFeatureChange("versioning")}
|
|
382
|
-
disabled={!canEdit || feedbackStatus === "saving"}
|
|
383
|
-
/>
|
|
466
|
+
<Switch checked={features.versioning} disabled={true} />
|
|
384
467
|
</div>
|
|
385
468
|
|
|
386
|
-
<div className="flex items-center justify-between">
|
|
469
|
+
<div className="flex items-center justify-between opacity-75">
|
|
387
470
|
<div>
|
|
388
|
-
<Label className="text-sm font-medium">
|
|
389
|
-
Enable Scheduling
|
|
390
|
-
</Label>
|
|
471
|
+
<Label className="text-sm font-medium">Scheduling</Label>
|
|
391
472
|
<p className="text-sm text-muted-foreground">
|
|
392
473
|
Schedule content to publish at a future date and time
|
|
393
474
|
</p>
|
|
394
475
|
</div>
|
|
395
|
-
<Switch
|
|
396
|
-
checked={formData?.features.scheduling ?? true}
|
|
397
|
-
onCheckedChange={() => handleFeatureChange("scheduling")}
|
|
398
|
-
disabled={!canEdit || feedbackStatus === "saving"}
|
|
399
|
-
/>
|
|
476
|
+
<Switch checked={features.scheduling} disabled={true} />
|
|
400
477
|
</div>
|
|
401
478
|
|
|
402
|
-
<div className="flex items-center justify-between">
|
|
479
|
+
<div className="flex items-center justify-between opacity-75">
|
|
403
480
|
<div>
|
|
404
|
-
<Label className="text-sm font-medium">
|
|
405
|
-
Enable Localization
|
|
406
|
-
</Label>
|
|
481
|
+
<Label className="text-sm font-medium">Localization</Label>
|
|
407
482
|
<p className="text-sm text-muted-foreground">
|
|
408
483
|
Support multiple languages for content entries
|
|
409
484
|
</p>
|
|
410
485
|
</div>
|
|
411
|
-
<Switch
|
|
412
|
-
checked={formData?.features.localization ?? false}
|
|
413
|
-
onCheckedChange={() => handleFeatureChange("localization")}
|
|
414
|
-
disabled={!canEdit || feedbackStatus === "saving"}
|
|
415
|
-
/>
|
|
486
|
+
<Switch checked={features.localization} disabled={true} />
|
|
416
487
|
</div>
|
|
417
488
|
|
|
418
|
-
{
|
|
419
|
-
<div className="flex items-center justify-between">
|
|
489
|
+
{adminConfig.navigation.showMedia && (
|
|
490
|
+
<div className="flex items-center justify-between opacity-75">
|
|
420
491
|
<div>
|
|
421
|
-
<Label className="text-sm font-medium">
|
|
422
|
-
Enable Media Management
|
|
423
|
-
</Label>
|
|
492
|
+
<Label className="text-sm font-medium">Media Management</Label>
|
|
424
493
|
<p className="text-sm text-muted-foreground">
|
|
425
494
|
Use the built-in media library for image and file uploads
|
|
426
495
|
</p>
|
|
427
496
|
</div>
|
|
428
|
-
<Switch
|
|
429
|
-
checked={formData?.features.mediaManagement ?? true}
|
|
430
|
-
onCheckedChange={() =>
|
|
431
|
-
handleFeatureChange("mediaManagement")
|
|
432
|
-
}
|
|
433
|
-
disabled={!canEdit || feedbackStatus === "saving"}
|
|
434
|
-
/>
|
|
497
|
+
<Switch checked={features.mediaManagement} disabled={true} />
|
|
435
498
|
</div>
|
|
436
499
|
)}
|
|
437
500
|
</div>
|
|
@@ -451,7 +514,7 @@ export function SettingsPage({
|
|
|
451
514
|
</div>
|
|
452
515
|
</CmsSurface>
|
|
453
516
|
|
|
454
|
-
{canEdit && (
|
|
517
|
+
{canEdit && api.resetSettings && (
|
|
455
518
|
<CmsSurface
|
|
456
519
|
elevation="base"
|
|
457
520
|
className="border-red-200 p-6 dark:border-red-900"
|
|
@@ -17,10 +17,10 @@ function EditEntryPage() {
|
|
|
17
17
|
const navigate = useNavigate();
|
|
18
18
|
const { canDelete } = usePermissions();
|
|
19
19
|
|
|
20
|
-
const entry = useQuery(api.
|
|
20
|
+
const entry = useQuery(api.admin.getEntry, { id: entryId });
|
|
21
21
|
|
|
22
22
|
const contentType = useQuery(
|
|
23
|
-
api.
|
|
23
|
+
api.admin.getContentType,
|
|
24
24
|
entry ? { id: entry.contentTypeId } : 'skip'
|
|
25
25
|
);
|
|
26
26
|
|
|
@@ -15,7 +15,7 @@ function NewEntryPage() {
|
|
|
15
15
|
const { contentTypeId } = Route.useParams();
|
|
16
16
|
const navigate = useNavigate();
|
|
17
17
|
|
|
18
|
-
const contentType = useQuery(api.
|
|
18
|
+
const contentType = useQuery(api.admin.getContentType, { id: contentTypeId });
|
|
19
19
|
|
|
20
20
|
useBreadcrumbLabel(
|
|
21
21
|
`/entries/new/${contentTypeId}`,
|
|
@@ -50,7 +50,7 @@ function ContentTypeEntriesPage() {
|
|
|
50
50
|
|
|
51
51
|
const { canCreate, canUpdate, canDelete } = usePermissions()
|
|
52
52
|
|
|
53
|
-
const deleteEntry = useMutation(api.
|
|
53
|
+
const deleteEntry = useMutation(api.admin.deleteEntry)
|
|
54
54
|
|
|
55
55
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
|
|
56
56
|
const [entryToDelete, setEntryToDelete] = useState<{ _id: string; title: string } | null>(null)
|
|
@@ -65,11 +65,11 @@ function ContentTypeEntriesPage() {
|
|
|
65
65
|
return () => clearTimeout(timer)
|
|
66
66
|
}, [searchQuery])
|
|
67
67
|
|
|
68
|
-
const contentType = useQuery(api.
|
|
68
|
+
const contentType = useQuery(api.admin.getContentType, { id: contentTypeId })
|
|
69
69
|
|
|
70
70
|
useBreadcrumbLabel(`/entries/type/${contentTypeId}`, contentType?.displayName)
|
|
71
71
|
|
|
72
|
-
const entriesResult = useQuery(api.
|
|
72
|
+
const entriesResult = useQuery(api.admin.listEntries, {
|
|
73
73
|
contentTypeId: contentTypeId,
|
|
74
74
|
status: selectedStatus === 'all' ? undefined : selectedStatus,
|
|
75
75
|
search: debouncedSearch || undefined,
|
|
@@ -78,15 +78,15 @@ function ContentTypeEntriesPage() {
|
|
|
78
78
|
const allEntries = entriesResult?.page ?? []
|
|
79
79
|
|
|
80
80
|
const entriesByTermResult0 = useQuery(
|
|
81
|
-
api.
|
|
81
|
+
api.admin.getEntriesByTerm,
|
|
82
82
|
selectedTermIds[0] ? { termId: selectedTermIds[0] } : 'skip'
|
|
83
83
|
)
|
|
84
84
|
const entriesByTermResult1 = useQuery(
|
|
85
|
-
api.
|
|
85
|
+
api.admin.getEntriesByTerm,
|
|
86
86
|
selectedTermIds[1] ? { termId: selectedTermIds[1] } : 'skip'
|
|
87
87
|
)
|
|
88
88
|
const entriesByTermResult2 = useQuery(
|
|
89
|
-
api.
|
|
89
|
+
api.admin.getEntriesByTerm,
|
|
90
90
|
selectedTermIds[2] ? { termId: selectedTermIds[2] } : 'skip'
|
|
91
91
|
)
|
|
92
92
|
|