@actuate-media/cms-admin 0.10.0 → 0.11.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/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +8 -5
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/layout/primitives.test.d.ts +2 -0
- package/dist/__tests__/layout/primitives.test.d.ts.map +1 -0
- package/dist/__tests__/layout/primitives.test.js +34 -0
- package/dist/__tests__/layout/primitives.test.js.map +1 -0
- package/dist/__tests__/lib/cv.test.d.ts +2 -0
- package/dist/__tests__/lib/cv.test.d.ts.map +1 -0
- package/dist/__tests__/lib/cv.test.js +66 -0
- package/dist/__tests__/lib/cv.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/assets/actuate-logo.d.ts +36 -0
- package/dist/assets/actuate-logo.d.ts.map +1 -0
- package/dist/assets/actuate-logo.js +15 -0
- package/dist/assets/actuate-logo.js.map +1 -0
- package/dist/components/Breadcrumbs.js +2 -2
- package/dist/components/CommandPalette.js +10 -10
- package/dist/components/ContentOverviewChart.js +3 -3
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/FocalPointPicker.js +2 -2
- package/dist/components/FolderTree.js +20 -20
- package/dist/components/LivePreview.js +3 -3
- package/dist/components/LocaleSwitcher.js +1 -1
- package/dist/components/MediaPickerModal.js +4 -4
- package/dist/components/PresenceIndicator.js +1 -1
- package/dist/components/SEOConfigPanel.d.ts +2 -0
- package/dist/components/SEOConfigPanel.d.ts.map +1 -0
- package/dist/components/SEOConfigPanel.js +174 -0
- package/dist/components/SEOConfigPanel.js.map +1 -0
- package/dist/components/SEOPanel.js +9 -9
- package/dist/components/SEOPerformance.js +2 -2
- package/dist/components/SchedulePublishDialog.js +1 -1
- package/dist/components/SharePreviewLinkDialog.js +1 -1
- package/dist/components/TipTapEditor.js +5 -5
- package/dist/components/VersionHistory.js +2 -2
- package/dist/components/ui/Badge.d.ts +33 -3
- package/dist/components/ui/Badge.d.ts.map +1 -1
- package/dist/components/ui/Badge.js +42 -8
- package/dist/components/ui/Badge.js.map +1 -1
- package/dist/components/ui/Button.d.ts +19 -8
- package/dist/components/ui/Button.d.ts.map +1 -1
- package/dist/components/ui/Button.js +35 -14
- package/dist/components/ui/Button.js.map +1 -1
- package/dist/components/ui/Card.d.ts +26 -0
- package/dist/components/ui/Card.d.ts.map +1 -0
- package/dist/components/ui/Card.js +45 -0
- package/dist/components/ui/Card.js.map +1 -0
- package/dist/components/ui/DataTable.js +1 -1
- package/dist/components/ui/Input.d.ts +15 -0
- package/dist/components/ui/Input.d.ts.map +1 -0
- package/dist/components/ui/Input.js +23 -0
- package/dist/components/ui/Input.js.map +1 -0
- package/dist/components/ui/SearchInput.js +1 -1
- package/dist/components/ui/Select.d.ts +16 -0
- package/dist/components/ui/Select.d.ts.map +1 -0
- package/dist/components/ui/Select.js +25 -0
- package/dist/components/ui/Select.js.map +1 -0
- package/dist/components/ui/Toast.js +1 -1
- package/dist/components/ui/index.d.ts +10 -4
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -2
- package/dist/components/ui/index.js.map +1 -1
- package/dist/fields/BlockBuilderField.js +3 -3
- package/dist/fields/DateField.js +1 -1
- package/dist/fields/RelationshipField.js +3 -3
- package/dist/fields/TextField.js +1 -1
- package/dist/index.d.ts +2 -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/Layout.d.ts +14 -0
- package/dist/layout/Layout.d.ts.map +1 -1
- package/dist/layout/Layout.js +17 -11
- package/dist/layout/Layout.js.map +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +21 -11
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/layout/primitives/AdminShell.d.ts +43 -0
- package/dist/layout/primitives/AdminShell.d.ts.map +1 -0
- package/dist/layout/primitives/AdminShell.js +51 -0
- package/dist/layout/primitives/AdminShell.js.map +1 -0
- package/dist/layout/primitives/Box.d.ts +19 -0
- package/dist/layout/primitives/Box.d.ts.map +1 -0
- package/dist/layout/primitives/Box.js +12 -0
- package/dist/layout/primitives/Box.js.map +1 -0
- package/dist/layout/primitives/Cluster.d.ts +27 -0
- package/dist/layout/primitives/Cluster.d.ts.map +1 -0
- package/dist/layout/primitives/Cluster.js +37 -0
- package/dist/layout/primitives/Cluster.js.map +1 -0
- package/dist/layout/primitives/Grid.d.ts +45 -0
- package/dist/layout/primitives/Grid.d.ts.map +1 -0
- package/dist/layout/primitives/Grid.js +59 -0
- package/dist/layout/primitives/Grid.js.map +1 -0
- package/dist/layout/primitives/PageContainer.d.ts +36 -0
- package/dist/layout/primitives/PageContainer.d.ts.map +1 -0
- package/dist/layout/primitives/PageContainer.js +41 -0
- package/dist/layout/primitives/PageContainer.js.map +1 -0
- package/dist/layout/primitives/Split.d.ts +34 -0
- package/dist/layout/primitives/Split.d.ts.map +1 -0
- package/dist/layout/primitives/Split.js +27 -0
- package/dist/layout/primitives/Split.js.map +1 -0
- package/dist/layout/primitives/Stack.d.ts +23 -0
- package/dist/layout/primitives/Stack.d.ts.map +1 -0
- package/dist/layout/primitives/Stack.js +34 -0
- package/dist/layout/primitives/Stack.js.map +1 -0
- package/dist/layout/primitives/index.d.ts +30 -0
- package/dist/layout/primitives/index.d.ts.map +1 -0
- package/dist/layout/primitives/index.js +22 -0
- package/dist/layout/primitives/index.js.map +1 -0
- package/dist/layout/primitives/tokens.d.ts +48 -0
- package/dist/layout/primitives/tokens.d.ts.map +1 -0
- package/dist/layout/primitives/tokens.js +54 -0
- package/dist/layout/primitives/tokens.js.map +1 -0
- package/dist/lib/cv.d.ts +53 -0
- package/dist/lib/cv.d.ts.map +1 -0
- package/dist/lib/cv.js +39 -0
- package/dist/lib/cv.js.map +1 -0
- package/dist/views/ApiKeys.js +7 -7
- package/dist/views/CollectionList.js +8 -8
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +333 -78
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/DocumentEdit.js +3 -3
- package/dist/views/ForgotPassword.js +2 -2
- package/dist/views/FormEditor.js +5 -5
- package/dist/views/FormSubmissions.js +6 -6
- package/dist/views/Forms.js +2 -2
- package/dist/views/Login.d.ts +16 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +17 -7
- package/dist/views/Login.js.map +1 -1
- package/dist/views/MediaBrowser.js +16 -16
- package/dist/views/PageEditor.js +2 -2
- package/dist/views/Pages.js +10 -10
- package/dist/views/PostEditor.js +2 -2
- package/dist/views/Posts.js +4 -4
- package/dist/views/Redirects.js +4 -4
- package/dist/views/ResetPassword.js +2 -2
- package/dist/views/SEO.js +6 -6
- package/dist/views/ScriptTagEditor.js +4 -4
- package/dist/views/ScriptTags.js +2 -2
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +9 -8
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/SetupWizard.js +2 -2
- package/dist/views/Users.js +4 -4
- package/dist/views/page-builder/AIBlockAssist.js +1 -1
- package/dist/views/page-builder/AIGenerateDialog.js +10 -10
- package/dist/views/page-builder/BlockEditor.js +10 -10
- package/dist/views/page-builder/BlockPicker.js +4 -4
- package/dist/views/page-builder/BottomBar.js +1 -1
- package/dist/views/page-builder/BuilderToolbar.js +2 -2
- package/dist/views/page-builder/ContextPanel.js +2 -2
- package/dist/views/page-builder/DesignScore.js +9 -9
- package/dist/views/page-builder/NodeSettings.js +8 -8
- package/dist/views/page-builder/PageBuilder.js +3 -3
- package/dist/views/page-builder/PageSettings.js +1 -1
- package/dist/views/page-builder/PageTemplates.js +2 -2
- package/dist/views/page-builder/SEOPanel.js +13 -13
- package/dist/views/page-builder/SavedSections.js +5 -5
- package/dist/views/page-builder/TemplatePicker.js +2 -2
- package/dist/views/page-builder/block-renderers/CTAPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/FallbackPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/HeroPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/ImagePreview.js +3 -3
- package/dist/views/page-builder/block-renderers/TextPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/VideoPreview.js +4 -4
- package/dist/views/page-builder/canvas/BlockRenderer.js +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js +3 -3
- package/dist/views/page-builder/canvas/ColumnRenderer.js +2 -2
- package/dist/views/page-builder/canvas/ContainerRenderer.js +2 -2
- package/dist/views/page-builder/canvas/RowRenderer.js +2 -2
- package/dist/views/page-builder/canvas/SectionRenderer.js +2 -2
- package/package.json +6 -2
- package/src/AdminRoot.tsx +21 -11
- package/src/__tests__/layout/primitives.test.ts +37 -0
- package/src/__tests__/lib/cv.test.ts +74 -0
- package/src/assets/actuate-logo.tsx +72 -0
- package/src/components/Breadcrumbs.tsx +6 -6
- package/src/components/CommandPalette.tsx +34 -34
- package/src/components/ContentOverviewChart.tsx +3 -3
- package/src/components/ErrorBoundary.tsx +3 -3
- package/src/components/FocalPointPicker.tsx +4 -4
- package/src/components/FolderTree.tsx +38 -38
- package/src/components/LivePreview.tsx +16 -16
- package/src/components/LocaleSwitcher.tsx +7 -7
- package/src/components/MediaPickerModal.tsx +21 -21
- package/src/components/PresenceIndicator.tsx +2 -2
- package/src/components/SEOConfigPanel.tsx +582 -0
- package/src/components/SEOPanel.tsx +46 -46
- package/src/components/SEOPerformance.tsx +21 -21
- package/src/components/SchedulePublishDialog.tsx +4 -4
- package/src/components/SharePreviewLinkDialog.tsx +1 -1
- package/src/components/TipTapEditor.tsx +33 -33
- package/src/components/VersionHistory.tsx +16 -16
- package/src/components/ui/Badge.tsx +66 -14
- package/src/components/ui/Button.tsx +70 -33
- package/src/components/ui/Card.tsx +101 -0
- package/src/components/ui/DataTable.tsx +1 -1
- package/src/components/ui/Input.tsx +35 -0
- package/src/components/ui/SearchInput.tsx +4 -4
- package/src/components/ui/Select.tsx +56 -0
- package/src/components/ui/Toast.tsx +1 -1
- package/src/components/ui/index.ts +18 -4
- package/src/fields/BlockBuilderField.tsx +3 -3
- package/src/fields/DateField.tsx +1 -1
- package/src/fields/RelationshipField.tsx +10 -10
- package/src/fields/TextField.tsx +1 -1
- package/src/index.ts +28 -0
- package/src/layout/Header.tsx +28 -28
- package/src/layout/Layout.tsx +39 -46
- package/src/layout/Sidebar.tsx +37 -64
- package/src/layout/primitives/AdminShell.tsx +118 -0
- package/src/layout/primitives/Box.tsx +30 -0
- package/src/layout/primitives/Cluster.tsx +74 -0
- package/src/layout/primitives/Grid.tsx +120 -0
- package/src/layout/primitives/PageContainer.tsx +96 -0
- package/src/layout/primitives/Split.tsx +73 -0
- package/src/layout/primitives/Stack.tsx +67 -0
- package/src/layout/primitives/index.ts +36 -0
- package/src/layout/primitives/tokens.ts +76 -0
- package/src/lib/cv.ts +96 -0
- package/src/styles/build-input.css +1 -1
- package/src/views/ApiKeys.tsx +57 -57
- package/src/views/CollectionList.tsx +30 -30
- package/src/views/Dashboard.tsx +737 -186
- package/src/views/DocumentEdit.tsx +9 -9
- package/src/views/ForgotPassword.tsx +18 -18
- package/src/views/FormEditor.tsx +75 -75
- package/src/views/FormSubmissions.tsx +76 -76
- package/src/views/Forms.tsx +27 -27
- package/src/views/Login.tsx +65 -25
- package/src/views/MediaBrowser.tsx +127 -127
- package/src/views/PageEditor.tsx +25 -25
- package/src/views/Pages.tsx +59 -59
- package/src/views/PostEditor.tsx +37 -37
- package/src/views/Posts.tsx +48 -48
- package/src/views/Redirects.tsx +21 -21
- package/src/views/ResetPassword.tsx +28 -28
- package/src/views/SEO.tsx +144 -144
- package/src/views/ScriptTagEditor.tsx +24 -24
- package/src/views/ScriptTags.tsx +10 -10
- package/src/views/Settings.tsx +88 -80
- package/src/views/SetupWizard.tsx +28 -28
- package/src/views/Users.tsx +20 -20
- package/src/views/page-builder/AIBlockAssist.tsx +1 -1
- package/src/views/page-builder/AIGenerateDialog.tsx +63 -63
- package/src/views/page-builder/BlockEditor.tsx +26 -26
- package/src/views/page-builder/BlockPicker.tsx +22 -22
- package/src/views/page-builder/BottomBar.tsx +8 -8
- package/src/views/page-builder/BuilderToolbar.tsx +17 -17
- package/src/views/page-builder/ContextPanel.tsx +3 -3
- package/src/views/page-builder/DesignScore.tsx +21 -21
- package/src/views/page-builder/NodeSettings.tsx +27 -27
- package/src/views/page-builder/PageBuilder.tsx +11 -11
- package/src/views/page-builder/PageSettings.tsx +4 -4
- package/src/views/page-builder/PageTemplates.tsx +18 -18
- package/src/views/page-builder/SEOPanel.tsx +53 -53
- package/src/views/page-builder/SavedSections.tsx +37 -37
- package/src/views/page-builder/TemplatePicker.tsx +17 -17
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +5 -5
- package/src/views/page-builder/block-renderers/CodePreview.tsx +6 -6
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +3 -3
- package/src/views/page-builder/block-renderers/FormPreview.tsx +20 -20
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +8 -8
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +16 -16
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +4 -4
- package/src/views/page-builder/block-renderers/TextPreview.tsx +14 -14
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +12 -12
- package/src/views/page-builder/canvas/BlockRenderer.tsx +4 -4
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +6 -6
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +3 -3
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/RowRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/SectionRenderer.tsx +2 -2
|
@@ -75,7 +75,7 @@ function MediaPreview({ item }: { item: MediaItem }) {
|
|
|
75
75
|
)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
return <FileImage className="
|
|
78
|
+
return <FileImage className="text-muted-foreground h-6 w-6 sm:h-8 sm:w-8" />
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
type MediaSortKey = 'name' | 'type' | 'size' | 'date'
|
|
@@ -348,17 +348,17 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
348
348
|
<button
|
|
349
349
|
type="button"
|
|
350
350
|
onClick={() => setSortConfig(toggleSort(sortConfig, sortKey))}
|
|
351
|
-
className="flex items-center gap-1 text-xs font-medium text-gray-700 hover:text-gray-900
|
|
351
|
+
className="flex items-center gap-1 text-xs font-medium text-gray-700 transition-colors hover:text-gray-900"
|
|
352
352
|
>
|
|
353
353
|
{label}
|
|
354
354
|
{active ? (
|
|
355
355
|
sortConfig!.direction === 'asc' ? (
|
|
356
|
-
<ArrowUp className="
|
|
356
|
+
<ArrowUp className="h-3 w-3" />
|
|
357
357
|
) : (
|
|
358
|
-
<ArrowDown className="
|
|
358
|
+
<ArrowDown className="h-3 w-3" />
|
|
359
359
|
)
|
|
360
360
|
) : (
|
|
361
|
-
<ArrowUpDown className="
|
|
361
|
+
<ArrowUpDown className="h-3 w-3 text-gray-400" />
|
|
362
362
|
)}
|
|
363
363
|
</button>
|
|
364
364
|
)
|
|
@@ -366,39 +366,39 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
366
366
|
|
|
367
367
|
if (loading) {
|
|
368
368
|
return (
|
|
369
|
-
<div className="p-3 pr-6 sm:p-4 sm:pr-8
|
|
370
|
-
<Loader2 className="
|
|
369
|
+
<div className="flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8">
|
|
370
|
+
<Loader2 className="h-6 w-6 animate-spin text-blue-600" />
|
|
371
371
|
</div>
|
|
372
372
|
)
|
|
373
373
|
}
|
|
374
374
|
|
|
375
375
|
return (
|
|
376
|
-
<div className="p-3 pr-6 sm:p-4 sm:pr-8
|
|
376
|
+
<div className="flex h-full flex-col p-3 pr-6 sm:p-4 sm:pr-8">
|
|
377
377
|
{error && (
|
|
378
378
|
<div className="mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
|
|
379
|
-
<AlertTriangle className="
|
|
380
|
-
<span className="text-sm text-red-800
|
|
379
|
+
<AlertTriangle className="h-5 w-5 shrink-0 text-red-600" />
|
|
380
|
+
<span className="flex-1 text-sm text-red-800">{error}</span>
|
|
381
381
|
<button
|
|
382
382
|
onClick={refetch}
|
|
383
|
-
className="px-3 py-1 text-sm text-red-700
|
|
383
|
+
className="rounded-lg border border-red-300 px-3 py-1 text-sm text-red-700 transition-colors hover:bg-red-100"
|
|
384
384
|
>
|
|
385
385
|
Retry
|
|
386
386
|
</button>
|
|
387
387
|
</div>
|
|
388
388
|
)}
|
|
389
389
|
|
|
390
|
-
<div className="flex items-center justify-between
|
|
390
|
+
<div className="mb-4 flex items-center justify-between">
|
|
391
391
|
<div className="flex items-center gap-3">
|
|
392
392
|
<button
|
|
393
393
|
type="button"
|
|
394
394
|
onClick={() => setSidebarOpen((prev) => !prev)}
|
|
395
|
-
className="p-1.5
|
|
395
|
+
className="rounded-lg p-1.5 transition-colors hover:bg-gray-100"
|
|
396
396
|
title={sidebarOpen ? 'Hide folders' : 'Show folders'}
|
|
397
397
|
>
|
|
398
|
-
<FolderInput className="
|
|
398
|
+
<FolderInput className="h-5 w-5 text-gray-600" />
|
|
399
399
|
</button>
|
|
400
400
|
<div>
|
|
401
|
-
<h1 className="
|
|
401
|
+
<h1 className="mb-1 text-xl font-semibold text-gray-900 sm:text-2xl">Media Library</h1>
|
|
402
402
|
<p className="text-sm text-gray-600">{filteredAndSorted.length} files</p>
|
|
403
403
|
</div>
|
|
404
404
|
</div>
|
|
@@ -414,21 +414,21 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
414
414
|
<button
|
|
415
415
|
onClick={() => fileInputRef.current?.click()}
|
|
416
416
|
disabled={uploading}
|
|
417
|
-
className="flex items-center gap-2 px-4 py-2
|
|
417
|
+
className="flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50"
|
|
418
418
|
>
|
|
419
419
|
{uploading ? (
|
|
420
|
-
<Loader2 className="
|
|
420
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
421
421
|
) : (
|
|
422
|
-
<Upload className="
|
|
422
|
+
<Upload className="h-4 w-4" />
|
|
423
423
|
)}
|
|
424
424
|
{uploading ? 'Uploading...' : 'Upload Files'}
|
|
425
425
|
</button>
|
|
426
426
|
</div>
|
|
427
427
|
</div>
|
|
428
428
|
|
|
429
|
-
<div className="flex
|
|
429
|
+
<div className="flex min-h-0 flex-1 gap-4 overflow-hidden">
|
|
430
430
|
{sidebarOpen && (
|
|
431
|
-
<div className="w-56 shrink-0
|
|
431
|
+
<div className="flex w-56 shrink-0 flex-col overflow-hidden rounded-lg border border-gray-200 bg-white">
|
|
432
432
|
<FolderTree
|
|
433
433
|
scope="media"
|
|
434
434
|
selected={folderSel}
|
|
@@ -443,24 +443,24 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
443
443
|
</div>
|
|
444
444
|
)}
|
|
445
445
|
|
|
446
|
-
<div className="flex-
|
|
447
|
-
<div className="
|
|
448
|
-
<div className="
|
|
449
|
-
<div className="flex items-center gap-3
|
|
450
|
-
<div className="
|
|
451
|
-
<Search className="absolute
|
|
446
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
447
|
+
<div className="mb-4 rounded-lg border border-gray-200 bg-white">
|
|
448
|
+
<div className="flex items-center justify-between p-3">
|
|
449
|
+
<div className="flex flex-1 items-center gap-3">
|
|
450
|
+
<div className="relative max-w-md flex-1">
|
|
451
|
+
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" />
|
|
452
452
|
<input
|
|
453
453
|
type="text"
|
|
454
454
|
placeholder="Search media..."
|
|
455
455
|
value={searchQuery}
|
|
456
456
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
457
|
-
className="w-full
|
|
457
|
+
className="w-full rounded-lg border border-gray-300 py-2 pr-3 pl-9 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
458
458
|
/>
|
|
459
459
|
</div>
|
|
460
460
|
<select
|
|
461
461
|
value={filterType}
|
|
462
462
|
onChange={(e) => setFilterType(e.target.value)}
|
|
463
|
-
className="
|
|
463
|
+
className="rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
464
464
|
>
|
|
465
465
|
<option value="all">All Types</option>
|
|
466
466
|
<option value="image">Images</option>
|
|
@@ -468,25 +468,25 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
468
468
|
<option value="document">Documents</option>
|
|
469
469
|
</select>
|
|
470
470
|
</div>
|
|
471
|
-
<div className="flex items-center gap-1 bg-gray-100 p-1
|
|
471
|
+
<div className="flex items-center gap-1 rounded-lg bg-gray-100 p-1">
|
|
472
472
|
<button
|
|
473
473
|
onClick={() => setViewMode('grid')}
|
|
474
|
-
className={`p-1.5
|
|
474
|
+
className={`rounded p-1.5 transition-colors ${viewMode === 'grid' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-600 hover:text-gray-900'}`}
|
|
475
475
|
>
|
|
476
|
-
<Grid3x3 className="
|
|
476
|
+
<Grid3x3 className="h-4 w-4" />
|
|
477
477
|
</button>
|
|
478
478
|
<button
|
|
479
479
|
onClick={() => setViewMode('list')}
|
|
480
|
-
className={`p-1.5
|
|
480
|
+
className={`rounded p-1.5 transition-colors ${viewMode === 'list' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-600 hover:text-gray-900'}`}
|
|
481
481
|
>
|
|
482
|
-
<List className="
|
|
482
|
+
<List className="h-4 w-4" />
|
|
483
483
|
</button>
|
|
484
484
|
</div>
|
|
485
485
|
</div>
|
|
486
486
|
</div>
|
|
487
487
|
|
|
488
488
|
{selectedMedia.length > 0 && (
|
|
489
|
-
<div className="
|
|
489
|
+
<div className="mb-4 rounded-lg border border-blue-200 bg-blue-50 p-3">
|
|
490
490
|
<div className="flex items-center justify-between">
|
|
491
491
|
<span className="text-sm text-blue-900">
|
|
492
492
|
{selectedMedia.length} file{selectedMedia.length !== 1 ? 's' : ''} selected
|
|
@@ -497,13 +497,13 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
497
497
|
for (const id of selectedMedia) await deleteMedia(id)
|
|
498
498
|
setSelectedMedia([])
|
|
499
499
|
}}
|
|
500
|
-
className="px-3 py-1.5 text-sm
|
|
500
|
+
className="rounded-lg bg-red-600 px-3 py-1.5 text-sm text-white transition-colors hover:bg-red-700"
|
|
501
501
|
>
|
|
502
502
|
Delete Selected
|
|
503
503
|
</button>
|
|
504
504
|
<button
|
|
505
505
|
onClick={() => setSelectedMedia([])}
|
|
506
|
-
className="px-3 py-1.5 text-sm
|
|
506
|
+
className="rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm transition-colors hover:bg-gray-50"
|
|
507
507
|
>
|
|
508
508
|
Cancel
|
|
509
509
|
</button>
|
|
@@ -514,10 +514,10 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
514
514
|
|
|
515
515
|
{filteredAndSorted.length === 0 && !loading ? (
|
|
516
516
|
<div className="flex flex-col items-center justify-center py-16 text-center">
|
|
517
|
-
<div className="
|
|
518
|
-
<ImageIcon className="
|
|
517
|
+
<div className="mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
|
|
518
|
+
<ImageIcon className="h-6 w-6 text-gray-400" />
|
|
519
519
|
</div>
|
|
520
|
-
<h3 className="text-sm font-medium text-gray-900
|
|
520
|
+
<h3 className="mb-1 text-sm font-medium text-gray-900">
|
|
521
521
|
{folderSel.type === 'smart' && folderSel.smart === 'uncategorized'
|
|
522
522
|
? 'No uncategorized media'
|
|
523
523
|
: folderSel.type === 'folder'
|
|
@@ -527,13 +527,13 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
527
527
|
<p className="text-sm text-gray-500">Upload your first file to get started.</p>
|
|
528
528
|
</div>
|
|
529
529
|
) : (
|
|
530
|
-
<div className="flex
|
|
530
|
+
<div className="flex min-h-0 flex-1 gap-4 overflow-hidden">
|
|
531
531
|
<div
|
|
532
|
-
className={`
|
|
532
|
+
className={`overflow-hidden rounded-lg border border-gray-200 bg-white transition-all duration-200 ${panelOpen ? 'min-w-0 flex-1' : 'w-full'}`}
|
|
533
533
|
>
|
|
534
534
|
{viewMode === 'grid' ? (
|
|
535
535
|
<div
|
|
536
|
-
className={`grid gap-2
|
|
536
|
+
className={`grid h-full gap-2 overflow-y-auto p-2 sm:gap-3 sm:p-3 ${
|
|
537
537
|
panelOpen
|
|
538
538
|
? 'grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4'
|
|
539
539
|
: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-8'
|
|
@@ -545,7 +545,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
545
545
|
return (
|
|
546
546
|
<div
|
|
547
547
|
key={item.id}
|
|
548
|
-
className={`group relative aspect-square rounded-lg border-2
|
|
548
|
+
className={`group relative aspect-square cursor-pointer overflow-hidden rounded-lg border-2 transition-all ${
|
|
549
549
|
isActive
|
|
550
550
|
? 'border-blue-500 ring-2 ring-blue-200'
|
|
551
551
|
: selectedMedia.includes(item.id)
|
|
@@ -556,36 +556,36 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
556
556
|
draggable
|
|
557
557
|
onDragStart={(e) => handleDragStart(e, item.id)}
|
|
558
558
|
>
|
|
559
|
-
<div className="
|
|
559
|
+
<div className="flex h-full w-full items-center justify-center bg-gray-100">
|
|
560
560
|
<MediaPreview item={item} />
|
|
561
561
|
</div>
|
|
562
|
-
<div className="absolute inset-0 bg-linear-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100
|
|
563
|
-
<div className="absolute
|
|
564
|
-
<p className="
|
|
565
|
-
<p className="text-white/80
|
|
562
|
+
<div className="absolute inset-0 bg-linear-to-t from-black/60 to-transparent opacity-0 transition-opacity group-hover:opacity-100">
|
|
563
|
+
<div className="absolute right-0 bottom-0 left-0 p-2">
|
|
564
|
+
<p className="truncate text-xs font-medium text-white">{item.name}</p>
|
|
565
|
+
<p className="text-xs text-white/80">{item.size}</p>
|
|
566
566
|
</div>
|
|
567
567
|
</div>
|
|
568
568
|
{hasIssues && (
|
|
569
569
|
<div
|
|
570
|
-
className="absolute top-1.5 left-1.5
|
|
570
|
+
className="absolute top-1.5 left-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-yellow-500"
|
|
571
571
|
title="Needs attention"
|
|
572
572
|
>
|
|
573
|
-
<AlertTriangle className="
|
|
573
|
+
<AlertTriangle className="h-3 w-3 text-white" />
|
|
574
574
|
</div>
|
|
575
575
|
)}
|
|
576
576
|
<div
|
|
577
|
-
className="absolute top-1.5 right-1.5 opacity-0 group-hover:opacity-100
|
|
577
|
+
className="absolute top-1.5 right-1.5 opacity-0 transition-opacity group-hover:opacity-100"
|
|
578
578
|
onClick={(e) => handleCheckbox(e, item.id)}
|
|
579
579
|
>
|
|
580
580
|
<div
|
|
581
|
-
className={`
|
|
581
|
+
className={`flex h-5 w-5 items-center justify-center rounded border-2 transition-colors ${
|
|
582
582
|
selectedMedia.includes(item.id)
|
|
583
|
-
? '
|
|
584
|
-
: 'bg-white/80
|
|
583
|
+
? 'border-blue-600 bg-blue-600'
|
|
584
|
+
: 'border-gray-400 bg-white/80'
|
|
585
585
|
}`}
|
|
586
586
|
>
|
|
587
587
|
{selectedMedia.includes(item.id) && (
|
|
588
|
-
<svg className="
|
|
588
|
+
<svg className="h-3 w-3 text-white" fill="none" viewBox="0 0 12 12">
|
|
589
589
|
<path
|
|
590
590
|
d="M10 3L4.5 8.5L2 6"
|
|
591
591
|
stroke="currentColor"
|
|
@@ -602,9 +602,9 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
602
602
|
})}
|
|
603
603
|
</div>
|
|
604
604
|
) : (
|
|
605
|
-
<div className="overflow-y-auto
|
|
605
|
+
<div className="h-full overflow-y-auto">
|
|
606
606
|
<table className="w-full">
|
|
607
|
-
<thead className="
|
|
607
|
+
<thead className="sticky top-0 border-b border-gray-200 bg-gray-50">
|
|
608
608
|
<tr>
|
|
609
609
|
<th className="w-8 px-3 py-2 text-left">
|
|
610
610
|
<input
|
|
@@ -642,7 +642,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
642
642
|
return (
|
|
643
643
|
<tr
|
|
644
644
|
key={item.id}
|
|
645
|
-
className={`transition-colors
|
|
645
|
+
className={`cursor-pointer transition-colors ${isActive ? 'bg-blue-50' : 'hover:bg-gray-50'}`}
|
|
646
646
|
onClick={() => openDetail(item)}
|
|
647
647
|
draggable
|
|
648
648
|
onDragStart={(e) => handleDragStart(e, item.id)}
|
|
@@ -660,12 +660,12 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
660
660
|
className="rounded border-gray-300"
|
|
661
661
|
/>
|
|
662
662
|
</td>
|
|
663
|
-
<td className="px-1 py-2
|
|
664
|
-
<GripVertical className="
|
|
663
|
+
<td className="cursor-grab px-1 py-2">
|
|
664
|
+
<GripVertical className="h-4 w-4 text-gray-300" />
|
|
665
665
|
</td>
|
|
666
666
|
<td className="px-3 py-2">
|
|
667
667
|
<div className="flex items-center gap-3">
|
|
668
|
-
<div className="
|
|
668
|
+
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded bg-gray-100">
|
|
669
669
|
<MediaPreview item={item} />
|
|
670
670
|
</div>
|
|
671
671
|
<span className="text-sm font-medium text-gray-900">
|
|
@@ -680,11 +680,11 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
680
680
|
<td className="px-3 py-2 text-sm text-gray-600">{item.date}</td>
|
|
681
681
|
<td className="px-3 py-2">
|
|
682
682
|
{hasIssues ? (
|
|
683
|
-
<span className="inline-flex items-center gap-1 px-2 py-0.5
|
|
684
|
-
<AlertTriangle className="
|
|
683
|
+
<span className="inline-flex items-center gap-1 rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800">
|
|
684
|
+
<AlertTriangle className="h-3 w-3" /> Needs attention
|
|
685
685
|
</span>
|
|
686
686
|
) : (
|
|
687
|
-
<span className="inline-flex items-center px-2 py-0.5
|
|
687
|
+
<span className="inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
|
|
688
688
|
Complete
|
|
689
689
|
</span>
|
|
690
690
|
)}
|
|
@@ -699,37 +699,37 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
699
699
|
</div>
|
|
700
700
|
|
|
701
701
|
{panelOpen && activeItem && (
|
|
702
|
-
<div className="w-80
|
|
703
|
-
<div className="flex items-center justify-between
|
|
704
|
-
<h3 className="text-sm font-semibold text-gray-900
|
|
702
|
+
<div className="flex w-80 shrink-0 flex-col overflow-y-auto rounded-lg border border-gray-200 bg-white lg:w-96">
|
|
703
|
+
<div className="sticky top-0 z-10 flex items-center justify-between border-b border-gray-200 bg-white p-4">
|
|
704
|
+
<h3 className="truncate text-sm font-semibold text-gray-900">
|
|
705
705
|
{activeItem.name}
|
|
706
706
|
</h3>
|
|
707
707
|
<button
|
|
708
708
|
onClick={closeDetail}
|
|
709
|
-
className="p-1 hover:bg-gray-100
|
|
709
|
+
className="rounded p-1 transition-colors hover:bg-gray-100"
|
|
710
710
|
aria-label="Close panel"
|
|
711
711
|
>
|
|
712
|
-
<X className="
|
|
712
|
+
<X className="h-4 w-4 text-gray-500" />
|
|
713
713
|
</button>
|
|
714
714
|
</div>
|
|
715
715
|
|
|
716
716
|
<div className="flex-1 overflow-y-auto">
|
|
717
|
-
<div className="
|
|
718
|
-
<div className="aspect-video
|
|
717
|
+
<div className="border-b border-gray-200 p-4">
|
|
718
|
+
<div className="flex aspect-video items-center justify-center overflow-hidden rounded-lg bg-gray-100">
|
|
719
719
|
{isImageMedia(activeItem) ? (
|
|
720
720
|
<MediaPreview item={activeItem} />
|
|
721
721
|
) : (
|
|
722
|
-
<ImageIcon className="
|
|
722
|
+
<ImageIcon className="h-12 w-12 text-gray-300" />
|
|
723
723
|
)}
|
|
724
724
|
</div>
|
|
725
725
|
</div>
|
|
726
726
|
|
|
727
727
|
{issues.length > 0 && (
|
|
728
|
-
<div className="mx-4 mt-4
|
|
728
|
+
<div className="mx-4 mt-4 rounded-lg border border-yellow-200 bg-yellow-50 p-3">
|
|
729
729
|
<div className="flex items-start gap-2">
|
|
730
|
-
<AlertTriangle className="
|
|
730
|
+
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-600" />
|
|
731
731
|
<div>
|
|
732
|
-
<p className="text-xs font-semibold text-yellow-900
|
|
732
|
+
<p className="mb-1 text-xs font-semibold text-yellow-900">
|
|
733
733
|
{issues.length} issue{issues.length !== 1 ? 's' : ''} found
|
|
734
734
|
</p>
|
|
735
735
|
<ul className="space-y-0.5">
|
|
@@ -748,78 +748,78 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
748
748
|
if (!activeItem.title) await handleAiGenerate('title')
|
|
749
749
|
if (activeItem.sizeBytes > 2000000) await handleAiGenerate('optimize')
|
|
750
750
|
}}
|
|
751
|
-
className="mt-2 w-full
|
|
751
|
+
className="mt-2 flex w-full items-center justify-center gap-1.5 rounded-lg bg-yellow-600 px-3 py-1.5 text-xs text-white transition-colors hover:bg-yellow-700"
|
|
752
752
|
>
|
|
753
|
-
<Sparkles className="
|
|
753
|
+
<Sparkles className="h-3.5 w-3.5" />
|
|
754
754
|
AI Fix All Issues
|
|
755
755
|
</button>
|
|
756
756
|
</div>
|
|
757
757
|
)}
|
|
758
758
|
|
|
759
|
-
<div className="
|
|
760
|
-
<h4 className="text-xs font-semibold text-gray-500 uppercase
|
|
759
|
+
<div className="space-y-3 border-b border-gray-200 p-4">
|
|
760
|
+
<h4 className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
|
|
761
761
|
File Information
|
|
762
762
|
</h4>
|
|
763
763
|
<div className="grid grid-cols-2 gap-3">
|
|
764
764
|
<div>
|
|
765
|
-
<div className="text-xs text-gray-500
|
|
765
|
+
<div className="mb-0.5 text-xs text-gray-500">Format</div>
|
|
766
766
|
<div className="text-sm text-gray-900">
|
|
767
767
|
{activeItem.format ?? 'Unknown'}
|
|
768
768
|
</div>
|
|
769
769
|
</div>
|
|
770
770
|
<div>
|
|
771
|
-
<div className="text-xs text-gray-500
|
|
772
|
-
<div className="text-sm text-gray-900
|
|
771
|
+
<div className="mb-0.5 text-xs text-gray-500">File Size</div>
|
|
772
|
+
<div className="flex items-center gap-1 text-sm text-gray-900">
|
|
773
773
|
{activeItem.size}
|
|
774
774
|
{activeItem.sizeBytes > 2000000 && (
|
|
775
|
-
<span className="text-yellow-600
|
|
775
|
+
<span className="text-xs text-yellow-600">(large)</span>
|
|
776
776
|
)}
|
|
777
777
|
</div>
|
|
778
778
|
</div>
|
|
779
779
|
<div>
|
|
780
|
-
<div className="text-xs text-gray-500
|
|
780
|
+
<div className="mb-0.5 text-xs text-gray-500">Dimensions</div>
|
|
781
781
|
<div className="text-sm text-gray-900">
|
|
782
782
|
{activeItem.dimensions ?? '—'}
|
|
783
783
|
</div>
|
|
784
784
|
</div>
|
|
785
785
|
<div>
|
|
786
|
-
<div className="text-xs text-gray-500
|
|
786
|
+
<div className="mb-0.5 text-xs text-gray-500">Uploaded</div>
|
|
787
787
|
<div className="text-sm text-gray-900">{activeItem.date}</div>
|
|
788
788
|
</div>
|
|
789
789
|
</div>
|
|
790
790
|
|
|
791
791
|
<div>
|
|
792
|
-
<div className="text-xs text-gray-500
|
|
792
|
+
<div className="mb-1 text-xs text-gray-500">URL</div>
|
|
793
793
|
<div className="flex items-center gap-1">
|
|
794
|
-
<code className="flex-1
|
|
794
|
+
<code className="flex-1 truncate rounded border border-gray-200 bg-gray-50 px-2 py-1.5 text-xs text-gray-700">
|
|
795
795
|
{activeItem.url}
|
|
796
796
|
</code>
|
|
797
797
|
<button
|
|
798
798
|
onClick={handleCopyUrl}
|
|
799
|
-
className="p-1.5 hover:bg-gray-100
|
|
799
|
+
className="shrink-0 rounded p-1.5 transition-colors hover:bg-gray-100"
|
|
800
800
|
title="Copy URL"
|
|
801
801
|
>
|
|
802
|
-
<Copy className="
|
|
802
|
+
<Copy className="h-3.5 w-3.5 text-gray-500" />
|
|
803
803
|
</button>
|
|
804
804
|
</div>
|
|
805
805
|
</div>
|
|
806
806
|
</div>
|
|
807
807
|
|
|
808
|
-
<div className="
|
|
809
|
-
<h4 className="text-xs font-semibold text-gray-500 uppercase
|
|
808
|
+
<div className="space-y-4 border-b border-gray-200 p-4">
|
|
809
|
+
<h4 className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
|
|
810
810
|
SEO & Accessibility
|
|
811
811
|
</h4>
|
|
812
812
|
|
|
813
813
|
<div>
|
|
814
|
-
<div className="flex items-center justify-between
|
|
814
|
+
<div className="mb-1 flex items-center justify-between">
|
|
815
815
|
<label className="text-sm font-medium text-gray-700">Alt Tag</label>
|
|
816
816
|
<button
|
|
817
817
|
type="button"
|
|
818
818
|
onClick={() => handleAiGenerate('alt')}
|
|
819
819
|
disabled={aiGenerating === 'alt'}
|
|
820
|
-
className="flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-700 disabled:opacity-50
|
|
820
|
+
className="flex items-center gap-1 text-xs text-indigo-600 transition-colors hover:text-indigo-700 disabled:opacity-50"
|
|
821
821
|
>
|
|
822
|
-
<Bot className="
|
|
822
|
+
<Bot className="h-3.5 w-3.5" />
|
|
823
823
|
{aiGenerating === 'alt' ? 'Generating...' : 'AI Generate'}
|
|
824
824
|
</button>
|
|
825
825
|
</div>
|
|
@@ -828,25 +828,25 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
828
828
|
onChange={(e) => setEditAlt(e.target.value)}
|
|
829
829
|
placeholder="Describe this image for accessibility..."
|
|
830
830
|
rows={2}
|
|
831
|
-
className="w-full
|
|
831
|
+
className="w-full resize-none rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
832
832
|
/>
|
|
833
833
|
{!editAlt && (
|
|
834
|
-
<p className="text-xs text-red-500
|
|
834
|
+
<p className="mt-1 text-xs text-red-500">
|
|
835
835
|
Required for accessibility and SEO
|
|
836
836
|
</p>
|
|
837
837
|
)}
|
|
838
838
|
</div>
|
|
839
839
|
|
|
840
840
|
<div>
|
|
841
|
-
<div className="flex items-center justify-between
|
|
841
|
+
<div className="mb-1 flex items-center justify-between">
|
|
842
842
|
<label className="text-sm font-medium text-gray-700">Title</label>
|
|
843
843
|
<button
|
|
844
844
|
type="button"
|
|
845
845
|
onClick={() => handleAiGenerate('title')}
|
|
846
846
|
disabled={aiGenerating === 'title'}
|
|
847
|
-
className="flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-700 disabled:opacity-50
|
|
847
|
+
className="flex items-center gap-1 text-xs text-indigo-600 transition-colors hover:text-indigo-700 disabled:opacity-50"
|
|
848
848
|
>
|
|
849
|
-
<Bot className="
|
|
849
|
+
<Bot className="h-3.5 w-3.5" />
|
|
850
850
|
{aiGenerating === 'title' ? 'Generating...' : 'AI Generate'}
|
|
851
851
|
</button>
|
|
852
852
|
</div>
|
|
@@ -855,25 +855,25 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
855
855
|
value={editTitle}
|
|
856
856
|
onChange={(e) => setEditTitle(e.target.value)}
|
|
857
857
|
placeholder="Image title..."
|
|
858
|
-
className="w-full
|
|
858
|
+
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
859
859
|
/>
|
|
860
860
|
</div>
|
|
861
861
|
|
|
862
862
|
<div>
|
|
863
|
-
<label className="text-sm font-medium text-gray-700
|
|
863
|
+
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
864
864
|
File Name
|
|
865
865
|
</label>
|
|
866
866
|
<input
|
|
867
867
|
type="text"
|
|
868
868
|
value={editFilename}
|
|
869
869
|
onChange={(e) => setEditFilename(e.target.value)}
|
|
870
|
-
className="w-full
|
|
870
|
+
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
|
|
871
871
|
/>
|
|
872
872
|
</div>
|
|
873
873
|
</div>
|
|
874
874
|
|
|
875
875
|
{isImageMedia(activeItem) && activeItem.url && (
|
|
876
|
-
<div className="
|
|
876
|
+
<div className="border-b border-gray-200 p-4">
|
|
877
877
|
<FocalPointPicker
|
|
878
878
|
imageUrl={activeItem.url}
|
|
879
879
|
focalX={focalX}
|
|
@@ -886,8 +886,8 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
886
886
|
</div>
|
|
887
887
|
)}
|
|
888
888
|
|
|
889
|
-
<div className="
|
|
890
|
-
<h4 className="text-xs font-semibold text-gray-500 uppercase
|
|
889
|
+
<div className="border-b border-gray-200 p-4">
|
|
890
|
+
<h4 className="mb-3 text-xs font-semibold tracking-wide text-gray-500 uppercase">
|
|
891
891
|
Used On {activeItem.usedOn && `(${activeItem.usedOn.length})`}
|
|
892
892
|
</h4>
|
|
893
893
|
{activeItem.usedOn && activeItem.usedOn.length > 0 ? (
|
|
@@ -897,23 +897,23 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
897
897
|
key={i}
|
|
898
898
|
type="button"
|
|
899
899
|
onClick={() => onNavigate?.(usage.path)}
|
|
900
|
-
className="w-full
|
|
900
|
+
className="flex w-full items-center gap-2 rounded-lg border border-gray-200 p-2 text-left transition-colors hover:bg-gray-50"
|
|
901
901
|
>
|
|
902
|
-
<Link2 className="
|
|
903
|
-
<span className="text-sm text-gray-900
|
|
902
|
+
<Link2 className="h-4 w-4 shrink-0 text-gray-400" />
|
|
903
|
+
<span className="flex-1 truncate text-sm text-gray-900">
|
|
904
904
|
{usage.page}
|
|
905
905
|
</span>
|
|
906
|
-
<ExternalLink className="
|
|
906
|
+
<ExternalLink className="h-3.5 w-3.5 shrink-0 text-gray-400" />
|
|
907
907
|
</button>
|
|
908
908
|
))}
|
|
909
909
|
</div>
|
|
910
910
|
) : (
|
|
911
|
-
<div className="
|
|
911
|
+
<div className="rounded-lg border border-orange-200 bg-orange-50 p-3">
|
|
912
912
|
<div className="flex items-start gap-2">
|
|
913
|
-
<AlertTriangle className="
|
|
913
|
+
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-orange-600" />
|
|
914
914
|
<div>
|
|
915
915
|
<p className="text-xs font-medium text-orange-900">Orphaned media</p>
|
|
916
|
-
<p className="text-xs text-orange-700
|
|
916
|
+
<p className="mt-0.5 text-xs text-orange-700">
|
|
917
917
|
This file isn't used on any page. Consider deleting it to save
|
|
918
918
|
storage.
|
|
919
919
|
</p>
|
|
@@ -923,38 +923,38 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
923
923
|
)}
|
|
924
924
|
</div>
|
|
925
925
|
|
|
926
|
-
<div className="
|
|
927
|
-
<h4 className="text-xs font-semibold text-gray-500 uppercase
|
|
926
|
+
<div className="space-y-3 p-4">
|
|
927
|
+
<h4 className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
|
|
928
928
|
AI Optimization
|
|
929
929
|
</h4>
|
|
930
930
|
<button
|
|
931
931
|
type="button"
|
|
932
932
|
onClick={() => handleAiGenerate('optimize')}
|
|
933
933
|
disabled={aiGenerating === 'optimize'}
|
|
934
|
-
className="w-full
|
|
934
|
+
className="flex w-full items-center gap-2 rounded-lg border border-indigo-200 bg-indigo-50 p-3 text-left transition-colors hover:bg-indigo-100 disabled:opacity-50"
|
|
935
935
|
>
|
|
936
936
|
<Sparkles
|
|
937
|
-
className={`
|
|
937
|
+
className={`h-5 w-5 shrink-0 text-indigo-600 ${aiGenerating === 'optimize' ? 'animate-spin' : ''}`}
|
|
938
938
|
/>
|
|
939
939
|
<div className="flex-1">
|
|
940
940
|
<div className="text-sm font-medium text-indigo-900">
|
|
941
941
|
{aiGenerating === 'optimize' ? 'Optimizing...' : 'Optimize Image'}
|
|
942
942
|
</div>
|
|
943
|
-
<div className="text-xs text-indigo-700
|
|
943
|
+
<div className="mt-0.5 text-xs text-indigo-700">
|
|
944
944
|
Compress and convert to modern format (WebP/AVIF)
|
|
945
945
|
</div>
|
|
946
946
|
</div>
|
|
947
947
|
</button>
|
|
948
948
|
<button
|
|
949
949
|
type="button"
|
|
950
|
-
className="w-full
|
|
950
|
+
className="flex w-full items-center gap-2 rounded-lg border border-gray-200 p-3 text-left transition-colors hover:bg-gray-50"
|
|
951
951
|
>
|
|
952
|
-
<Bot className="
|
|
952
|
+
<Bot className="h-5 w-5 shrink-0 text-gray-500" />
|
|
953
953
|
<div className="flex-1">
|
|
954
954
|
<div className="text-sm font-medium text-gray-900">
|
|
955
955
|
AI Content Analysis
|
|
956
956
|
</div>
|
|
957
|
-
<div className="text-xs text-gray-600
|
|
957
|
+
<div className="mt-0.5 text-xs text-gray-600">
|
|
958
958
|
Detect objects, faces, text, and suggest categories
|
|
959
959
|
</div>
|
|
960
960
|
</div>
|
|
@@ -962,30 +962,30 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
|
|
|
962
962
|
</div>
|
|
963
963
|
</div>
|
|
964
964
|
|
|
965
|
-
<div className="
|
|
965
|
+
<div className="sticky bottom-0 flex items-center gap-2 border-t border-gray-200 bg-white p-4">
|
|
966
966
|
<button
|
|
967
967
|
type="button"
|
|
968
968
|
onClick={handleSaveDetails}
|
|
969
969
|
disabled={saving}
|
|
970
|
-
className="flex-1
|
|
970
|
+
className="flex flex-1 items-center justify-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50"
|
|
971
971
|
>
|
|
972
|
-
{saving && <Loader2 className="
|
|
972
|
+
{saving && <Loader2 className="h-4 w-4 animate-spin" />}
|
|
973
973
|
{saving ? 'Saving...' : 'Save Changes'}
|
|
974
974
|
</button>
|
|
975
975
|
<button
|
|
976
976
|
type="button"
|
|
977
|
-
className="p-2 hover:bg-gray-100
|
|
977
|
+
className="rounded-lg p-2 transition-colors hover:bg-gray-100"
|
|
978
978
|
title="Download"
|
|
979
979
|
>
|
|
980
|
-
<Download className="
|
|
980
|
+
<Download className="h-4 w-4 text-gray-600" />
|
|
981
981
|
</button>
|
|
982
982
|
<button
|
|
983
983
|
type="button"
|
|
984
984
|
onClick={() => deleteMedia(activeItem.id)}
|
|
985
|
-
className="p-2 hover:bg-gray-100
|
|
985
|
+
className="rounded-lg p-2 transition-colors hover:bg-gray-100"
|
|
986
986
|
title="Delete"
|
|
987
987
|
>
|
|
988
|
-
<Trash2 className="
|
|
988
|
+
<Trash2 className="h-4 w-4 text-red-600" />
|
|
989
989
|
</button>
|
|
990
990
|
</div>
|
|
991
991
|
</div>
|