@actuate-media/cms-admin 0.69.0 → 0.69.1
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/CHANGELOG.md +6 -0
- package/dist/__tests__/views/form-entries.render.test.js +14 -4
- package/dist/__tests__/views/form-entries.render.test.js.map +1 -1
- package/dist/__tests__/views/form-submissions.render.test.js +14 -4
- package/dist/__tests__/views/form-submissions.render.test.js.map +1 -1
- package/dist/__tests__/views/media-seo-audit.render.test.js +26 -0
- package/dist/__tests__/views/media-seo-audit.render.test.js.map +1 -1
- package/dist/__tests__/views/redirects-pane.render.test.d.ts +2 -0
- package/dist/__tests__/views/redirects-pane.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/redirects-pane.render.test.js +121 -0
- package/dist/__tests__/views/redirects-pane.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/SEOPanel.js +3 -3
- package/dist/components/SEOPanel.js.map +1 -1
- package/dist/components/collections/EntryDetailPane.js +1 -1
- package/dist/components/collections/EntryDetailPane.js.map +1 -1
- package/dist/components/forms/EntriesTable.d.ts +3 -1
- package/dist/components/forms/EntriesTable.d.ts.map +1 -1
- package/dist/components/forms/EntriesTable.js +5 -2
- package/dist/components/forms/EntriesTable.js.map +1 -1
- package/dist/components/forms/FormEntryDetailPane.d.ts +10 -0
- package/dist/components/forms/FormEntryDetailPane.d.ts.map +1 -0
- package/dist/components/forms/FormEntryDetailPane.js +137 -0
- package/dist/components/forms/FormEntryDetailPane.js.map +1 -0
- package/dist/components/forms/FormSchemaPane.js +1 -1
- package/dist/components/forms/FormSchemaPane.js.map +1 -1
- package/dist/components/script-tags/ScriptTagEditorPane.js +1 -1
- package/dist/components/script-tags/ScriptTagEditorPane.js.map +1 -1
- package/dist/components/seo/RedirectEditorPane.d.ts +12 -0
- package/dist/components/seo/RedirectEditorPane.d.ts.map +1 -0
- package/dist/components/seo/RedirectEditorPane.js +89 -0
- package/dist/components/seo/RedirectEditorPane.js.map +1 -0
- package/dist/components/seo/SeoEditorPane.js +1 -1
- package/dist/components/seo/SeoEditorPane.js.map +1 -1
- package/dist/components/ui/DataTable.js +2 -2
- package/dist/components/ui/DataTable.js.map +1 -1
- package/dist/components/ui/Skeleton.js +3 -3
- package/dist/components/ui/Skeleton.js.map +1 -1
- package/dist/components/ui/Toggle.js +1 -1
- package/dist/components/ui/Toggle.js.map +1 -1
- package/dist/fields/BlockBuilderField.js +1 -1
- package/dist/fields/BlockBuilderField.js.map +1 -1
- package/dist/fields/MediaField.js +1 -1
- package/dist/fields/MediaField.js.map +1 -1
- package/dist/fields/PropInput.js +1 -1
- package/dist/fields/PropInput.js.map +1 -1
- package/dist/layout/Header.js +1 -1
- package/dist/layout/Header.js.map +1 -1
- package/dist/views/DocumentEdit.js +1 -1
- package/dist/views/DocumentEdit.js.map +1 -1
- package/dist/views/FormEntries.d.ts.map +1 -1
- package/dist/views/FormEntries.js +18 -14
- package/dist/views/FormEntries.js.map +1 -1
- package/dist/views/FormSubmissions.d.ts.map +1 -1
- package/dist/views/FormSubmissions.js +18 -15
- package/dist/views/FormSubmissions.js.map +1 -1
- package/dist/views/Forms.js +5 -5
- package/dist/views/Forms.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +1 -1
- package/dist/views/MediaBrowser.js.map +1 -1
- package/dist/views/MediaSeoAudit.d.ts +4 -1
- package/dist/views/MediaSeoAudit.d.ts.map +1 -1
- package/dist/views/MediaSeoAudit.js +12 -2
- package/dist/views/MediaSeoAudit.js.map +1 -1
- package/dist/views/Pages/PagesListView.js +14 -14
- package/dist/views/Pages/PagesListView.js.map +1 -1
- package/dist/views/Pages/PagesTreeTable.d.ts.map +1 -1
- package/dist/views/Pages/PagesTreeTable.js +3 -3
- package/dist/views/Pages/PagesTreeTable.js.map +1 -1
- package/dist/views/ScriptTags.d.ts.map +1 -1
- package/dist/views/ScriptTags.js +3 -3
- package/dist/views/ScriptTags.js.map +1 -1
- package/dist/views/seo/ContentTab.js +6 -6
- package/dist/views/seo/ContentTab.js.map +1 -1
- package/dist/views/seo/RedirectsTab.d.ts.map +1 -1
- package/dist/views/seo/RedirectsTab.js +23 -8
- package/dist/views/seo/RedirectsTab.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/views/form-entries.render.test.tsx +16 -4
- package/src/__tests__/views/form-submissions.render.test.tsx +16 -4
- package/src/__tests__/views/media-seo-audit.render.test.tsx +34 -0
- package/src/__tests__/views/redirects-pane.render.test.tsx +156 -0
- package/src/components/SEOPanel.tsx +9 -9
- package/src/components/collections/EntryDetailPane.tsx +1 -1
- package/src/components/forms/EntriesTable.tsx +12 -3
- package/src/components/forms/{EntryDetailDrawer.tsx → FormEntryDetailPane.tsx} +112 -102
- package/src/components/forms/FormSchemaPane.tsx +1 -1
- package/src/components/script-tags/ScriptTagEditorPane.tsx +1 -1
- package/src/components/seo/RedirectEditorPane.tsx +271 -0
- package/src/components/seo/SeoEditorPane.tsx +1 -1
- package/src/components/ui/DataTable.tsx +3 -3
- package/src/components/ui/Skeleton.tsx +8 -8
- package/src/components/ui/Toggle.tsx +1 -1
- package/src/fields/BlockBuilderField.tsx +1 -1
- package/src/fields/MediaField.tsx +1 -1
- package/src/fields/PropInput.tsx +1 -1
- package/src/layout/Header.tsx +2 -2
- package/src/styles/actuate-tokens.css +4 -0
- package/src/styles/theme.css +9 -3
- package/src/views/DocumentEdit.tsx +4 -4
- package/src/views/FormEntries.tsx +81 -66
- package/src/views/FormSubmissions.tsx +78 -64
- package/src/views/Forms.tsx +16 -16
- package/src/views/MediaBrowser.tsx +6 -1
- package/src/views/MediaSeoAudit.tsx +39 -2
- package/src/views/Pages/PagesListView.tsx +46 -46
- package/src/views/Pages/PagesTreeTable.tsx +11 -8
- package/src/views/ScriptTags.tsx +26 -36
- package/src/views/seo/ContentTab.tsx +10 -10
- package/src/views/seo/RedirectsTab.tsx +145 -109
- package/dist/components/forms/EntryDetailDrawer.d.ts +0 -11
- package/dist/components/forms/EntryDetailDrawer.d.ts.map +0 -1
- package/dist/components/forms/EntryDetailDrawer.js +0 -145
- package/dist/components/forms/EntryDetailDrawer.js.map +0 -1
- package/dist/components/seo/RedirectEditorDrawer.d.ts +0 -9
- package/dist/components/seo/RedirectEditorDrawer.d.ts.map +0 -1
- package/dist/components/seo/RedirectEditorDrawer.js +0 -95
- package/dist/components/seo/RedirectEditorDrawer.js.map +0 -1
- package/src/components/seo/RedirectEditorDrawer.tsx +0 -231
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { useRef, useState } from 'react'
|
|
4
4
|
import {
|
|
5
5
|
Plus,
|
|
6
|
-
Pencil,
|
|
7
6
|
Trash2,
|
|
8
7
|
Upload,
|
|
9
8
|
Download,
|
|
@@ -39,7 +38,8 @@ import {
|
|
|
39
38
|
btnSecondary,
|
|
40
39
|
btnGhostIcon,
|
|
41
40
|
} from '../../components/seo/primitives.js'
|
|
42
|
-
import {
|
|
41
|
+
import { RedirectEditorPane } from '../../components/seo/RedirectEditorPane.js'
|
|
42
|
+
import { InspectorPane, SplitPaneLayout, SplitPaneList } from '../../components/ui/InspectorPane.js'
|
|
43
43
|
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js'
|
|
44
44
|
import { useSeoResource } from './useSeoResource.js'
|
|
45
45
|
|
|
@@ -227,10 +227,14 @@ export function RedirectsTab() {
|
|
|
227
227
|
bump()
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
const toggleRow = (rule: RedirectRule) =>
|
|
231
|
+
setEditor((prev) => (prev?.rule?.id === rule.id ? null : { rule }))
|
|
232
|
+
|
|
230
233
|
async function handleDelete(rule: RedirectRule) {
|
|
231
234
|
const res = await deleteRedirectRule(rule.id)
|
|
232
235
|
if (res.error) return toast.error(res.error)
|
|
233
236
|
toast.success('Redirect deleted.')
|
|
237
|
+
setEditor((cur) => (cur?.rule?.id === rule.id ? null : cur))
|
|
234
238
|
refetchAll()
|
|
235
239
|
}
|
|
236
240
|
|
|
@@ -272,117 +276,149 @@ export function RedirectsTab() {
|
|
|
272
276
|
<ChainMapCard onFlattened={refetchAll} />
|
|
273
277
|
</div>
|
|
274
278
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
accept=".csv,text/csv"
|
|
283
|
-
className="sr-only"
|
|
284
|
-
aria-hidden
|
|
285
|
-
tabIndex={-1}
|
|
286
|
-
onChange={(e) => {
|
|
287
|
-
const f = e.target.files?.[0]
|
|
288
|
-
if (f) handleImportFile(f)
|
|
289
|
-
e.target.value = ''
|
|
290
|
-
}}
|
|
291
|
-
/>
|
|
292
|
-
<button className={btnSecondary} onClick={() => fileRef.current?.click()}>
|
|
293
|
-
<Upload className="h-4 w-4" aria-hidden /> Import
|
|
294
|
-
</button>
|
|
295
|
-
<button className={btnSecondary} onClick={handleExport}>
|
|
296
|
-
<Download className="h-4 w-4" aria-hidden /> Export
|
|
297
|
-
</button>
|
|
298
|
-
<button className={btnPrimary} onClick={() => setEditor({ rule: null })}>
|
|
299
|
-
<Plus className="h-4 w-4" aria-hidden /> New redirect
|
|
300
|
-
</button>
|
|
301
|
-
</div>
|
|
302
|
-
}
|
|
303
|
-
>
|
|
304
|
-
{loading ? (
|
|
305
|
-
<SeoLoading label="Loading redirects…" />
|
|
306
|
-
) : error ? (
|
|
307
|
-
<SeoErrorState message={error} onRetry={refetch} />
|
|
308
|
-
) : (data ?? []).length === 0 ? (
|
|
309
|
-
<SeoEmptyState
|
|
310
|
-
icon={<Link2 size={24} />}
|
|
311
|
-
title="No redirects yet"
|
|
312
|
-
description="Create a redirect or import a CSV to get started."
|
|
279
|
+
{/* The SEO page scrolls (tabbed layout), so the split area takes a
|
|
280
|
+
viewport-derived height to keep the pane's body/footer independently
|
|
281
|
+
scrollable, matching the Content tab's inline editor pane. */}
|
|
282
|
+
<SplitPaneLayout className="h-[calc(100vh-280px)] min-h-[440px]">
|
|
283
|
+
<SplitPaneList>
|
|
284
|
+
<SectionCard
|
|
285
|
+
title="Redirect Rules"
|
|
313
286
|
action={
|
|
314
|
-
<
|
|
315
|
-
<
|
|
316
|
-
|
|
287
|
+
<div className="flex flex-wrap gap-2">
|
|
288
|
+
<input
|
|
289
|
+
ref={fileRef}
|
|
290
|
+
type="file"
|
|
291
|
+
accept=".csv,text/csv"
|
|
292
|
+
className="sr-only"
|
|
293
|
+
aria-hidden
|
|
294
|
+
tabIndex={-1}
|
|
295
|
+
onChange={(e) => {
|
|
296
|
+
const f = e.target.files?.[0]
|
|
297
|
+
if (f) handleImportFile(f)
|
|
298
|
+
e.target.value = ''
|
|
299
|
+
}}
|
|
300
|
+
/>
|
|
301
|
+
<button className={btnSecondary} onClick={() => fileRef.current?.click()}>
|
|
302
|
+
<Upload className="h-4 w-4" aria-hidden /> Import
|
|
303
|
+
</button>
|
|
304
|
+
<button className={btnSecondary} onClick={handleExport}>
|
|
305
|
+
<Download className="h-4 w-4" aria-hidden /> Export
|
|
306
|
+
</button>
|
|
307
|
+
<button className={btnPrimary} onClick={() => setEditor({ rule: null })}>
|
|
308
|
+
<Plus className="h-4 w-4" aria-hidden /> New redirect
|
|
309
|
+
</button>
|
|
310
|
+
</div>
|
|
317
311
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
<
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
<
|
|
330
|
-
<
|
|
331
|
-
</
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
onClick={() =>
|
|
359
|
-
aria-label={`Edit redirect ${r.fromPath}`}
|
|
360
|
-
>
|
|
361
|
-
<Pencil className="h-4 w-4" aria-hidden />
|
|
362
|
-
</button>
|
|
363
|
-
<button
|
|
364
|
-
className={btnGhostIcon}
|
|
365
|
-
onClick={() => setConfirmDelete(r)}
|
|
366
|
-
aria-label={`Delete redirect ${r.fromPath}`}
|
|
312
|
+
>
|
|
313
|
+
{loading ? (
|
|
314
|
+
<SeoLoading label="Loading redirects…" />
|
|
315
|
+
) : error ? (
|
|
316
|
+
<SeoErrorState message={error} onRetry={refetch} />
|
|
317
|
+
) : (data ?? []).length === 0 ? (
|
|
318
|
+
<SeoEmptyState
|
|
319
|
+
icon={<Link2 size={24} />}
|
|
320
|
+
title="No redirects yet"
|
|
321
|
+
description="Create a redirect or import a CSV to get started."
|
|
322
|
+
action={
|
|
323
|
+
<button className={btnPrimary} onClick={() => setEditor({ rule: null })}>
|
|
324
|
+
<Plus className="h-4 w-4" aria-hidden /> New redirect
|
|
325
|
+
</button>
|
|
326
|
+
}
|
|
327
|
+
/>
|
|
328
|
+
) : (
|
|
329
|
+
<div className="overflow-x-auto">
|
|
330
|
+
<table className="w-full text-sm" aria-label="Redirect rules">
|
|
331
|
+
<thead>
|
|
332
|
+
<tr className="text-muted-foreground border-border border-b text-left">
|
|
333
|
+
<th className="py-2 pr-4 font-medium">Source</th>
|
|
334
|
+
<th className="py-2 pr-4 font-medium">Destination</th>
|
|
335
|
+
<th className="py-2 pr-4 font-medium">Type</th>
|
|
336
|
+
<th className="py-2 pr-4 text-right font-medium">Hits</th>
|
|
337
|
+
<th className="py-2 pr-4 font-medium">Status</th>
|
|
338
|
+
<th className="py-2 text-right font-medium">
|
|
339
|
+
<span className="sr-only">Actions</span>
|
|
340
|
+
</th>
|
|
341
|
+
</tr>
|
|
342
|
+
</thead>
|
|
343
|
+
<tbody className="divide-border divide-y">
|
|
344
|
+
{data!.map((r) => {
|
|
345
|
+
const isSelected = editor?.rule?.id === r.id
|
|
346
|
+
return (
|
|
347
|
+
<tr
|
|
348
|
+
key={r.id}
|
|
349
|
+
className={`cursor-pointer transition-colors ${
|
|
350
|
+
isSelected ? 'bg-[var(--acc-l)]' : 'hover:bg-accent/40'
|
|
351
|
+
}`}
|
|
352
|
+
onClick={() => toggleRow(r)}
|
|
367
353
|
>
|
|
368
|
-
<
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
354
|
+
<td className="py-2.5 pr-4">
|
|
355
|
+
<button
|
|
356
|
+
type="button"
|
|
357
|
+
onClick={(e) => {
|
|
358
|
+
e.stopPropagation()
|
|
359
|
+
toggleRow(r)
|
|
360
|
+
}}
|
|
361
|
+
aria-expanded={isSelected}
|
|
362
|
+
className="text-foreground focus-visible:ring-ring rounded text-left font-mono hover:underline focus-visible:ring-2 focus-visible:outline-none"
|
|
363
|
+
>
|
|
364
|
+
{r.fromPath}
|
|
365
|
+
</button>
|
|
366
|
+
</td>
|
|
367
|
+
<td className="text-muted-foreground py-2.5 pr-4 font-mono">
|
|
368
|
+
{r.type === 410 ? <span className="italic">— (gone)</span> : r.toPath}
|
|
369
|
+
</td>
|
|
370
|
+
<td className="text-foreground py-2.5 pr-4 tabular-nums">{r.type}</td>
|
|
371
|
+
<td className="text-foreground py-2.5 pr-4 text-right tabular-nums">
|
|
372
|
+
{r.hits}
|
|
373
|
+
</td>
|
|
374
|
+
<td className="py-2.5 pr-4">
|
|
375
|
+
<span className="flex flex-wrap gap-1.5">
|
|
376
|
+
<SeoStatusBadge
|
|
377
|
+
label={r.enabled ? 'Enabled' : 'Disabled'}
|
|
378
|
+
tone={r.enabled ? 'good' : 'neutral'}
|
|
379
|
+
/>
|
|
380
|
+
{r.stale && <SeoStatusBadge label="Stale" tone="fair" />}
|
|
381
|
+
</span>
|
|
382
|
+
</td>
|
|
383
|
+
<td className="py-2.5 text-right">
|
|
384
|
+
<div className="flex justify-end gap-1">
|
|
385
|
+
<button
|
|
386
|
+
className={btnGhostIcon}
|
|
387
|
+
onClick={(e) => {
|
|
388
|
+
e.stopPropagation()
|
|
389
|
+
setConfirmDelete(r)
|
|
390
|
+
}}
|
|
391
|
+
aria-label={`Delete redirect ${r.fromPath}`}
|
|
392
|
+
>
|
|
393
|
+
<Trash2 className="h-4 w-4" aria-hidden />
|
|
394
|
+
</button>
|
|
395
|
+
</div>
|
|
396
|
+
</td>
|
|
397
|
+
</tr>
|
|
398
|
+
)
|
|
399
|
+
})}
|
|
400
|
+
</tbody>
|
|
401
|
+
</table>
|
|
402
|
+
</div>
|
|
403
|
+
)}
|
|
404
|
+
</SectionCard>
|
|
405
|
+
</SplitPaneList>
|
|
379
406
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
407
|
+
<InspectorPane open={editor !== null} width={420} aria-label="Redirect editor">
|
|
408
|
+
{editor && (
|
|
409
|
+
<RedirectEditorPane
|
|
410
|
+
key={editor.rule?.id ?? 'new'}
|
|
411
|
+
rule={editor.rule}
|
|
412
|
+
onClose={() => setEditor(null)}
|
|
413
|
+
onSaved={() => {
|
|
414
|
+
setEditor(null)
|
|
415
|
+
refetchAll()
|
|
416
|
+
}}
|
|
417
|
+
onDelete={(rule) => setConfirmDelete(rule)}
|
|
418
|
+
/>
|
|
419
|
+
)}
|
|
420
|
+
</InspectorPane>
|
|
421
|
+
</SplitPaneLayout>
|
|
386
422
|
|
|
387
423
|
<ConfirmDialog
|
|
388
424
|
open={confirmDelete !== null}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export interface EntryDetailDrawerProps {
|
|
2
|
-
entryId: string | null;
|
|
3
|
-
open: boolean;
|
|
4
|
-
onOpenChange: (open: boolean) => void;
|
|
5
|
-
/** Called after a mutation (star/archive/read/delete) so the list refreshes. */
|
|
6
|
-
onChanged?: () => void;
|
|
7
|
-
/** Called specifically after a delete so the caller can drop the row/selection. */
|
|
8
|
-
onDeleted?: (id: string) => void;
|
|
9
|
-
}
|
|
10
|
-
export declare function EntryDetailDrawer({ entryId, open, onOpenChange, onChanged, onDeleted, }: EntryDetailDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
-
//# sourceMappingURL=EntryDetailDrawer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EntryDetailDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/forms/EntryDetailDrawer.tsx"],"names":[],"mappings":"AA6DA,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,mFAAmF;IACnF,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,SAAS,GACV,EAAE,sBAAsB,2CAyMxB"}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
/**
|
|
4
|
-
* Entry detail drawer — shows a single submission rendered against the schema
|
|
5
|
-
* version it was captured with, plus sender, attribution, and per-entry
|
|
6
|
-
* actions (star, archive, mark unread, delete). Opening an unread entry marks
|
|
7
|
-
* it read. Token-only styling; built on the shared `Drawer`.
|
|
8
|
-
*/
|
|
9
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
10
|
-
import { Archive, ArchiveRestore, Loader2, Mail, Star, Trash2 } from 'lucide-react';
|
|
11
|
-
import { toast } from 'sonner';
|
|
12
|
-
import { deleteEntry, fetchEntry, updateEntry, } from '../../lib/forms-service.js';
|
|
13
|
-
import { emitFormsChanged } from '../../lib/forms-events.js';
|
|
14
|
-
import { ConfirmDialog } from '../ui/ConfirmDialog.js';
|
|
15
|
-
import { Drawer } from './Drawer.js';
|
|
16
|
-
import { FormsErrorState, btnGhostIcon } from './primitives.js';
|
|
17
|
-
function formatDateTime(iso) {
|
|
18
|
-
if (!iso)
|
|
19
|
-
return '—';
|
|
20
|
-
const d = new Date(iso);
|
|
21
|
-
if (Number.isNaN(d.getTime()))
|
|
22
|
-
return '—';
|
|
23
|
-
return d.toLocaleString(undefined, {
|
|
24
|
-
month: 'short',
|
|
25
|
-
day: 'numeric',
|
|
26
|
-
year: 'numeric',
|
|
27
|
-
hour: 'numeric',
|
|
28
|
-
minute: '2-digit',
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
/** Render a stored value for display, coping with arrays/objects/booleans. */
|
|
32
|
-
function displayValue(value) {
|
|
33
|
-
if (value == null || value === '')
|
|
34
|
-
return '—';
|
|
35
|
-
if (typeof value === 'boolean')
|
|
36
|
-
return value ? 'Yes' : 'No';
|
|
37
|
-
if (Array.isArray(value))
|
|
38
|
-
return value.map((v) => String(v)).join(', ');
|
|
39
|
-
if (typeof value === 'object')
|
|
40
|
-
return JSON.stringify(value);
|
|
41
|
-
return String(value);
|
|
42
|
-
}
|
|
43
|
-
function attributionRows(entry) {
|
|
44
|
-
return [
|
|
45
|
-
{ label: 'Source', value: entry.utmSource },
|
|
46
|
-
{ label: 'Medium', value: entry.utmMedium },
|
|
47
|
-
{ label: 'Campaign', value: entry.utmCampaign },
|
|
48
|
-
{ label: 'Page', value: entry.pageUrl },
|
|
49
|
-
{ label: 'Referrer', value: entry.referrer },
|
|
50
|
-
].filter((r) => r.value);
|
|
51
|
-
}
|
|
52
|
-
export function EntryDetailDrawer({ entryId, open, onOpenChange, onChanged, onDeleted, }) {
|
|
53
|
-
const [detail, setDetail] = useState(null);
|
|
54
|
-
const [loading, setLoading] = useState(false);
|
|
55
|
-
const [error, setError] = useState(null);
|
|
56
|
-
const [busy, setBusy] = useState(false);
|
|
57
|
-
const [confirmDelete, setConfirmDelete] = useState(false);
|
|
58
|
-
const notifyChanged = useCallback(() => {
|
|
59
|
-
emitFormsChanged();
|
|
60
|
-
onChanged?.();
|
|
61
|
-
}, [onChanged]);
|
|
62
|
-
// Load the entry (and its schema) when the drawer opens, marking it read.
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
if (!open || !entryId)
|
|
65
|
-
return;
|
|
66
|
-
let cancelled = false;
|
|
67
|
-
setLoading(true);
|
|
68
|
-
setError(null);
|
|
69
|
-
setDetail(null);
|
|
70
|
-
void (async () => {
|
|
71
|
-
try {
|
|
72
|
-
const data = await fetchEntry(entryId);
|
|
73
|
-
if (cancelled)
|
|
74
|
-
return;
|
|
75
|
-
if (!data) {
|
|
76
|
-
setError('Entry not found');
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
setDetail(data);
|
|
80
|
-
// Mark read on open (fire-and-forget; refresh the list counts).
|
|
81
|
-
if (data.entry.unread) {
|
|
82
|
-
const res = await updateEntry(entryId, { unread: false });
|
|
83
|
-
if (!cancelled && !res.error)
|
|
84
|
-
notifyChanged();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
if (!cancelled)
|
|
89
|
-
setError(e instanceof Error ? e.message : 'Failed to load entry');
|
|
90
|
-
}
|
|
91
|
-
finally {
|
|
92
|
-
if (!cancelled)
|
|
93
|
-
setLoading(false);
|
|
94
|
-
}
|
|
95
|
-
})();
|
|
96
|
-
return () => {
|
|
97
|
-
cancelled = true;
|
|
98
|
-
};
|
|
99
|
-
}, [open, entryId, notifyChanged]);
|
|
100
|
-
const mutate = useCallback(async (patch) => {
|
|
101
|
-
if (!entryId)
|
|
102
|
-
return;
|
|
103
|
-
setBusy(true);
|
|
104
|
-
const res = await updateEntry(entryId, patch);
|
|
105
|
-
setBusy(false);
|
|
106
|
-
if (res.error) {
|
|
107
|
-
toast.error(res.error);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
setDetail((cur) => (cur ? { ...cur, entry: { ...cur.entry, ...patch } } : cur));
|
|
111
|
-
notifyChanged();
|
|
112
|
-
}, [entryId, notifyChanged]);
|
|
113
|
-
const handleDelete = useCallback(async () => {
|
|
114
|
-
if (!entryId)
|
|
115
|
-
return;
|
|
116
|
-
setBusy(true);
|
|
117
|
-
const res = await deleteEntry(entryId);
|
|
118
|
-
setBusy(false);
|
|
119
|
-
if (res.error) {
|
|
120
|
-
toast.error(res.error);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
toast.success('Submission deleted');
|
|
124
|
-
notifyChanged();
|
|
125
|
-
onDeleted?.(entryId);
|
|
126
|
-
onOpenChange(false);
|
|
127
|
-
}, [entryId, notifyChanged, onDeleted, onOpenChange]);
|
|
128
|
-
const entry = detail?.entry;
|
|
129
|
-
const senderName = entry?.senderName || 'Anonymous';
|
|
130
|
-
const attrRows = entry ? attributionRows(entry) : [];
|
|
131
|
-
return (_jsxs(_Fragment, { children: [_jsx(Drawer, { open: open, onOpenChange: onOpenChange, title: entry ? `Entry${entry.entryNumber ? ` #${entry.entryNumber}` : ''}` : 'Entry', description: entry ? formatDateTime(entry.submittedAt) : undefined, headerExtra: entry ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: btnGhostIcon, "aria-pressed": entry.starred, "aria-label": entry.starred ? 'Unstar entry' : 'Star entry', disabled: busy, onClick: () => void mutate({ starred: !entry.starred }), children: _jsx(Star, { size: 18, "aria-hidden": true, className: entry.starred ? 'fill-warning text-warning' : '' }) }), _jsx("button", { type: "button", className: btnGhostIcon, "aria-pressed": entry.archived, "aria-label": entry.archived ? 'Unarchive entry' : 'Archive entry', disabled: busy, onClick: () => void mutate({ archived: !entry.archived }), children: entry.archived ? (_jsx(ArchiveRestore, { size: 18, "aria-hidden": true })) : (_jsx(Archive, { size: 18, "aria-hidden": true })) })] })) : undefined, footer: entry ? (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: "text-destructive hover:bg-destructive/10 focus-visible:ring-ring inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-60", disabled: busy, onClick: () => setConfirmDelete(true), children: [_jsx(Trash2, { size: 16, "aria-hidden": true }), "Delete"] }), _jsxs("button", { type: "button", className: "border-border text-foreground hover:bg-accent focus-visible:ring-ring inline-flex items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-60", disabled: busy, onClick: () => void mutate({ unread: !entry.unread }), children: [_jsx(Mail, { size: 16, "aria-hidden": true }), "Mark ", entry.unread ? 'read' : 'unread'] })] })) : undefined, children: loading ? (_jsxs("div", { className: "flex h-40 items-center justify-center", role: "status", "aria-live": "polite", children: [_jsx(Loader2, { className: "text-muted-foreground h-5 w-5 animate-spin", "aria-hidden": true }), _jsx("span", { className: "sr-only", children: "Loading entry\u2026" })] })) : error ? (_jsx(FormsErrorState, { message: error })) : entry ? (_jsxs("div", { className: "space-y-6", children: [_jsxs("section", { className: "space-y-1", children: [_jsx("h3", { className: "text-foreground text-sm font-medium", children: senderName }), entry.senderEmail && (_jsx("a", { href: `mailto:${entry.senderEmail}`, className: "text-primary block text-sm hover:underline", children: entry.senderEmail })), entry.senderPhone && (_jsx("span", { className: "text-muted-foreground block text-sm", children: entry.senderPhone }))] }), _jsx(FieldValues, { entry: entry, fields: detail.fields }), attrRows.length > 0 && (_jsxs("section", { children: [_jsx("h4", { className: "text-muted-foreground mb-2 text-xs font-medium tracking-wide uppercase", children: "Attribution" }), _jsx("dl", { className: "divide-border divide-y text-sm", children: attrRows.map((row) => (_jsxs("div", { className: "flex justify-between gap-4 py-1.5", children: [_jsx("dt", { className: "text-muted-foreground shrink-0", children: row.label }), _jsx("dd", { className: "text-foreground min-w-0 truncate text-right", children: row.value })] }, row.label))) })] }))] })) : null }), _jsx(ConfirmDialog, { open: confirmDelete, onClose: () => setConfirmDelete(false), onConfirm: () => void handleDelete(), title: "Delete submission?", description: "This permanently removes the submission and any attached files. This cannot be undone.", confirmLabel: "Delete", destructive: true })] }));
|
|
132
|
-
}
|
|
133
|
-
/** Render the captured field values against the entry's schema. */
|
|
134
|
-
function FieldValues({ entry, fields }) {
|
|
135
|
-
// Prefer the schema order; fall back to whatever keys exist on the data.
|
|
136
|
-
const inputFields = fields.filter((f) => f.type !== 'divider' && f.type !== 'richtext' && f.type !== 'honeypot');
|
|
137
|
-
const rows = inputFields.length
|
|
138
|
-
? inputFields.map((f) => ({ key: f.key, label: f.label }))
|
|
139
|
-
: Object.keys(entry.data ?? {}).map((k) => ({ key: k, label: k }));
|
|
140
|
-
if (rows.length === 0) {
|
|
141
|
-
return _jsx("p", { className: "text-muted-foreground text-sm", children: "No field data captured." });
|
|
142
|
-
}
|
|
143
|
-
return (_jsxs("section", { children: [_jsx("h4", { className: "text-muted-foreground mb-2 text-xs font-medium tracking-wide uppercase", children: "Submission" }), _jsx("dl", { className: "space-y-3", children: rows.map((row) => (_jsxs("div", { children: [_jsx("dt", { className: "text-muted-foreground text-xs", children: row.label }), _jsx("dd", { className: "text-foreground mt-0.5 text-sm wrap-break-word whitespace-pre-wrap", children: displayValue(entry.data?.[row.key]) })] }, row.key))) })] }));
|
|
144
|
-
}
|
|
145
|
-
//# sourceMappingURL=EntryDetailDrawer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"EntryDetailDrawer.js","sourceRoot":"","sources":["../../../src/components/forms/EntryDetailDrawer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type RedirectRule } from '../../lib/seo-service.js';
|
|
2
|
-
export declare function RedirectEditorDrawer({ open, onOpenChange, rule, onSaved, }: {
|
|
3
|
-
open: boolean;
|
|
4
|
-
onOpenChange: (open: boolean) => void;
|
|
5
|
-
/** null = create mode */
|
|
6
|
-
rule: RedirectRule | null;
|
|
7
|
-
onSaved?: () => void;
|
|
8
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
//# sourceMappingURL=RedirectEditorDrawer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RedirectEditorDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/seo/RedirectEditorDrawer.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,YAAY,EAGlB,MAAM,0BAA0B,CAAA;AAuBjC,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,OAAO,GACR,EAAE;IACD,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,yBAAyB;IACzB,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACzB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB,2CA0LA"}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
import { toast } from 'sonner';
|
|
5
|
-
import { createRedirectRule, updateRedirectRule, } from '../../lib/seo-service.js';
|
|
6
|
-
import { Drawer } from './Drawer.js';
|
|
7
|
-
import { btnPrimary, btnSecondary } from './primitives.js';
|
|
8
|
-
import { Toggle } from '../ui/Toggle.js';
|
|
9
|
-
const STATUS_CODES = [
|
|
10
|
-
{ value: 301, label: '301 — Permanent' },
|
|
11
|
-
{ value: 302, label: '302 — Temporary' },
|
|
12
|
-
{ value: 307, label: '307 — Temporary (preserve method)' },
|
|
13
|
-
{ value: 308, label: '308 — Permanent (preserve method)' },
|
|
14
|
-
{ value: 410, label: '410 — Gone (no destination)' },
|
|
15
|
-
];
|
|
16
|
-
const inputCls = 'w-full rounded-md border border-border bg-input-background px-3 py-2 text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring';
|
|
17
|
-
function normalizePath(p) {
|
|
18
|
-
const t = p.trim();
|
|
19
|
-
if (!t)
|
|
20
|
-
return '';
|
|
21
|
-
if (/^https?:\/\//i.test(t))
|
|
22
|
-
return t;
|
|
23
|
-
return t.startsWith('/') ? t : `/${t}`;
|
|
24
|
-
}
|
|
25
|
-
export function RedirectEditorDrawer({ open, onOpenChange, rule, onSaved, }) {
|
|
26
|
-
const [form, setForm] = useState({
|
|
27
|
-
type: 301,
|
|
28
|
-
fromPath: '',
|
|
29
|
-
toPath: '',
|
|
30
|
-
enabled: true,
|
|
31
|
-
preserveQuery: false,
|
|
32
|
-
notes: '',
|
|
33
|
-
});
|
|
34
|
-
const [saving, setSaving] = useState(false);
|
|
35
|
-
const [clientError, setClientError] = useState(null);
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (!open)
|
|
38
|
-
return;
|
|
39
|
-
if (rule) {
|
|
40
|
-
setForm({
|
|
41
|
-
type: rule.type,
|
|
42
|
-
fromPath: rule.fromPath,
|
|
43
|
-
toPath: rule.toPath,
|
|
44
|
-
enabled: rule.enabled,
|
|
45
|
-
preserveQuery: rule.preserveQuery,
|
|
46
|
-
notes: rule.notes ?? '',
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
setForm({
|
|
51
|
-
type: 301,
|
|
52
|
-
fromPath: '',
|
|
53
|
-
toPath: '',
|
|
54
|
-
enabled: true,
|
|
55
|
-
preserveQuery: false,
|
|
56
|
-
notes: '',
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
setClientError(null);
|
|
60
|
-
}, [open, rule]);
|
|
61
|
-
const isGone = form.type === 410;
|
|
62
|
-
const update = (patch) => setForm((f) => ({ ...f, ...patch }));
|
|
63
|
-
async function handleSave() {
|
|
64
|
-
const fromPath = normalizePath(form.fromPath);
|
|
65
|
-
const toPath = isGone ? '' : normalizePath(form.toPath);
|
|
66
|
-
if (!fromPath) {
|
|
67
|
-
setClientError('Source path is required.');
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (!isGone && !toPath) {
|
|
71
|
-
setClientError('Destination is required for redirect status codes.');
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if (!isGone && fromPath === toPath) {
|
|
75
|
-
setClientError('Source and destination cannot be identical (would create a loop).');
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
setClientError(null);
|
|
79
|
-
setSaving(true);
|
|
80
|
-
const payload = { ...form, fromPath, toPath };
|
|
81
|
-
const res = rule
|
|
82
|
-
? await updateRedirectRule(rule.id, payload)
|
|
83
|
-
: await createRedirectRule(payload);
|
|
84
|
-
setSaving(false);
|
|
85
|
-
if (res.error) {
|
|
86
|
-
toast.error(res.error);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
toast.success(rule ? 'Redirect updated.' : 'Redirect created.');
|
|
90
|
-
onOpenChange(false);
|
|
91
|
-
onSaved?.();
|
|
92
|
-
}
|
|
93
|
-
return (_jsx(Drawer, { open: open, onOpenChange: onOpenChange, title: rule ? 'Edit redirect' : 'New redirect', width: "max-w-lg", footer: _jsxs(_Fragment, { children: [_jsx("button", { className: btnSecondary, onClick: () => onOpenChange(false), disabled: saving, children: "Cancel" }), _jsx("button", { className: btnPrimary, onClick: handleSave, disabled: saving, children: saving ? 'Saving…' : rule ? 'Save changes' : 'Create redirect' })] }), children: _jsxs("div", { className: "space-y-4", children: [clientError && (_jsx("p", { className: "text-destructive bg-destructive/10 rounded-md px-3 py-2 text-sm", role: "alert", children: clientError })), _jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-foreground text-sm font-medium", htmlFor: "redir-type", children: "Status code" }), _jsx("select", { id: "redir-type", className: inputCls, value: form.type, onChange: (e) => update({ type: Number(e.target.value) }), children: STATUS_CODES.map((s) => (_jsx("option", { value: s.value, children: s.label }, s.value))) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-foreground text-sm font-medium", htmlFor: "redir-from", children: "Source path" }), _jsx("input", { id: "redir-from", className: inputCls, value: form.fromPath, onChange: (e) => update({ fromPath: e.target.value }), placeholder: "/old-path" })] }), !isGone && (_jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "text-foreground text-sm font-medium", htmlFor: "redir-to", children: "Destination" }), _jsx("input", { id: "redir-to", className: inputCls, value: form.toPath, onChange: (e) => update({ toPath: e.target.value }), placeholder: "/new-path or https://example.com/page" })] })), _jsxs("div", { className: "space-y-1.5", children: [_jsxs("label", { className: "text-foreground text-sm font-medium", htmlFor: "redir-notes", children: ["Notes ", _jsx("span", { className: "text-muted-foreground font-normal", children: "(optional)" })] }), _jsx("input", { id: "redir-notes", className: inputCls, value: form.notes ?? '', onChange: (e) => update({ notes: e.target.value }), placeholder: "Why this redirect exists" })] }), _jsxs("div", { className: "border-border space-y-3 rounded-lg border p-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("span", { className: "text-foreground text-sm font-medium", children: "Enabled" }), _jsx(Toggle, { checked: !!form.enabled, onCheckedChange: (v) => update({ enabled: v }), variant: "enabled", "aria-label": "Enabled" })] }), !isGone && (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "text-foreground block text-sm font-medium", children: "Preserve query string" }), _jsx("span", { className: "text-muted-foreground block text-sm", children: "Append the original query params to the destination." })] }), _jsx(Toggle, { checked: !!form.preserveQuery, onCheckedChange: (v) => update({ preserveQuery: v }), "aria-label": "Preserve query string" })] }))] })] }) }));
|
|
94
|
-
}
|
|
95
|
-
//# sourceMappingURL=RedirectEditorDrawer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RedirectEditorDrawer.js","sourceRoot":"","sources":["../../../src/components/seo/RedirectEditorDrawer.tsx"],"names":[],"mappings":"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"}
|