@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
|
@@ -412,8 +412,8 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
412
412
|
the design's `.page-hdr` / `.hdr-r` layout. */}
|
|
413
413
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
414
414
|
<div className="flex flex-col gap-1">
|
|
415
|
-
<h1 className="text-
|
|
416
|
-
<p className="text-
|
|
415
|
+
<h1 className="text-[var(--txt)]">Pages</h1>
|
|
416
|
+
<p className="text-sm text-[var(--sub)]">
|
|
417
417
|
{treeMode
|
|
418
418
|
? `${grandTotal} page${grandTotal === 1 ? '' : 's'} total`
|
|
419
419
|
: `${grandTotal} page${grandTotal === 1 ? '' : 's'} · organized by tags`}
|
|
@@ -422,7 +422,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
422
422
|
<button
|
|
423
423
|
type="button"
|
|
424
424
|
onClick={() => onNavigate('/pages/new')}
|
|
425
|
-
className="bg-primary text-primary-foreground hover:bg-primary/90
|
|
425
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-sm font-medium shadow-sm transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]"
|
|
426
426
|
>
|
|
427
427
|
<Plus className="h-3.5 w-3.5" aria-hidden />
|
|
428
428
|
New page
|
|
@@ -433,7 +433,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
433
433
|
<div className="flex flex-wrap items-center gap-2">
|
|
434
434
|
<div className="relative max-w-sm flex-1">
|
|
435
435
|
<Search
|
|
436
|
-
className="
|
|
436
|
+
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--sub)]"
|
|
437
437
|
aria-hidden
|
|
438
438
|
/>
|
|
439
439
|
<input
|
|
@@ -442,7 +442,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
442
442
|
onChange={(e) => setSearch(e.target.value)}
|
|
443
443
|
aria-label="Search pages"
|
|
444
444
|
placeholder="Search pages…"
|
|
445
|
-
className="
|
|
445
|
+
className="focus:border-primary focus:ring-primary/30 w-full rounded-lg border border-[var(--bdr)] bg-[var(--card)] py-2 pr-3 pl-9 text-sm text-[var(--txt)] shadow-sm placeholder:text-[var(--sub)] focus:ring-2 focus:outline-none"
|
|
446
446
|
/>
|
|
447
447
|
</div>
|
|
448
448
|
|
|
@@ -479,7 +479,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
479
479
|
|
|
480
480
|
<ViewModeToggle mode={viewMode} onChange={changeViewMode} />
|
|
481
481
|
|
|
482
|
-
<span className="
|
|
482
|
+
<span className="ml-auto text-sm text-[var(--sub)] tabular-nums">
|
|
483
483
|
{treeMode
|
|
484
484
|
? treeFiltersActive
|
|
485
485
|
? `${treeRows.length} result${treeRows.length === 1 ? '' : 's'}`
|
|
@@ -492,7 +492,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
492
492
|
with the page hierarchy) */}
|
|
493
493
|
{!treeMode && (
|
|
494
494
|
<div className="flex flex-wrap items-center gap-2">
|
|
495
|
-
<span className="text-
|
|
495
|
+
<span className="text-[10px] font-medium tracking-wider text-[var(--sub)] uppercase">
|
|
496
496
|
Tags
|
|
497
497
|
</span>
|
|
498
498
|
{tags.map((t) => (
|
|
@@ -509,7 +509,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
509
509
|
<button
|
|
510
510
|
type="button"
|
|
511
511
|
onClick={() => setSelectedTags(new Set())}
|
|
512
|
-
className="
|
|
512
|
+
className="inline-flex items-center gap-1 text-xs text-[var(--sub)] hover:text-[var(--txt)]"
|
|
513
513
|
>
|
|
514
514
|
<X className="h-3 w-3" aria-hidden />
|
|
515
515
|
Clear tags
|
|
@@ -543,7 +543,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
543
543
|
)}
|
|
544
544
|
|
|
545
545
|
{/* Table card */}
|
|
546
|
-
<div className="
|
|
546
|
+
<div className="overflow-hidden rounded-xl border border-[var(--bdr)] bg-[var(--card)] shadow-sm">
|
|
547
547
|
{treeMode ? (
|
|
548
548
|
<PagesTreeTable
|
|
549
549
|
rows={treeRows}
|
|
@@ -579,15 +579,15 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
579
579
|
<col style={{ width: '80px' }} />
|
|
580
580
|
<col style={{ width: '110px' }} />
|
|
581
581
|
</colgroup>
|
|
582
|
-
<thead className="
|
|
583
|
-
<tr className="text-
|
|
582
|
+
<thead className="bg-muted/40 border-b border-[var(--bdr)]">
|
|
583
|
+
<tr className="text-xs text-[var(--sub)]">
|
|
584
584
|
<th scope="col" className="px-3 py-2.5 text-left">
|
|
585
585
|
<input
|
|
586
586
|
type="checkbox"
|
|
587
587
|
aria-label="Select all pages on this page"
|
|
588
588
|
checked={allOnPageSelected}
|
|
589
589
|
onChange={(e) => toggleAllOnPage(e.target.checked)}
|
|
590
|
-
className="
|
|
590
|
+
className="accent-primary rounded border-[var(--bdr)]"
|
|
591
591
|
/>
|
|
592
592
|
</th>
|
|
593
593
|
<th
|
|
@@ -668,7 +668,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
668
668
|
{!loading && error && (
|
|
669
669
|
<tr>
|
|
670
670
|
<td colSpan={8} className="px-3 py-12 text-center">
|
|
671
|
-
<div className="
|
|
671
|
+
<div className="mb-2 text-sm font-medium text-[var(--txt)]">{error}</div>
|
|
672
672
|
<button
|
|
673
673
|
onClick={() => setRefreshToken((t) => t + 1)}
|
|
674
674
|
className="text-primary text-sm hover:underline"
|
|
@@ -779,8 +779,8 @@ function SortHeader({
|
|
|
779
779
|
<button
|
|
780
780
|
type="button"
|
|
781
781
|
onClick={() => onSort(field)}
|
|
782
|
-
className={`
|
|
783
|
-
active ? 'text-
|
|
782
|
+
className={`inline-flex items-center gap-1 text-xs font-medium tracking-wider uppercase transition-colors hover:text-[var(--txt)] ${
|
|
783
|
+
active ? 'text-[var(--txt)]' : 'text-[var(--sub)]'
|
|
784
784
|
}`}
|
|
785
785
|
>
|
|
786
786
|
<span>{label}</span>
|
|
@@ -849,12 +849,12 @@ function FilterSelect({
|
|
|
849
849
|
options: { value: string; label: string }[]
|
|
850
850
|
}) {
|
|
851
851
|
return (
|
|
852
|
-
<label className="
|
|
853
|
-
<span className="text-
|
|
852
|
+
<label className="inline-flex items-center gap-1.5 text-sm text-[var(--txt)]">
|
|
853
|
+
<span className="text-[var(--sub)]">{label}:</span>
|
|
854
854
|
<select
|
|
855
855
|
value={value}
|
|
856
856
|
onChange={(e) => onChange(e.target.value)}
|
|
857
|
-
className="
|
|
857
|
+
className="focus:border-primary focus:ring-primary/30 rounded-lg border border-[var(--bdr)] bg-[var(--card)] px-2.5 py-1.5 text-sm text-[var(--txt)] shadow-sm focus:ring-2 focus:outline-none"
|
|
858
858
|
>
|
|
859
859
|
{options.map((o) => (
|
|
860
860
|
<option key={o.value} value={o.value}>
|
|
@@ -882,12 +882,12 @@ function GroupBlock({
|
|
|
882
882
|
return (
|
|
883
883
|
<>
|
|
884
884
|
{showHeader && (
|
|
885
|
-
<tr className="bg-muted/30 border-
|
|
885
|
+
<tr className="bg-muted/30 border-b border-[var(--bdr)]">
|
|
886
886
|
<td colSpan={8} className="px-3 py-2">
|
|
887
|
-
<span className="
|
|
887
|
+
<span className="inline-flex items-center gap-2 text-xs font-medium tracking-wider text-[var(--txt)] uppercase">
|
|
888
888
|
{color && <span aria-hidden className={`h-2 w-2 rounded-full ${dotFor(color)}`} />}
|
|
889
889
|
{label}
|
|
890
|
-
<span className="text-
|
|
890
|
+
<span className="text-[var(--sub)] tabular-nums">{count}</span>
|
|
891
891
|
</span>
|
|
892
892
|
</td>
|
|
893
893
|
</tr>
|
|
@@ -934,19 +934,19 @@ function BulkActionBar({
|
|
|
934
934
|
}) {
|
|
935
935
|
return (
|
|
936
936
|
<div className="border-primary/30 bg-primary/5 flex flex-wrap items-center gap-2 rounded-lg border px-4 py-2">
|
|
937
|
-
<span className="text-
|
|
937
|
+
<span className="text-sm font-medium text-[var(--txt)]">{count} selected</span>
|
|
938
938
|
<div className="bg-border h-4 w-px" aria-hidden />
|
|
939
939
|
<button
|
|
940
940
|
type="button"
|
|
941
941
|
onClick={onPublish}
|
|
942
|
-
className="
|
|
942
|
+
className="rounded-md px-2 py-1 text-sm text-[var(--txt)] transition-colors hover:bg-[var(--acc-l)]"
|
|
943
943
|
>
|
|
944
944
|
Publish
|
|
945
945
|
</button>
|
|
946
946
|
<button
|
|
947
947
|
type="button"
|
|
948
948
|
onClick={onDraft}
|
|
949
|
-
className="
|
|
949
|
+
className="rounded-md px-2 py-1 text-sm text-[var(--txt)] transition-colors hover:bg-[var(--acc-l)]"
|
|
950
950
|
>
|
|
951
951
|
Move to draft
|
|
952
952
|
</button>
|
|
@@ -965,7 +965,7 @@ function BulkActionBar({
|
|
|
965
965
|
<button
|
|
966
966
|
type="button"
|
|
967
967
|
onClick={onClear}
|
|
968
|
-
className="
|
|
968
|
+
className="ml-auto inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm text-[var(--sub)] transition-colors hover:text-[var(--txt)]"
|
|
969
969
|
>
|
|
970
970
|
<X className="h-3.5 w-3.5" aria-hidden />
|
|
971
971
|
Clear
|
|
@@ -988,7 +988,7 @@ function TagMenu({
|
|
|
988
988
|
<DropdownMenu.Trigger asChild>
|
|
989
989
|
<button
|
|
990
990
|
type="button"
|
|
991
|
-
className="
|
|
991
|
+
className="inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm text-[var(--txt)] transition-colors hover:bg-[var(--acc-l)]"
|
|
992
992
|
>
|
|
993
993
|
{label}
|
|
994
994
|
<ChevronDown className="h-3 w-3" aria-hidden />
|
|
@@ -998,16 +998,16 @@ function TagMenu({
|
|
|
998
998
|
<DropdownMenu.Content
|
|
999
999
|
align="start"
|
|
1000
1000
|
sideOffset={4}
|
|
1001
|
-
className="
|
|
1001
|
+
className="bg-popover z-50 max-h-64 w-44 overflow-auto rounded-lg border border-[var(--bdr)] p-1 shadow-md"
|
|
1002
1002
|
>
|
|
1003
1003
|
{tags.length === 0 ? (
|
|
1004
|
-
<div className="
|
|
1004
|
+
<div className="px-2 py-1.5 text-xs text-[var(--sub)]">No tags yet</div>
|
|
1005
1005
|
) : (
|
|
1006
1006
|
tags.map((t) => (
|
|
1007
1007
|
<DropdownMenu.Item
|
|
1008
1008
|
key={t.slug}
|
|
1009
1009
|
onSelect={() => onPick(t.slug)}
|
|
1010
|
-
className="
|
|
1010
|
+
className="focus:bg-accent flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm text-[var(--txt)] outline-none"
|
|
1011
1011
|
>
|
|
1012
1012
|
<span aria-hidden className={`h-2 w-2 rounded-full ${dotFor(t.color ?? 'gray')}`} />
|
|
1013
1013
|
{t.name}
|
|
@@ -1039,8 +1039,8 @@ function PageRow({
|
|
|
1039
1039
|
}) {
|
|
1040
1040
|
return (
|
|
1041
1041
|
<tr
|
|
1042
|
-
className={`
|
|
1043
|
-
selected ? 'bg-
|
|
1042
|
+
className={`group border-b border-[var(--bdr)] transition-colors last:border-b-0 hover:bg-[var(--acc-l)] ${
|
|
1043
|
+
selected ? 'bg-[var(--acc-l)]' : ''
|
|
1044
1044
|
}`}
|
|
1045
1045
|
>
|
|
1046
1046
|
<td className="px-3 py-3" onClick={(e) => e.stopPropagation()}>
|
|
@@ -1049,17 +1049,17 @@ function PageRow({
|
|
|
1049
1049
|
aria-label={`Select "${page.title}"`}
|
|
1050
1050
|
checked={selected}
|
|
1051
1051
|
onChange={onSelect}
|
|
1052
|
-
className="
|
|
1052
|
+
className="accent-primary rounded border-[var(--bdr)]"
|
|
1053
1053
|
/>
|
|
1054
1054
|
</td>
|
|
1055
1055
|
<td className="px-3 py-3">
|
|
1056
1056
|
<button type="button" onClick={onOpen} className="flex w-full items-center gap-3 text-left">
|
|
1057
1057
|
<span aria-hidden className="bg-muted block h-9 w-9 shrink-0 rounded-md" />
|
|
1058
1058
|
<span className="min-w-0">
|
|
1059
|
-
<span className="
|
|
1059
|
+
<span className="block truncate text-sm font-medium text-[var(--txt)] group-hover:text-[var(--acc)]">
|
|
1060
1060
|
{page.title}
|
|
1061
1061
|
</span>
|
|
1062
|
-
<span className="
|
|
1062
|
+
<span className="block truncate text-xs text-[var(--sub)]">{page.path}</span>
|
|
1063
1063
|
</span>
|
|
1064
1064
|
</button>
|
|
1065
1065
|
</td>
|
|
@@ -1070,17 +1070,17 @@ function PageRow({
|
|
|
1070
1070
|
<div className="flex items-center gap-2">
|
|
1071
1071
|
<span
|
|
1072
1072
|
aria-hidden
|
|
1073
|
-
className="bg-muted
|
|
1073
|
+
className="bg-muted inline-flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-medium text-[var(--sub)]"
|
|
1074
1074
|
>
|
|
1075
1075
|
{page.authorInitials}
|
|
1076
1076
|
</span>
|
|
1077
|
-
<span className="text-
|
|
1077
|
+
<span className="truncate text-sm text-[var(--txt)]">{page.authorName}</span>
|
|
1078
1078
|
</div>
|
|
1079
1079
|
</td>
|
|
1080
1080
|
<td className="px-3 py-3">
|
|
1081
1081
|
<PostStatusBadge status={page.status} />
|
|
1082
1082
|
</td>
|
|
1083
|
-
<td className="
|
|
1083
|
+
<td className="px-3 py-3 text-sm text-[var(--sub)] tabular-nums">
|
|
1084
1084
|
{formatDateShort(page.lastModified)}
|
|
1085
1085
|
</td>
|
|
1086
1086
|
<td className="px-3 py-3">
|
|
@@ -1117,7 +1117,7 @@ function LoadingRows() {
|
|
|
1117
1117
|
return (
|
|
1118
1118
|
<>
|
|
1119
1119
|
{Array.from({ length: 8 }).map((_, i) => (
|
|
1120
|
-
<tr key={i} className="border-
|
|
1120
|
+
<tr key={i} className="border-b border-[var(--bdr)] last:border-b-0">
|
|
1121
1121
|
{Array.from({ length: 8 }).map((__, j) => (
|
|
1122
1122
|
<td key={j} className="px-3 py-3">
|
|
1123
1123
|
<div className="bg-muted h-4 w-full animate-pulse rounded" />
|
|
@@ -1141,10 +1141,10 @@ function EmptyRow({
|
|
|
1141
1141
|
return (
|
|
1142
1142
|
<tr>
|
|
1143
1143
|
<td colSpan={8} className="px-3 py-16 text-center">
|
|
1144
|
-
<div className="
|
|
1144
|
+
<div className="mb-2 text-sm font-medium text-[var(--txt)]">
|
|
1145
1145
|
{filtersActive ? 'No pages match your filters' : 'No pages yet'}
|
|
1146
1146
|
</div>
|
|
1147
|
-
<div className="
|
|
1147
|
+
<div className="mb-4 text-sm text-[var(--sub)]">
|
|
1148
1148
|
{filtersActive
|
|
1149
1149
|
? 'Try clearing the search, tags, or status filter above.'
|
|
1150
1150
|
: 'Create your first page to get started.'}
|
|
@@ -1184,8 +1184,8 @@ function PaginationFooter({
|
|
|
1184
1184
|
const start = total === 0 ? 0 : (page - 1) * pageSize + 1
|
|
1185
1185
|
const end = Math.min(total, page * pageSize)
|
|
1186
1186
|
return (
|
|
1187
|
-
<div className="
|
|
1188
|
-
<span className="text-
|
|
1187
|
+
<div className="flex items-center justify-between border-t border-[var(--bdr)] px-4 py-3 text-sm">
|
|
1188
|
+
<span className="text-[var(--sub)] tabular-nums">
|
|
1189
1189
|
{start}–{end} of {total} page{total === 1 ? '' : 's'}
|
|
1190
1190
|
</span>
|
|
1191
1191
|
<nav className="flex items-center gap-1" aria-label="Pages pagination">
|
|
@@ -1194,7 +1194,7 @@ function PaginationFooter({
|
|
|
1194
1194
|
onClick={() => onPage(Math.max(1, page - 1))}
|
|
1195
1195
|
disabled={page === 1}
|
|
1196
1196
|
aria-label="Previous page"
|
|
1197
|
-
className="
|
|
1197
|
+
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-[var(--bdr)] bg-[var(--bg)] text-[var(--txt)] transition-colors hover:bg-[var(--acc-l)] disabled:cursor-not-allowed disabled:opacity-40"
|
|
1198
1198
|
>
|
|
1199
1199
|
<ChevronLeft className="h-4 w-4" aria-hidden />
|
|
1200
1200
|
</button>
|
|
@@ -1203,7 +1203,7 @@ function PaginationFooter({
|
|
|
1203
1203
|
<span
|
|
1204
1204
|
key={`gap-${idx}`}
|
|
1205
1205
|
aria-hidden
|
|
1206
|
-
className="
|
|
1206
|
+
className="inline-flex h-8 w-8 items-center justify-center text-sm text-[var(--sub)]"
|
|
1207
1207
|
>
|
|
1208
1208
|
…
|
|
1209
1209
|
</span>
|
|
@@ -1216,7 +1216,7 @@ function PaginationFooter({
|
|
|
1216
1216
|
className={`inline-flex h-8 w-8 items-center justify-center rounded-md text-sm tabular-nums transition-colors ${
|
|
1217
1217
|
p === page
|
|
1218
1218
|
? 'bg-primary text-primary-foreground'
|
|
1219
|
-
: 'border-
|
|
1219
|
+
: 'border border-[var(--bdr)] bg-[var(--bg)] text-[var(--txt)] hover:bg-[var(--acc-l)]'
|
|
1220
1220
|
}`}
|
|
1221
1221
|
>
|
|
1222
1222
|
{p}
|
|
@@ -1228,7 +1228,7 @@ function PaginationFooter({
|
|
|
1228
1228
|
onClick={() => onPage(Math.min(totalPages, page + 1))}
|
|
1229
1229
|
disabled={page === totalPages}
|
|
1230
1230
|
aria-label="Next page"
|
|
1231
|
-
className="
|
|
1231
|
+
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-[var(--bdr)] bg-[var(--bg)] text-[var(--txt)] transition-colors hover:bg-[var(--acc-l)] disabled:cursor-not-allowed disabled:opacity-40"
|
|
1232
1232
|
>
|
|
1233
1233
|
<ChevronRight className="h-4 w-4" aria-hidden />
|
|
1234
1234
|
</button>
|
|
@@ -82,7 +82,7 @@ export function PagesTreeTable({
|
|
|
82
82
|
aria-label="Select all visible pages"
|
|
83
83
|
checked={allSelected}
|
|
84
84
|
onChange={(e) => onToggleAll(e.target.checked)}
|
|
85
|
-
className="
|
|
85
|
+
className="accent-primary rounded border-[var(--bdr)]"
|
|
86
86
|
/>
|
|
87
87
|
</th>
|
|
88
88
|
<th scope="col" className={`${TH_CLASS} min-w-[280px]`}>
|
|
@@ -113,8 +113,11 @@ export function PagesTreeTable({
|
|
|
113
113
|
{!loading && error && (
|
|
114
114
|
<tr>
|
|
115
115
|
<td colSpan={8} className="px-3 py-12 text-center">
|
|
116
|
-
<div className="
|
|
117
|
-
<button
|
|
116
|
+
<div className="mb-2 text-sm font-medium text-[var(--txt)]">{error}</div>
|
|
117
|
+
<button
|
|
118
|
+
onClick={onRetry}
|
|
119
|
+
className="text-primary rounded-sm text-sm hover:underline focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none"
|
|
120
|
+
>
|
|
118
121
|
Try again
|
|
119
122
|
</button>
|
|
120
123
|
</td>
|
|
@@ -123,10 +126,10 @@ export function PagesTreeTable({
|
|
|
123
126
|
{!loading && !error && rows.length === 0 && (
|
|
124
127
|
<tr>
|
|
125
128
|
<td colSpan={8} className="px-3 py-16 text-center">
|
|
126
|
-
<div className="
|
|
129
|
+
<div className="mb-2 text-sm font-medium text-[var(--txt)]">
|
|
127
130
|
{flattened ? 'No pages match your filters' : 'No pages yet'}
|
|
128
131
|
</div>
|
|
129
|
-
<div className="
|
|
132
|
+
<div className="mb-4 text-sm text-[var(--sub)]">
|
|
130
133
|
{flattened
|
|
131
134
|
? 'Try clearing the search or status filter above.'
|
|
132
135
|
: 'Create your first page to get started.'}
|
|
@@ -135,7 +138,7 @@ export function PagesTreeTable({
|
|
|
135
138
|
<button
|
|
136
139
|
type="button"
|
|
137
140
|
onClick={onClearFilters}
|
|
138
|
-
className="text-primary text-sm hover:underline"
|
|
141
|
+
className="text-primary rounded-sm text-sm hover:underline focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none"
|
|
139
142
|
>
|
|
140
143
|
Clear filters
|
|
141
144
|
</button>
|
|
@@ -143,7 +146,7 @@ export function PagesTreeTable({
|
|
|
143
146
|
<button
|
|
144
147
|
type="button"
|
|
145
148
|
onClick={onNewPage}
|
|
146
|
-
className="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-sm font-medium shadow-sm transition-colors"
|
|
149
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-sm font-medium shadow-sm transition-colors focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none"
|
|
147
150
|
>
|
|
148
151
|
<Plus className="h-3.5 w-3.5" aria-hidden />
|
|
149
152
|
New page
|
|
@@ -208,7 +211,7 @@ function TreeRow({
|
|
|
208
211
|
aria-label={`Select "${page.title}"`}
|
|
209
212
|
checked={isSelected}
|
|
210
213
|
onChange={onToggleSelect}
|
|
211
|
-
className="
|
|
214
|
+
className="accent-primary rounded border-[var(--bdr)]"
|
|
212
215
|
/>
|
|
213
216
|
</td>
|
|
214
217
|
<td className="px-3 align-middle">
|
package/src/views/ScriptTags.tsx
CHANGED
|
@@ -139,7 +139,7 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
139
139
|
<span className="text-destructive flex-1 text-sm">{error}</span>
|
|
140
140
|
<button
|
|
141
141
|
onClick={refetch}
|
|
142
|
-
className="border-destructive/40 text-destructive hover:bg-destructive/15 rounded-lg border px-3 py-1 text-sm transition-colors"
|
|
142
|
+
className="border-destructive/40 text-destructive hover:bg-destructive/15 rounded-lg border px-3 py-1 text-sm transition-colors focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none"
|
|
143
143
|
>
|
|
144
144
|
Retry
|
|
145
145
|
</button>
|
|
@@ -148,8 +148,8 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
148
148
|
|
|
149
149
|
<div className="mb-4 flex items-center justify-between">
|
|
150
150
|
<div>
|
|
151
|
-
<h1 className="
|
|
152
|
-
<p className="text-
|
|
151
|
+
<h1 className="mb-1 text-[var(--txt)]">Script Tags</h1>
|
|
152
|
+
<p className="text-sm text-[var(--sub)]">
|
|
153
153
|
Manage tracking codes, analytics, and custom scripts injected into your site
|
|
154
154
|
</p>
|
|
155
155
|
{tags.length > 0 && (
|
|
@@ -171,7 +171,7 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
171
171
|
<button
|
|
172
172
|
type="button"
|
|
173
173
|
onClick={() => setSelection({ mode: 'new' })}
|
|
174
|
-
className="bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors"
|
|
174
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none"
|
|
175
175
|
>
|
|
176
176
|
<Plus className="h-4 w-4" />
|
|
177
177
|
New Script
|
|
@@ -182,45 +182,35 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
182
182
|
<SplitPaneLayout>
|
|
183
183
|
<SplitPaneList className="p-3 pt-0 pr-6 sm:p-4 sm:pt-0 sm:pr-8">
|
|
184
184
|
{tags.length === 0 && !error ? (
|
|
185
|
-
<div className="
|
|
186
|
-
<Code2 className="
|
|
187
|
-
<h3 className="text-
|
|
188
|
-
<p className="
|
|
185
|
+
<div className="rounded-lg border border-[var(--bdr)] bg-[var(--card)] p-12 text-center">
|
|
186
|
+
<Code2 className="mx-auto mb-3 h-10 w-10 text-[var(--sub)]" />
|
|
187
|
+
<h3 className="text-sm font-medium text-[var(--txt)]">No script tags yet</h3>
|
|
188
|
+
<p className="mt-1 text-sm text-[var(--sub)]">
|
|
189
189
|
Add tracking codes like Google Analytics, Tag Manager, or Facebook Pixel.
|
|
190
190
|
</p>
|
|
191
191
|
<button
|
|
192
192
|
type="button"
|
|
193
193
|
onClick={() => setSelection({ mode: 'new' })}
|
|
194
|
-
className="bg-primary text-primary-foreground hover:bg-primary/90 mt-4 inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors"
|
|
194
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90 mt-4 inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none"
|
|
195
195
|
>
|
|
196
196
|
<Plus className="h-4 w-4" />
|
|
197
197
|
Add Your First Script
|
|
198
198
|
</button>
|
|
199
199
|
</div>
|
|
200
200
|
) : (
|
|
201
|
-
<div className="
|
|
201
|
+
<div className="overflow-hidden rounded-lg border border-[var(--bdr)] bg-[var(--card)]">
|
|
202
202
|
<table className="w-full text-sm" aria-label="Script tags">
|
|
203
203
|
<thead>
|
|
204
|
-
<tr className="
|
|
205
|
-
<th className="
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<th className="text-muted-foreground px-4 py-3 text-left font-medium">
|
|
209
|
-
Location
|
|
210
|
-
</th>
|
|
211
|
-
<th className="text-muted-foreground px-4 py-3 text-center font-medium">
|
|
204
|
+
<tr className="bg-muted border-b border-[var(--bdr)]">
|
|
205
|
+
<th className="px-4 py-3 text-left font-medium text-[var(--sub)]">Script</th>
|
|
206
|
+
<th className="px-4 py-3 text-left font-medium text-[var(--sub)]">Location</th>
|
|
207
|
+
<th className="px-4 py-3 text-center font-medium text-[var(--sub)]">
|
|
212
208
|
Priority
|
|
213
209
|
</th>
|
|
214
|
-
<th className="
|
|
215
|
-
<th className="
|
|
216
|
-
|
|
217
|
-
</th>
|
|
218
|
-
<th className="text-muted-foreground px-4 py-3 text-center font-medium">
|
|
219
|
-
Active
|
|
220
|
-
</th>
|
|
221
|
-
<th className="text-muted-foreground px-4 py-3 text-left font-medium">
|
|
222
|
-
Updated
|
|
223
|
-
</th>
|
|
210
|
+
<th className="px-4 py-3 text-left font-medium text-[var(--sub)]">Scope</th>
|
|
211
|
+
<th className="px-4 py-3 text-left font-medium text-[var(--sub)]">Consent</th>
|
|
212
|
+
<th className="px-4 py-3 text-center font-medium text-[var(--sub)]">Active</th>
|
|
213
|
+
<th className="px-4 py-3 text-left font-medium text-[var(--sub)]">Updated</th>
|
|
224
214
|
</tr>
|
|
225
215
|
</thead>
|
|
226
216
|
<tbody>
|
|
@@ -229,8 +219,8 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
229
219
|
return (
|
|
230
220
|
<tr
|
|
231
221
|
key={tag.id}
|
|
232
|
-
className={`
|
|
233
|
-
isSelected ? 'bg-[var(--acc-l)]' : 'hover:bg-
|
|
222
|
+
className={`cursor-pointer border-b border-[var(--bdr)] transition-colors last:border-0 ${
|
|
223
|
+
isSelected ? 'bg-[var(--acc-l)]' : 'hover:bg-[var(--acc-l)]'
|
|
234
224
|
}`}
|
|
235
225
|
onClick={() => toggleRow(tag)}
|
|
236
226
|
>
|
|
@@ -242,29 +232,29 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
242
232
|
toggleRow(tag)
|
|
243
233
|
}}
|
|
244
234
|
aria-expanded={isSelected}
|
|
245
|
-
className="text-
|
|
235
|
+
className="text-left font-medium text-[var(--txt)] hover:text-[var(--acc)]"
|
|
246
236
|
>
|
|
247
237
|
{tag.name}
|
|
248
238
|
</button>
|
|
249
239
|
</td>
|
|
250
240
|
<td className="px-4 py-[11px]">
|
|
251
241
|
<span
|
|
252
|
-
className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${PLACEMENT_COLORS[tag.placement] ?? 'bg-muted text-
|
|
242
|
+
className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${PLACEMENT_COLORS[tag.placement] ?? 'bg-muted text-[var(--txt)]'}`}
|
|
253
243
|
>
|
|
254
244
|
{PLACEMENT_LABELS[tag.placement] ?? tag.placement}
|
|
255
245
|
</span>
|
|
256
246
|
</td>
|
|
257
|
-
<td className="
|
|
247
|
+
<td className="px-4 py-[11px] text-center font-mono text-[var(--sub)]">
|
|
258
248
|
{tag.priority}
|
|
259
249
|
</td>
|
|
260
|
-
<td className="
|
|
250
|
+
<td className="px-4 py-[11px] text-[var(--sub)]">{scopeLabel(tag)}</td>
|
|
261
251
|
<td className="px-4 py-[11px]">
|
|
262
252
|
{tag.consentCategory && tag.consentCategory !== 'none' ? (
|
|
263
253
|
<span className="rounded-[5px] border border-[var(--bdr)] bg-[var(--bg)] px-1.5 py-0.5 text-[11px] text-[var(--sub)]">
|
|
264
254
|
{CONSENT_LABELS[tag.consentCategory] ?? tag.consentCategory}
|
|
265
255
|
</span>
|
|
266
256
|
) : (
|
|
267
|
-
<span className="text-
|
|
257
|
+
<span className="text-xs text-[var(--sub)]">—</span>
|
|
268
258
|
)}
|
|
269
259
|
</td>
|
|
270
260
|
<td
|
|
@@ -278,7 +268,7 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
278
268
|
aria-label={`${tag.enabled ? 'Disable' : 'Enable'} ${tag.name}`}
|
|
279
269
|
/>
|
|
280
270
|
</td>
|
|
281
|
-
<td className="
|
|
271
|
+
<td className="px-4 py-[11px] text-xs text-[var(--sub)]">
|
|
282
272
|
{formatUpdated(tag.updatedAt)}
|
|
283
273
|
</td>
|
|
284
274
|
</tr>
|
|
@@ -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">
|