@actuate-media/cms-admin 0.69.0 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +4 -1
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/lib/pages-tree.test.js +40 -1
- package/dist/__tests__/lib/pages-tree.test.js.map +1 -1
- 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/page-builder-template-seed.render.test.d.ts +2 -0
- package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/page-builder-template-seed.render.test.js +48 -0
- package/dist/__tests__/views/page-builder-template-seed.render.test.js.map +1 -0
- package/dist/__tests__/views/page-templates.render.test.js +21 -0
- package/dist/__tests__/views/page-templates.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/TipTapEditor.d.ts.map +1 -1
- package/dist/components/TipTapEditor.js +11 -9
- package/dist/components/TipTapEditor.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/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/Header.js +1 -1
- package/dist/layout/Header.js.map +1 -1
- package/dist/lib/pages-service.d.ts +7 -0
- package/dist/lib/pages-service.d.ts.map +1 -1
- package/dist/lib/pages-service.js +11 -0
- package/dist/lib/pages-service.js.map +1 -1
- package/dist/lib/pages-tree.d.ts +14 -0
- package/dist/lib/pages-tree.d.ts.map +1 -1
- package/dist/lib/pages-tree.js +51 -0
- package/dist/lib/pages-tree.js.map +1 -1
- package/dist/views/DocumentEdit.js +1 -1
- package/dist/views/DocumentEdit.js.map +1 -1
- package/dist/views/FormEditor.js +5 -7
- package/dist/views/FormEditor.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/HiddenLoginNotFound.d.ts +6 -0
- package/dist/views/HiddenLoginNotFound.d.ts.map +1 -1
- package/dist/views/HiddenLoginNotFound.js +6 -0
- package/dist/views/HiddenLoginNotFound.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +3 -2
- 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/PageEditor.d.ts +4 -0
- package/dist/views/PageEditor.d.ts.map +1 -1
- package/dist/views/PageEditor.js +4 -0
- package/dist/views/PageEditor.js.map +1 -1
- package/dist/views/Pages/PagesListView.d.ts.map +1 -1
- package/dist/views/Pages/PagesListView.js +52 -17
- package/dist/views/Pages/PagesListView.js.map +1 -1
- package/dist/views/Pages/PagesTreeTable.d.ts +11 -1
- package/dist/views/Pages/PagesTreeTable.d.ts.map +1 -1
- package/dist/views/Pages/PagesTreeTable.js +40 -9
- package/dist/views/Pages/PagesTreeTable.js.map +1 -1
- package/dist/views/Pages.d.ts +4 -0
- package/dist/views/Pages.d.ts.map +1 -1
- package/dist/views/Pages.js +4 -0
- package/dist/views/Pages.js.map +1 -1
- package/dist/views/Redirects.d.ts +4 -0
- package/dist/views/Redirects.d.ts.map +1 -1
- package/dist/views/Redirects.js +4 -0
- package/dist/views/Redirects.js.map +1 -1
- package/dist/views/ScriptTags.d.ts.map +1 -1
- package/dist/views/ScriptTags.js +4 -3
- package/dist/views/ScriptTags.js.map +1 -1
- package/dist/views/page-builder/PageBuilder.d.ts +3 -1
- package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
- package/dist/views/page-builder/PageBuilder.js +24 -2
- package/dist/views/page-builder/PageBuilder.js.map +1 -1
- package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
- package/dist/views/page-builder/PageTemplates.js +30 -4
- package/dist/views/page-builder/PageTemplates.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/AdminRoot.tsx +11 -1
- package/src/__tests__/lib/pages-tree.test.ts +49 -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/page-builder-template-seed.render.test.tsx +59 -0
- package/src/__tests__/views/page-templates.render.test.tsx +40 -0
- package/src/__tests__/views/redirects-pane.render.test.tsx +156 -0
- package/src/components/SEOPanel.tsx +9 -9
- package/src/components/TipTapEditor.tsx +11 -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/index.ts +3 -0
- package/src/layout/Header.tsx +2 -2
- package/src/lib/pages-service.ts +15 -0
- package/src/lib/pages-tree.ts +54 -0
- 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/FormEditor.tsx +10 -10
- package/src/views/FormEntries.tsx +81 -66
- package/src/views/FormSubmissions.tsx +78 -64
- package/src/views/Forms.tsx +16 -16
- package/src/views/HiddenLoginNotFound.tsx +6 -0
- package/src/views/MediaBrowser.tsx +14 -10
- package/src/views/MediaSeoAudit.tsx +39 -2
- package/src/views/PageEditor.tsx +4 -0
- package/src/views/Pages/PagesListView.tsx +99 -54
- package/src/views/Pages/PagesTreeTable.tsx +204 -109
- package/src/views/Pages.tsx +4 -0
- package/src/views/Redirects.tsx +4 -0
- package/src/views/ScriptTags.tsx +36 -42
- package/src/views/page-builder/PageBuilder.tsx +30 -2
- package/src/views/page-builder/PageTemplates.tsx +56 -15
- 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
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
duplicatePage,
|
|
29
29
|
fetchPagesDataset,
|
|
30
30
|
groupPages,
|
|
31
|
+
movePage,
|
|
31
32
|
type BulkResult,
|
|
32
33
|
type Page,
|
|
33
34
|
type PageGroupBy,
|
|
@@ -35,9 +36,15 @@ import {
|
|
|
35
36
|
type PageStatus,
|
|
36
37
|
type PageTag,
|
|
37
38
|
} from '../../lib/pages-service.js'
|
|
38
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
buildPageTree,
|
|
41
|
+
checkReparent,
|
|
42
|
+
defaultExpandedIds,
|
|
43
|
+
flattenPageTree,
|
|
44
|
+
} from '../../lib/pages-tree.js'
|
|
39
45
|
import { PostStatusBadge, SeoScoreIndicator } from '../../components/posts/badges.js'
|
|
40
46
|
import { PageTagChip, PageTagsCell } from '../../components/pages/tag-badges.js'
|
|
47
|
+
import { Button } from '../../components/ui/Button.js'
|
|
41
48
|
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js'
|
|
42
49
|
import { RowActionButton, RowActions } from '../../components/ui/RowActions.js'
|
|
43
50
|
import { PagesTreeTable } from './PagesTreeTable.js'
|
|
@@ -382,6 +389,38 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
382
389
|
}
|
|
383
390
|
}
|
|
384
391
|
|
|
392
|
+
/** Drag-reparent from the tree view: `sourceId` becomes a child of `targetId`. */
|
|
393
|
+
const runReparent = async (sourceId: string, targetId: string) => {
|
|
394
|
+
const source = allPages.find((p) => p.id === sourceId)
|
|
395
|
+
const target = allPages.find((p) => p.id === targetId)
|
|
396
|
+
if (!source || !target) return
|
|
397
|
+
const check = checkReparent(treeRoots, sourceId, targetId)
|
|
398
|
+
if (!check.ok) {
|
|
399
|
+
toast.error(check.reason)
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
const promise = movePage(sourceId, targetId).then((r) => {
|
|
403
|
+
if (r.error) throw new Error(r.error)
|
|
404
|
+
})
|
|
405
|
+
toast.promise(promise, {
|
|
406
|
+
loading: `Moving "${source.title}"…`,
|
|
407
|
+
success: `"${source.title}" moved under "${target.title}"`,
|
|
408
|
+
error: (e: Error) => e.message || 'Move failed',
|
|
409
|
+
})
|
|
410
|
+
try {
|
|
411
|
+
await promise
|
|
412
|
+
// Make sure the new parent is expanded so the moved row stays visible.
|
|
413
|
+
setExpandedOverride((prev) => {
|
|
414
|
+
const next = new Set(prev ?? defaultExpanded)
|
|
415
|
+
next.add(targetId)
|
|
416
|
+
return next
|
|
417
|
+
})
|
|
418
|
+
setRefreshToken((t) => t + 1)
|
|
419
|
+
} catch {
|
|
420
|
+
/* surfaced via toast.promise */
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
385
424
|
const runRowDelete = async (p: Page) => {
|
|
386
425
|
const promise = deletePage(p.id).then((r) => {
|
|
387
426
|
if (r.error) throw new Error(r.error)
|
|
@@ -412,28 +451,30 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
412
451
|
the design's `.page-hdr` / `.hdr-r` layout. */}
|
|
413
452
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
414
453
|
<div className="flex flex-col gap-1">
|
|
415
|
-
<h1 className="text-
|
|
416
|
-
<p className="text-
|
|
454
|
+
<h1 className="text-[var(--txt)]">Pages</h1>
|
|
455
|
+
<p className="text-sm text-[var(--sub)]">
|
|
417
456
|
{treeMode
|
|
418
457
|
? `${grandTotal} page${grandTotal === 1 ? '' : 's'} total`
|
|
419
458
|
: `${grandTotal} page${grandTotal === 1 ? '' : 's'} · organized by tags`}
|
|
420
459
|
</p>
|
|
421
460
|
</div>
|
|
422
|
-
<
|
|
461
|
+
<Button
|
|
423
462
|
type="button"
|
|
463
|
+
variant="primary"
|
|
464
|
+
size="sm"
|
|
465
|
+
className="shadow-sm"
|
|
424
466
|
onClick={() => onNavigate('/pages/new')}
|
|
425
|
-
|
|
467
|
+
leftIcon={<Plus size={16} aria-hidden />}
|
|
426
468
|
>
|
|
427
|
-
<Plus className="h-3.5 w-3.5" aria-hidden />
|
|
428
469
|
New page
|
|
429
|
-
</
|
|
470
|
+
</Button>
|
|
430
471
|
</div>
|
|
431
472
|
|
|
432
473
|
{/* Controls row */}
|
|
433
474
|
<div className="flex flex-wrap items-center gap-2">
|
|
434
475
|
<div className="relative max-w-sm flex-1">
|
|
435
476
|
<Search
|
|
436
|
-
className="
|
|
477
|
+
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--sub)]"
|
|
437
478
|
aria-hidden
|
|
438
479
|
/>
|
|
439
480
|
<input
|
|
@@ -442,7 +483,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
442
483
|
onChange={(e) => setSearch(e.target.value)}
|
|
443
484
|
aria-label="Search pages"
|
|
444
485
|
placeholder="Search pages…"
|
|
445
|
-
className="
|
|
486
|
+
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
487
|
/>
|
|
447
488
|
</div>
|
|
448
489
|
|
|
@@ -479,7 +520,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
479
520
|
|
|
480
521
|
<ViewModeToggle mode={viewMode} onChange={changeViewMode} />
|
|
481
522
|
|
|
482
|
-
<span className="
|
|
523
|
+
<span className="ml-auto text-sm text-[var(--sub)] tabular-nums">
|
|
483
524
|
{treeMode
|
|
484
525
|
? treeFiltersActive
|
|
485
526
|
? `${treeRows.length} result${treeRows.length === 1 ? '' : 's'}`
|
|
@@ -492,7 +533,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
492
533
|
with the page hierarchy) */}
|
|
493
534
|
{!treeMode && (
|
|
494
535
|
<div className="flex flex-wrap items-center gap-2">
|
|
495
|
-
<span className="text-
|
|
536
|
+
<span className="text-[10px] font-medium tracking-wider text-[var(--sub)] uppercase">
|
|
496
537
|
Tags
|
|
497
538
|
</span>
|
|
498
539
|
{tags.map((t) => (
|
|
@@ -509,7 +550,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
509
550
|
<button
|
|
510
551
|
type="button"
|
|
511
552
|
onClick={() => setSelectedTags(new Set())}
|
|
512
|
-
className="
|
|
553
|
+
className="inline-flex items-center gap-1 text-xs text-[var(--sub)] hover:text-[var(--txt)]"
|
|
513
554
|
>
|
|
514
555
|
<X className="h-3 w-3" aria-hidden />
|
|
515
556
|
Clear tags
|
|
@@ -543,7 +584,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
543
584
|
)}
|
|
544
585
|
|
|
545
586
|
{/* Table card */}
|
|
546
|
-
<div className="
|
|
587
|
+
<div className="overflow-hidden rounded-xl border border-[var(--bdr)] bg-[var(--card)] shadow-sm">
|
|
547
588
|
{treeMode ? (
|
|
548
589
|
<PagesTreeTable
|
|
549
590
|
rows={treeRows}
|
|
@@ -559,6 +600,8 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
559
600
|
onAddSubPage={(p) => onNavigate(`/pages/new?parent=${encodeURIComponent(p.id)}`)}
|
|
560
601
|
onDuplicate={(p) => void runDuplicate(p)}
|
|
561
602
|
onDelete={(p) => setRowDelete(p)}
|
|
603
|
+
onReparent={(sourceId, targetId) => void runReparent(sourceId, targetId)}
|
|
604
|
+
canReparent={(sourceId, targetId) => checkReparent(treeRoots, sourceId, targetId).ok}
|
|
562
605
|
onRetry={() => setRefreshToken((t) => t + 1)}
|
|
563
606
|
onClearFilters={clearFilters}
|
|
564
607
|
onNewPage={() => onNavigate('/pages/new')}
|
|
@@ -579,15 +622,15 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
579
622
|
<col style={{ width: '80px' }} />
|
|
580
623
|
<col style={{ width: '110px' }} />
|
|
581
624
|
</colgroup>
|
|
582
|
-
<thead className="
|
|
583
|
-
<tr className="text-
|
|
625
|
+
<thead className="bg-muted/40 border-b border-[var(--bdr)]">
|
|
626
|
+
<tr className="text-xs text-[var(--sub)]">
|
|
584
627
|
<th scope="col" className="px-3 py-2.5 text-left">
|
|
585
628
|
<input
|
|
586
629
|
type="checkbox"
|
|
587
630
|
aria-label="Select all pages on this page"
|
|
588
631
|
checked={allOnPageSelected}
|
|
589
632
|
onChange={(e) => toggleAllOnPage(e.target.checked)}
|
|
590
|
-
className="
|
|
633
|
+
className="accent-primary rounded border-[var(--bdr)]"
|
|
591
634
|
/>
|
|
592
635
|
</th>
|
|
593
636
|
<th
|
|
@@ -668,7 +711,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
|
|
|
668
711
|
{!loading && error && (
|
|
669
712
|
<tr>
|
|
670
713
|
<td colSpan={8} className="px-3 py-12 text-center">
|
|
671
|
-
<div className="
|
|
714
|
+
<div className="mb-2 text-sm font-medium text-[var(--txt)]">{error}</div>
|
|
672
715
|
<button
|
|
673
716
|
onClick={() => setRefreshToken((t) => t + 1)}
|
|
674
717
|
className="text-primary text-sm hover:underline"
|
|
@@ -779,8 +822,8 @@ function SortHeader({
|
|
|
779
822
|
<button
|
|
780
823
|
type="button"
|
|
781
824
|
onClick={() => onSort(field)}
|
|
782
|
-
className={`
|
|
783
|
-
active ? 'text-
|
|
825
|
+
className={`inline-flex items-center gap-1 text-xs font-medium tracking-wider uppercase transition-colors hover:text-[var(--txt)] ${
|
|
826
|
+
active ? 'text-[var(--txt)]' : 'text-[var(--sub)]'
|
|
784
827
|
}`}
|
|
785
828
|
>
|
|
786
829
|
<span>{label}</span>
|
|
@@ -849,12 +892,12 @@ function FilterSelect({
|
|
|
849
892
|
options: { value: string; label: string }[]
|
|
850
893
|
}) {
|
|
851
894
|
return (
|
|
852
|
-
<label className="
|
|
853
|
-
<span className="text-
|
|
895
|
+
<label className="inline-flex items-center gap-1.5 text-sm text-[var(--txt)]">
|
|
896
|
+
<span className="text-[var(--sub)]">{label}:</span>
|
|
854
897
|
<select
|
|
855
898
|
value={value}
|
|
856
899
|
onChange={(e) => onChange(e.target.value)}
|
|
857
|
-
className="
|
|
900
|
+
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
901
|
>
|
|
859
902
|
{options.map((o) => (
|
|
860
903
|
<option key={o.value} value={o.value}>
|
|
@@ -882,12 +925,12 @@ function GroupBlock({
|
|
|
882
925
|
return (
|
|
883
926
|
<>
|
|
884
927
|
{showHeader && (
|
|
885
|
-
<tr className="bg-muted/30 border-
|
|
928
|
+
<tr className="bg-muted/30 border-b border-[var(--bdr)]">
|
|
886
929
|
<td colSpan={8} className="px-3 py-2">
|
|
887
|
-
<span className="
|
|
930
|
+
<span className="inline-flex items-center gap-2 text-xs font-medium tracking-wider text-[var(--txt)] uppercase">
|
|
888
931
|
{color && <span aria-hidden className={`h-2 w-2 rounded-full ${dotFor(color)}`} />}
|
|
889
932
|
{label}
|
|
890
|
-
<span className="text-
|
|
933
|
+
<span className="text-[var(--sub)] tabular-nums">{count}</span>
|
|
891
934
|
</span>
|
|
892
935
|
</td>
|
|
893
936
|
</tr>
|
|
@@ -934,19 +977,19 @@ function BulkActionBar({
|
|
|
934
977
|
}) {
|
|
935
978
|
return (
|
|
936
979
|
<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-
|
|
980
|
+
<span className="text-sm font-medium text-[var(--txt)]">{count} selected</span>
|
|
938
981
|
<div className="bg-border h-4 w-px" aria-hidden />
|
|
939
982
|
<button
|
|
940
983
|
type="button"
|
|
941
984
|
onClick={onPublish}
|
|
942
|
-
className="
|
|
985
|
+
className="rounded-md px-2 py-1 text-sm text-[var(--txt)] transition-colors hover:bg-[var(--acc-l)]"
|
|
943
986
|
>
|
|
944
987
|
Publish
|
|
945
988
|
</button>
|
|
946
989
|
<button
|
|
947
990
|
type="button"
|
|
948
991
|
onClick={onDraft}
|
|
949
|
-
className="
|
|
992
|
+
className="rounded-md px-2 py-1 text-sm text-[var(--txt)] transition-colors hover:bg-[var(--acc-l)]"
|
|
950
993
|
>
|
|
951
994
|
Move to draft
|
|
952
995
|
</button>
|
|
@@ -965,7 +1008,7 @@ function BulkActionBar({
|
|
|
965
1008
|
<button
|
|
966
1009
|
type="button"
|
|
967
1010
|
onClick={onClear}
|
|
968
|
-
className="
|
|
1011
|
+
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
1012
|
>
|
|
970
1013
|
<X className="h-3.5 w-3.5" aria-hidden />
|
|
971
1014
|
Clear
|
|
@@ -988,7 +1031,7 @@ function TagMenu({
|
|
|
988
1031
|
<DropdownMenu.Trigger asChild>
|
|
989
1032
|
<button
|
|
990
1033
|
type="button"
|
|
991
|
-
className="
|
|
1034
|
+
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
1035
|
>
|
|
993
1036
|
{label}
|
|
994
1037
|
<ChevronDown className="h-3 w-3" aria-hidden />
|
|
@@ -998,16 +1041,16 @@ function TagMenu({
|
|
|
998
1041
|
<DropdownMenu.Content
|
|
999
1042
|
align="start"
|
|
1000
1043
|
sideOffset={4}
|
|
1001
|
-
className="
|
|
1044
|
+
className="bg-popover z-50 max-h-64 w-44 overflow-auto rounded-lg border border-[var(--bdr)] p-1 shadow-md"
|
|
1002
1045
|
>
|
|
1003
1046
|
{tags.length === 0 ? (
|
|
1004
|
-
<div className="
|
|
1047
|
+
<div className="px-2 py-1.5 text-xs text-[var(--sub)]">No tags yet</div>
|
|
1005
1048
|
) : (
|
|
1006
1049
|
tags.map((t) => (
|
|
1007
1050
|
<DropdownMenu.Item
|
|
1008
1051
|
key={t.slug}
|
|
1009
1052
|
onSelect={() => onPick(t.slug)}
|
|
1010
|
-
className="
|
|
1053
|
+
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
1054
|
>
|
|
1012
1055
|
<span aria-hidden className={`h-2 w-2 rounded-full ${dotFor(t.color ?? 'gray')}`} />
|
|
1013
1056
|
{t.name}
|
|
@@ -1039,8 +1082,8 @@ function PageRow({
|
|
|
1039
1082
|
}) {
|
|
1040
1083
|
return (
|
|
1041
1084
|
<tr
|
|
1042
|
-
className={`
|
|
1043
|
-
selected ? 'bg-
|
|
1085
|
+
className={`group border-b border-[var(--bdr)] transition-colors last:border-b-0 hover:bg-[var(--acc-l)] ${
|
|
1086
|
+
selected ? 'bg-[var(--acc-l)]' : ''
|
|
1044
1087
|
}`}
|
|
1045
1088
|
>
|
|
1046
1089
|
<td className="px-3 py-3" onClick={(e) => e.stopPropagation()}>
|
|
@@ -1049,17 +1092,17 @@ function PageRow({
|
|
|
1049
1092
|
aria-label={`Select "${page.title}"`}
|
|
1050
1093
|
checked={selected}
|
|
1051
1094
|
onChange={onSelect}
|
|
1052
|
-
className="
|
|
1095
|
+
className="accent-primary rounded border-[var(--bdr)]"
|
|
1053
1096
|
/>
|
|
1054
1097
|
</td>
|
|
1055
1098
|
<td className="px-3 py-3">
|
|
1056
1099
|
<button type="button" onClick={onOpen} className="flex w-full items-center gap-3 text-left">
|
|
1057
1100
|
<span aria-hidden className="bg-muted block h-9 w-9 shrink-0 rounded-md" />
|
|
1058
1101
|
<span className="min-w-0">
|
|
1059
|
-
<span className="
|
|
1102
|
+
<span className="block truncate text-sm font-medium text-[var(--txt)] group-hover:text-[var(--acc)]">
|
|
1060
1103
|
{page.title}
|
|
1061
1104
|
</span>
|
|
1062
|
-
<span className="
|
|
1105
|
+
<span className="block truncate text-xs text-[var(--sub)]">{page.path}</span>
|
|
1063
1106
|
</span>
|
|
1064
1107
|
</button>
|
|
1065
1108
|
</td>
|
|
@@ -1070,17 +1113,17 @@ function PageRow({
|
|
|
1070
1113
|
<div className="flex items-center gap-2">
|
|
1071
1114
|
<span
|
|
1072
1115
|
aria-hidden
|
|
1073
|
-
className="bg-muted
|
|
1116
|
+
className="bg-muted inline-flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-medium text-[var(--sub)]"
|
|
1074
1117
|
>
|
|
1075
1118
|
{page.authorInitials}
|
|
1076
1119
|
</span>
|
|
1077
|
-
<span className="text-
|
|
1120
|
+
<span className="truncate text-sm text-[var(--txt)]">{page.authorName}</span>
|
|
1078
1121
|
</div>
|
|
1079
1122
|
</td>
|
|
1080
1123
|
<td className="px-3 py-3">
|
|
1081
1124
|
<PostStatusBadge status={page.status} />
|
|
1082
1125
|
</td>
|
|
1083
|
-
<td className="
|
|
1126
|
+
<td className="px-3 py-3 text-sm text-[var(--sub)] tabular-nums">
|
|
1084
1127
|
{formatDateShort(page.lastModified)}
|
|
1085
1128
|
</td>
|
|
1086
1129
|
<td className="px-3 py-3">
|
|
@@ -1117,7 +1160,7 @@ function LoadingRows() {
|
|
|
1117
1160
|
return (
|
|
1118
1161
|
<>
|
|
1119
1162
|
{Array.from({ length: 8 }).map((_, i) => (
|
|
1120
|
-
<tr key={i} className="border-
|
|
1163
|
+
<tr key={i} className="border-b border-[var(--bdr)] last:border-b-0">
|
|
1121
1164
|
{Array.from({ length: 8 }).map((__, j) => (
|
|
1122
1165
|
<td key={j} className="px-3 py-3">
|
|
1123
1166
|
<div className="bg-muted h-4 w-full animate-pulse rounded" />
|
|
@@ -1141,10 +1184,10 @@ function EmptyRow({
|
|
|
1141
1184
|
return (
|
|
1142
1185
|
<tr>
|
|
1143
1186
|
<td colSpan={8} className="px-3 py-16 text-center">
|
|
1144
|
-
<div className="
|
|
1187
|
+
<div className="mb-2 text-sm font-medium text-[var(--txt)]">
|
|
1145
1188
|
{filtersActive ? 'No pages match your filters' : 'No pages yet'}
|
|
1146
1189
|
</div>
|
|
1147
|
-
<div className="
|
|
1190
|
+
<div className="mb-4 text-sm text-[var(--sub)]">
|
|
1148
1191
|
{filtersActive
|
|
1149
1192
|
? 'Try clearing the search, tags, or status filter above.'
|
|
1150
1193
|
: 'Create your first page to get started.'}
|
|
@@ -1154,14 +1197,16 @@ function EmptyRow({
|
|
|
1154
1197
|
Clear filters
|
|
1155
1198
|
</button>
|
|
1156
1199
|
) : (
|
|
1157
|
-
<
|
|
1200
|
+
<Button
|
|
1158
1201
|
type="button"
|
|
1202
|
+
variant="primary"
|
|
1203
|
+
size="sm"
|
|
1204
|
+
className="shadow-sm"
|
|
1159
1205
|
onClick={onNewPage}
|
|
1160
|
-
|
|
1206
|
+
leftIcon={<Plus size={16} aria-hidden />}
|
|
1161
1207
|
>
|
|
1162
|
-
<Plus className="h-3.5 w-3.5" aria-hidden />
|
|
1163
1208
|
New page
|
|
1164
|
-
</
|
|
1209
|
+
</Button>
|
|
1165
1210
|
)}
|
|
1166
1211
|
</td>
|
|
1167
1212
|
</tr>
|
|
@@ -1184,8 +1229,8 @@ function PaginationFooter({
|
|
|
1184
1229
|
const start = total === 0 ? 0 : (page - 1) * pageSize + 1
|
|
1185
1230
|
const end = Math.min(total, page * pageSize)
|
|
1186
1231
|
return (
|
|
1187
|
-
<div className="
|
|
1188
|
-
<span className="text-
|
|
1232
|
+
<div className="flex items-center justify-between border-t border-[var(--bdr)] px-4 py-3 text-sm">
|
|
1233
|
+
<span className="text-[var(--sub)] tabular-nums">
|
|
1189
1234
|
{start}–{end} of {total} page{total === 1 ? '' : 's'}
|
|
1190
1235
|
</span>
|
|
1191
1236
|
<nav className="flex items-center gap-1" aria-label="Pages pagination">
|
|
@@ -1194,7 +1239,7 @@ function PaginationFooter({
|
|
|
1194
1239
|
onClick={() => onPage(Math.max(1, page - 1))}
|
|
1195
1240
|
disabled={page === 1}
|
|
1196
1241
|
aria-label="Previous page"
|
|
1197
|
-
className="
|
|
1242
|
+
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
1243
|
>
|
|
1199
1244
|
<ChevronLeft className="h-4 w-4" aria-hidden />
|
|
1200
1245
|
</button>
|
|
@@ -1203,7 +1248,7 @@ function PaginationFooter({
|
|
|
1203
1248
|
<span
|
|
1204
1249
|
key={`gap-${idx}`}
|
|
1205
1250
|
aria-hidden
|
|
1206
|
-
className="
|
|
1251
|
+
className="inline-flex h-8 w-8 items-center justify-center text-sm text-[var(--sub)]"
|
|
1207
1252
|
>
|
|
1208
1253
|
…
|
|
1209
1254
|
</span>
|
|
@@ -1216,7 +1261,7 @@ function PaginationFooter({
|
|
|
1216
1261
|
className={`inline-flex h-8 w-8 items-center justify-center rounded-md text-sm tabular-nums transition-colors ${
|
|
1217
1262
|
p === page
|
|
1218
1263
|
? 'bg-primary text-primary-foreground'
|
|
1219
|
-
: 'border-
|
|
1264
|
+
: 'border border-[var(--bdr)] bg-[var(--bg)] text-[var(--txt)] hover:bg-[var(--acc-l)]'
|
|
1220
1265
|
}`}
|
|
1221
1266
|
>
|
|
1222
1267
|
{p}
|
|
@@ -1228,7 +1273,7 @@ function PaginationFooter({
|
|
|
1228
1273
|
onClick={() => onPage(Math.min(totalPages, page + 1))}
|
|
1229
1274
|
disabled={page === totalPages}
|
|
1230
1275
|
aria-label="Next page"
|
|
1231
|
-
className="
|
|
1276
|
+
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
1277
|
>
|
|
1233
1278
|
<ChevronRight className="h-4 w-4" aria-hidden />
|
|
1234
1279
|
</button>
|