@actuate-media/cms-admin 0.68.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 +18 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +31 -11
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/hooks/useIdleLogout.test.js +57 -2
- package/dist/__tests__/hooks/useIdleLogout.test.js.map +1 -1
- package/dist/__tests__/router/query-string.test.d.ts +2 -0
- package/dist/__tests__/router/query-string.test.d.ts.map +1 -0
- package/dist/__tests__/router/query-string.test.js +44 -0
- package/dist/__tests__/router/query-string.test.js.map +1 -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/__tests__/views/users-selected-deeplink.render.test.d.ts +2 -0
- package/dist/__tests__/views/users-selected-deeplink.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/users-selected-deeplink.render.test.js +91 -0
- package/dist/__tests__/views/users-selected-deeplink.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/hooks/useIdleLogout.d.ts +21 -6
- package/dist/hooks/useIdleLogout.d.ts.map +1 -1
- package/dist/hooks/useIdleLogout.js +41 -8
- package/dist/hooks/useIdleLogout.js.map +1 -1
- package/dist/layout/Header.js +1 -1
- package/dist/layout/Header.js.map +1 -1
- package/dist/router/index.d.ts +7 -0
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +23 -4
- package/dist/router/index.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 +4 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +28 -6
- 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/SEO.d.ts.map +1 -1
- package/dist/views/SEO.js +2 -1
- package/dist/views/SEO.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/Settings.d.ts +4 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +4 -3
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/Users.d.ts +4 -1
- package/dist/views/Users.d.ts.map +1 -1
- package/dist/views/Users.js +22 -2
- package/dist/views/Users.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/dist/views/settings/SecurityTab.d.ts.map +1 -1
- package/dist/views/settings/SecurityTab.js +2 -1
- package/dist/views/settings/SecurityTab.js.map +1 -1
- package/package.json +2 -2
- package/src/AdminRoot.tsx +53 -12
- package/src/__tests__/hooks/useIdleLogout.test.tsx +103 -1
- package/src/__tests__/router/query-string.test.ts +54 -0
- 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/__tests__/views/users-selected-deeplink.render.test.tsx +102 -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/hooks/useIdleLogout.ts +65 -13
- package/src/layout/Header.tsx +2 -2
- package/src/router/index.ts +25 -4
- 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 +34 -6
- 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/SEO.tsx +2 -1
- package/src/views/ScriptTags.tsx +26 -36
- package/src/views/Settings.tsx +7 -2
- package/src/views/Users.tsx +23 -2
- package/src/views/seo/ContentTab.tsx +10 -10
- package/src/views/seo/RedirectsTab.tsx +145 -109
- package/src/views/settings/SecurityTab.tsx +2 -1
- 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
|
@@ -32,7 +32,7 @@ const FILTERS: { id: ContentSeoFilter; label: string }[] = [
|
|
|
32
32
|
]
|
|
33
33
|
|
|
34
34
|
const inputCls =
|
|
35
|
-
'w-full rounded-md border border-
|
|
35
|
+
'w-full rounded-md border border-[var(--bdr)] bg-input-background py-2 pl-9 pr-3 text-base text-[var(--txt)] placeholder:text-[var(--sub)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]'
|
|
36
36
|
|
|
37
37
|
type Selection = { type: string; id: string } | null
|
|
38
38
|
|
|
@@ -64,7 +64,7 @@ export function ContentTab({
|
|
|
64
64
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
65
65
|
<div className="relative max-w-sm flex-1">
|
|
66
66
|
<Search
|
|
67
|
-
className="
|
|
67
|
+
className="pointer-events-none absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--sub)]"
|
|
68
68
|
aria-hidden
|
|
69
69
|
/>
|
|
70
70
|
<input
|
|
@@ -82,10 +82,10 @@ export function ContentTab({
|
|
|
82
82
|
type="button"
|
|
83
83
|
aria-pressed={filter === f.id}
|
|
84
84
|
onClick={() => setFilter(f.id)}
|
|
85
|
-
className={`
|
|
85
|
+
className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none ${
|
|
86
86
|
filter === f.id
|
|
87
87
|
? 'bg-primary text-primary-foreground'
|
|
88
|
-
: 'text-
|
|
88
|
+
: 'text-[var(--sub)] hover:bg-[var(--acc-l)] hover:text-[var(--txt)]'
|
|
89
89
|
}`}
|
|
90
90
|
>
|
|
91
91
|
{f.label}
|
|
@@ -118,14 +118,14 @@ export function ContentTab({
|
|
|
118
118
|
<div className="overflow-x-auto">
|
|
119
119
|
<table className="w-full text-sm" aria-label="Content SEO">
|
|
120
120
|
<thead>
|
|
121
|
-
<tr className="
|
|
121
|
+
<tr className="border-b border-[var(--bdr)] text-left text-[var(--sub)]">
|
|
122
122
|
<th className="py-2 pr-4 font-medium">Title</th>
|
|
123
123
|
<th className="py-2 pr-4 font-medium">Type</th>
|
|
124
124
|
<th className="py-2 pr-4 font-medium">Meta</th>
|
|
125
125
|
<th className="py-2 text-right font-medium">Score</th>
|
|
126
126
|
</tr>
|
|
127
127
|
</thead>
|
|
128
|
-
<tbody className="divide-
|
|
128
|
+
<tbody className="divide-y divide-[var(--bdr)]">
|
|
129
129
|
{rows.map((r) => {
|
|
130
130
|
const isSelected =
|
|
131
131
|
selection?.type === r.entityType && selection.id === r.entityId
|
|
@@ -133,7 +133,7 @@ export function ContentTab({
|
|
|
133
133
|
<tr
|
|
134
134
|
key={`${r.entityType}-${r.entityId}`}
|
|
135
135
|
className={`cursor-pointer transition-colors ${
|
|
136
|
-
isSelected ? 'bg-[var(--acc-l)]' : 'hover:bg-
|
|
136
|
+
isSelected ? 'bg-[var(--acc-l)]' : 'hover:bg-[var(--acc-l)]'
|
|
137
137
|
}`}
|
|
138
138
|
onClick={() => toggleRow(r.entityType, r.entityId)}
|
|
139
139
|
>
|
|
@@ -147,12 +147,12 @@ export function ContentTab({
|
|
|
147
147
|
aria-expanded={isSelected}
|
|
148
148
|
className="text-left"
|
|
149
149
|
>
|
|
150
|
-
<span className="
|
|
151
|
-
<span className="
|
|
150
|
+
<span className="block font-medium text-[var(--txt)]">{r.title}</span>
|
|
151
|
+
<span className="block truncate text-[var(--sub)]">{r.url}</span>
|
|
152
152
|
</button>
|
|
153
153
|
</td>
|
|
154
154
|
<td className="py-2.5 pr-4 capitalize">
|
|
155
|
-
<span className="text-
|
|
155
|
+
<span className="text-[var(--sub)]">{r.entityType}</span>
|
|
156
156
|
</td>
|
|
157
157
|
<td className="py-2.5 pr-4">
|
|
158
158
|
<span className="flex flex-wrap gap-1.5">
|
|
@@ -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}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect } from 'react'
|
|
4
4
|
import { Loader2, ShieldAlert } from 'lucide-react'
|
|
5
|
+
import { parseRouteQuery } from '../../router/index.js'
|
|
5
6
|
import {
|
|
6
7
|
ConfirmDangerousSettingDialog,
|
|
7
8
|
SettingsSaveBar,
|
|
@@ -56,7 +57,7 @@ export function SecurityTab({
|
|
|
56
57
|
// Deep-link support: /settings?tab=security§ion=sessions|ip-allowlist|audit
|
|
57
58
|
useEffect(() => {
|
|
58
59
|
if (loading || typeof window === 'undefined') return
|
|
59
|
-
const section =
|
|
60
|
+
const section = parseRouteQuery(window.location.search).get('section')
|
|
60
61
|
if (!section) return
|
|
61
62
|
const el = document.getElementById(`security-${section}`)
|
|
62
63
|
el?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
@@ -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"}
|