@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
package/src/views/ApiKeys.tsx
CHANGED
|
@@ -93,43 +93,43 @@ export function ApiKeys(_props: ApiKeysProps) {
|
|
|
93
93
|
|
|
94
94
|
if (loading) {
|
|
95
95
|
return (
|
|
96
|
-
<div className="
|
|
97
|
-
<Loader2 className="
|
|
96
|
+
<div className="flex h-64 items-center justify-center p-4 sm:p-6">
|
|
97
|
+
<Loader2 className="h-6 w-6 animate-spin text-blue-600" />
|
|
98
98
|
</div>
|
|
99
99
|
)
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
return (
|
|
103
103
|
<div className="p-4 sm:p-6">
|
|
104
|
-
<div className="flex flex-col sm:flex-row sm:items-center
|
|
104
|
+
<div className="mb-6 flex flex-col justify-between gap-3 sm:flex-row sm:items-center">
|
|
105
105
|
<div>
|
|
106
|
-
<h1 className="
|
|
107
|
-
<KeyRound className="
|
|
106
|
+
<h1 className="flex items-center gap-2 text-xl font-semibold text-gray-900 sm:text-2xl">
|
|
107
|
+
<KeyRound className="h-5 w-5 text-gray-500" />
|
|
108
108
|
API Keys
|
|
109
109
|
</h1>
|
|
110
|
-
<p className="text-sm text-gray-500
|
|
110
|
+
<p className="mt-1 text-sm text-gray-500">
|
|
111
111
|
Long-lived credentials for programmatic access. Use the{' '}
|
|
112
|
-
<code className="px-1 py-0.5
|
|
112
|
+
<code className="rounded bg-gray-100 px-1 py-0.5 text-xs">Authorization: Bearer</code>{' '}
|
|
113
113
|
header instead of session cookies. API key requests skip CSRF.
|
|
114
114
|
</p>
|
|
115
115
|
</div>
|
|
116
116
|
<button
|
|
117
117
|
type="button"
|
|
118
118
|
onClick={() => setShowCreate(true)}
|
|
119
|
-
className="inline-flex items-center justify-center gap-2 px-4 py-2
|
|
119
|
+
className="inline-flex 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"
|
|
120
120
|
>
|
|
121
|
-
<Plus className="
|
|
121
|
+
<Plus className="h-4 w-4" />
|
|
122
122
|
New API Key
|
|
123
123
|
</button>
|
|
124
124
|
</div>
|
|
125
125
|
|
|
126
126
|
{error && (
|
|
127
127
|
<div className="mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
|
|
128
|
-
<AlertTriangle className="
|
|
129
|
-
<span className="text-sm text-red-800
|
|
128
|
+
<AlertTriangle className="h-5 w-5 shrink-0 text-red-600" />
|
|
129
|
+
<span className="flex-1 text-sm text-red-800">{error}</span>
|
|
130
130
|
<button
|
|
131
131
|
onClick={refetch}
|
|
132
|
-
className="px-3 py-1 text-sm text-red-700
|
|
132
|
+
className="rounded-lg border border-red-300 px-3 py-1 text-sm text-red-700 transition-colors hover:bg-red-100"
|
|
133
133
|
>
|
|
134
134
|
Retry
|
|
135
135
|
</button>
|
|
@@ -137,26 +137,26 @@ export function ApiKeys(_props: ApiKeysProps) {
|
|
|
137
137
|
)}
|
|
138
138
|
|
|
139
139
|
{keys.length === 0 ? (
|
|
140
|
-
<div className="
|
|
141
|
-
<KeyRound className="
|
|
142
|
-
<p className="text-sm text-gray-500
|
|
143
|
-
<p className="text-xs text-gray-400
|
|
140
|
+
<div className="rounded-lg border border-gray-200 bg-white p-10 text-center">
|
|
141
|
+
<KeyRound className="mx-auto mb-3 h-8 w-8 text-gray-300" />
|
|
142
|
+
<p className="mb-1 text-sm text-gray-500">No API keys yet</p>
|
|
143
|
+
<p className="mb-4 text-xs text-gray-400">
|
|
144
144
|
Create one to let AI agents, CI jobs, or external integrations call the CMS API.
|
|
145
145
|
</p>
|
|
146
146
|
<button
|
|
147
147
|
type="button"
|
|
148
148
|
onClick={() => setShowCreate(true)}
|
|
149
|
-
className="inline-flex items-center justify-center gap-2 px-4 py-2
|
|
149
|
+
className="inline-flex 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"
|
|
150
150
|
>
|
|
151
|
-
<Plus className="
|
|
151
|
+
<Plus className="h-4 w-4" />
|
|
152
152
|
Create your first API key
|
|
153
153
|
</button>
|
|
154
154
|
</div>
|
|
155
155
|
) : (
|
|
156
|
-
<div className="
|
|
156
|
+
<div className="overflow-hidden rounded-lg border border-gray-200 bg-white">
|
|
157
157
|
<div className="overflow-x-auto">
|
|
158
158
|
<table className="w-full">
|
|
159
|
-
<thead className="
|
|
159
|
+
<thead className="border-b border-gray-200 bg-gray-50">
|
|
160
160
|
<tr>
|
|
161
161
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-700">Name</th>
|
|
162
162
|
<th className="px-4 py-2 text-left text-xs font-medium text-gray-700">Token</th>
|
|
@@ -183,8 +183,8 @@ export function ApiKeys(_props: ApiKeysProps) {
|
|
|
183
183
|
</div>
|
|
184
184
|
)}
|
|
185
185
|
</td>
|
|
186
|
-
<td className="px-4 py-3
|
|
187
|
-
<td className="px-4 py-3 text-sm text-gray-700
|
|
186
|
+
<td className="px-4 py-3 font-mono text-sm text-gray-700">{k.keyPrefix}…</td>
|
|
187
|
+
<td className="max-w-xs px-4 py-3 text-sm text-gray-700">
|
|
188
188
|
{formatScopes(k.scopes)}
|
|
189
189
|
</td>
|
|
190
190
|
<td className="px-4 py-3 text-sm text-gray-600">
|
|
@@ -193,15 +193,15 @@ export function ApiKeys(_props: ApiKeysProps) {
|
|
|
193
193
|
<td className="px-4 py-3 text-sm text-gray-600">{formatDate(k.expiresAt)}</td>
|
|
194
194
|
<td className="px-4 py-3">
|
|
195
195
|
{revoked ? (
|
|
196
|
-
<span className="inline-flex px-2 py-0.5
|
|
196
|
+
<span className="inline-flex rounded-full bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700">
|
|
197
197
|
Revoked
|
|
198
198
|
</span>
|
|
199
199
|
) : expired ? (
|
|
200
|
-
<span className="inline-flex px-2 py-0.5
|
|
200
|
+
<span className="inline-flex rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-700">
|
|
201
201
|
Expired
|
|
202
202
|
</span>
|
|
203
203
|
) : (
|
|
204
|
-
<span className="inline-flex px-2 py-0.5
|
|
204
|
+
<span className="inline-flex rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700">
|
|
205
205
|
Active
|
|
206
206
|
</span>
|
|
207
207
|
)}
|
|
@@ -211,10 +211,10 @@ export function ApiKeys(_props: ApiKeysProps) {
|
|
|
211
211
|
<button
|
|
212
212
|
type="button"
|
|
213
213
|
onClick={() => handleRevoke(k.id, k.name)}
|
|
214
|
-
className="p-1.5 hover:bg-red-50
|
|
214
|
+
className="rounded p-1.5 transition-colors hover:bg-red-50"
|
|
215
215
|
title="Revoke"
|
|
216
216
|
>
|
|
217
|
-
<Trash2 className="
|
|
217
|
+
<Trash2 className="h-4 w-4 text-red-600" />
|
|
218
218
|
</button>
|
|
219
219
|
)}
|
|
220
220
|
</td>
|
|
@@ -320,35 +320,35 @@ function CreateApiKeyDialog({ open, onClose, onCreated }: CreateApiKeyDialogProp
|
|
|
320
320
|
return (
|
|
321
321
|
<Dialog.Root open={open} onOpenChange={(o) => !o && onClose()}>
|
|
322
322
|
<Dialog.Portal>
|
|
323
|
-
<Dialog.Overlay className="fixed inset-0 bg-black/40
|
|
324
|
-
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
|
|
325
|
-
<Dialog.Title className="text-lg font-semibold text-gray-900
|
|
326
|
-
<Shield className="
|
|
323
|
+
<Dialog.Overlay className="fixed inset-0 z-40 bg-black/40" />
|
|
324
|
+
<Dialog.Content className="fixed top-1/2 left-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6 shadow-xl">
|
|
325
|
+
<Dialog.Title className="mb-4 flex items-center gap-2 text-lg font-semibold text-gray-900">
|
|
326
|
+
<Shield className="h-5 w-5 text-blue-600" />
|
|
327
327
|
New API Key
|
|
328
328
|
</Dialog.Title>
|
|
329
329
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
330
330
|
<div>
|
|
331
|
-
<label className="text-sm font-medium text-gray-700
|
|
331
|
+
<label className="mb-1 block text-sm font-medium text-gray-700">Name</label>
|
|
332
332
|
<input
|
|
333
333
|
type="text"
|
|
334
334
|
value={name}
|
|
335
335
|
onChange={(e) => setName(e.target.value)}
|
|
336
336
|
placeholder="e.g. AI agent — production"
|
|
337
|
-
className="w-full
|
|
337
|
+
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"
|
|
338
338
|
required
|
|
339
339
|
maxLength={100}
|
|
340
340
|
/>
|
|
341
341
|
</div>
|
|
342
342
|
|
|
343
343
|
<div>
|
|
344
|
-
<label className="text-sm font-medium text-gray-700
|
|
344
|
+
<label className="mb-2 block text-sm font-medium text-gray-700">Preset</label>
|
|
345
345
|
<div className="grid grid-cols-2 gap-2">
|
|
346
346
|
{(['content', 'readonly', 'admin', 'custom'] as const).map((p) => (
|
|
347
347
|
<button
|
|
348
348
|
key={p}
|
|
349
349
|
type="button"
|
|
350
350
|
onClick={() => setPreset(p)}
|
|
351
|
-
className={`px-3 py-2 text-
|
|
351
|
+
className={`rounded-lg border px-3 py-2 text-left text-sm transition-colors ${
|
|
352
352
|
preset === p
|
|
353
353
|
? 'border-blue-500 bg-blue-50 text-blue-900'
|
|
354
354
|
: 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50'
|
|
@@ -367,7 +367,7 @@ function CreateApiKeyDialog({ open, onClose, onCreated }: CreateApiKeyDialogProp
|
|
|
367
367
|
</div>
|
|
368
368
|
|
|
369
369
|
{(preset === 'content' || preset === 'custom') && (
|
|
370
|
-
<div className="space-y-2 rounded-lg border border-gray-200
|
|
370
|
+
<div className="space-y-2 rounded-lg border border-gray-200 bg-gray-50 p-3">
|
|
371
371
|
<label className="flex items-center gap-2 text-sm text-gray-700">
|
|
372
372
|
<input
|
|
373
373
|
type="checkbox"
|
|
@@ -390,7 +390,7 @@ function CreateApiKeyDialog({ open, onClose, onCreated }: CreateApiKeyDialogProp
|
|
|
390
390
|
)}
|
|
391
391
|
|
|
392
392
|
<div>
|
|
393
|
-
<label className="text-sm font-medium text-gray-700
|
|
393
|
+
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
394
394
|
Expires in (days)
|
|
395
395
|
</label>
|
|
396
396
|
<input
|
|
@@ -399,22 +399,22 @@ function CreateApiKeyDialog({ open, onClose, onCreated }: CreateApiKeyDialogProp
|
|
|
399
399
|
placeholder="Never (leave blank)"
|
|
400
400
|
value={expiresInDays}
|
|
401
401
|
onChange={(e) => setExpiresInDays(e.target.value)}
|
|
402
|
-
className="w-full
|
|
402
|
+
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"
|
|
403
403
|
/>
|
|
404
404
|
</div>
|
|
405
405
|
|
|
406
406
|
<div>
|
|
407
|
-
<label className="text-sm font-medium text-gray-700
|
|
407
|
+
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
408
408
|
Confirm password
|
|
409
409
|
</label>
|
|
410
410
|
<input
|
|
411
411
|
type="password"
|
|
412
412
|
value={password}
|
|
413
413
|
onChange={(e) => setPassword(e.target.value)}
|
|
414
|
-
className="w-full
|
|
414
|
+
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"
|
|
415
415
|
required
|
|
416
416
|
/>
|
|
417
|
-
<p className="text-xs text-gray-500
|
|
417
|
+
<p className="mt-1 text-xs text-gray-500">
|
|
418
418
|
Creating an API key is a sensitive action and requires re-authentication.
|
|
419
419
|
</p>
|
|
420
420
|
</div>
|
|
@@ -423,14 +423,14 @@ function CreateApiKeyDialog({ open, onClose, onCreated }: CreateApiKeyDialogProp
|
|
|
423
423
|
<button
|
|
424
424
|
type="button"
|
|
425
425
|
onClick={onClose}
|
|
426
|
-
className="px-4 py-2 text-sm
|
|
426
|
+
className="rounded-lg border border-gray-300 px-4 py-2 text-sm transition-colors hover:bg-gray-50"
|
|
427
427
|
>
|
|
428
428
|
Cancel
|
|
429
429
|
</button>
|
|
430
430
|
<button
|
|
431
431
|
type="submit"
|
|
432
432
|
disabled={submitting}
|
|
433
|
-
className="px-4 py-2 text-sm
|
|
433
|
+
className="rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50"
|
|
434
434
|
>
|
|
435
435
|
{submitting ? 'Creating…' : 'Create key'}
|
|
436
436
|
</button>
|
|
@@ -453,35 +453,35 @@ function RevealKeyDialog({
|
|
|
453
453
|
return (
|
|
454
454
|
<Dialog.Root open onOpenChange={(o) => !o && onClose()}>
|
|
455
455
|
<Dialog.Portal>
|
|
456
|
-
<Dialog.Overlay className="fixed inset-0 bg-black/40
|
|
457
|
-
<Dialog.Content className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
|
|
458
|
-
<Dialog.Title className="text-lg font-semibold text-gray-900
|
|
459
|
-
<Shield className="
|
|
456
|
+
<Dialog.Overlay className="fixed inset-0 z-40 bg-black/40" />
|
|
457
|
+
<Dialog.Content className="fixed top-1/2 left-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6 shadow-xl">
|
|
458
|
+
<Dialog.Title className="mb-2 flex items-center gap-2 text-lg font-semibold text-gray-900">
|
|
459
|
+
<Shield className="h-5 w-5 text-green-600" />
|
|
460
460
|
API key created
|
|
461
461
|
</Dialog.Title>
|
|
462
|
-
<Dialog.Description className="text-sm text-gray-600
|
|
462
|
+
<Dialog.Description className="mb-4 text-sm text-gray-600">
|
|
463
463
|
Copy this key now — it will not be shown again. Treat it like a password.
|
|
464
464
|
</Dialog.Description>
|
|
465
465
|
|
|
466
|
-
<div className="rounded-lg border border-amber-200 bg-amber-50 p-3
|
|
467
|
-
<AlertTriangle className="
|
|
466
|
+
<div className="mb-4 flex items-start gap-2 rounded-lg border border-amber-200 bg-amber-50 p-3">
|
|
467
|
+
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-amber-600" />
|
|
468
468
|
<p className="text-xs text-amber-900">
|
|
469
469
|
We only store a hash of this key. If you lose it, you'll need to revoke and
|
|
470
470
|
create a new one.
|
|
471
471
|
</p>
|
|
472
472
|
</div>
|
|
473
473
|
|
|
474
|
-
<div className="flex items-center gap-2
|
|
475
|
-
<code className="flex-1 px-3 py-2
|
|
474
|
+
<div className="mb-4 flex items-center gap-2">
|
|
475
|
+
<code className="flex-1 rounded bg-gray-100 px-3 py-2 font-mono text-sm break-all">
|
|
476
476
|
{shown ? created.key : '•'.repeat(Math.min(48, created.key.length))}
|
|
477
477
|
</code>
|
|
478
478
|
<button
|
|
479
479
|
type="button"
|
|
480
480
|
onClick={() => setShown((s) => !s)}
|
|
481
|
-
className="
|
|
481
|
+
className="rounded border border-gray-300 p-2 hover:bg-gray-50"
|
|
482
482
|
title={shown ? 'Hide' : 'Show'}
|
|
483
483
|
>
|
|
484
|
-
{shown ? <EyeOff className="
|
|
484
|
+
{shown ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
485
485
|
</button>
|
|
486
486
|
<button
|
|
487
487
|
type="button"
|
|
@@ -489,10 +489,10 @@ function RevealKeyDialog({
|
|
|
489
489
|
navigator.clipboard.writeText(created.key)
|
|
490
490
|
toast.success('Copied to clipboard')
|
|
491
491
|
}}
|
|
492
|
-
className="
|
|
492
|
+
className="rounded border border-gray-300 p-2 hover:bg-gray-50"
|
|
493
493
|
title="Copy"
|
|
494
494
|
>
|
|
495
|
-
<Copy className="
|
|
495
|
+
<Copy className="h-4 w-4" />
|
|
496
496
|
</button>
|
|
497
497
|
</div>
|
|
498
498
|
|
|
@@ -500,7 +500,7 @@ function RevealKeyDialog({
|
|
|
500
500
|
<button
|
|
501
501
|
type="button"
|
|
502
502
|
onClick={onClose}
|
|
503
|
-
className="px-4 py-2 text-sm
|
|
503
|
+
className="rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700"
|
|
504
504
|
>
|
|
505
505
|
I've saved it
|
|
506
506
|
</button>
|
|
@@ -155,7 +155,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
155
155
|
<button
|
|
156
156
|
type="button"
|
|
157
157
|
onClick={() => toggleSort(field)}
|
|
158
|
-
className="flex items-center gap-1 text-xs font-medium hover:opacity-80
|
|
158
|
+
className="flex items-center gap-1 text-xs font-medium transition-opacity hover:opacity-80"
|
|
159
159
|
style={{ color: 'var(--actuate-text-secondary, #6b7280)' }}
|
|
160
160
|
>
|
|
161
161
|
{children}
|
|
@@ -165,9 +165,9 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
165
165
|
|
|
166
166
|
if (loading && docs.length === 0) {
|
|
167
167
|
return (
|
|
168
|
-
<div className="
|
|
168
|
+
<div className="flex h-64 items-center justify-center p-4">
|
|
169
169
|
<Loader2
|
|
170
|
-
className="
|
|
170
|
+
className="h-6 w-6 animate-spin"
|
|
171
171
|
style={{ color: 'var(--actuate-primary, #2563eb)' }}
|
|
172
172
|
/>
|
|
173
173
|
</div>
|
|
@@ -175,12 +175,12 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
return (
|
|
178
|
-
<div className="p-3 pr-6 sm:p-4 sm:pr-8
|
|
178
|
+
<div className="flex h-full flex-col p-3 pr-6 sm:p-4 sm:pr-8">
|
|
179
179
|
{/* Header */}
|
|
180
|
-
<div className="flex items-center justify-between
|
|
180
|
+
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
|
|
181
181
|
<div>
|
|
182
182
|
<h1
|
|
183
|
-
className="text-xl sm:text-2xl
|
|
183
|
+
className="text-xl font-semibold sm:text-2xl"
|
|
184
184
|
style={{ color: 'var(--actuate-text, #111827)' }}
|
|
185
185
|
>
|
|
186
186
|
{labels.plural}
|
|
@@ -191,17 +191,17 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
191
191
|
</div>
|
|
192
192
|
<button
|
|
193
193
|
onClick={() => onNavigate(`/${collectionSlug}/new`)}
|
|
194
|
-
className="flex items-center gap-2 px-4 py-2
|
|
194
|
+
className="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors"
|
|
195
195
|
style={{ background: 'var(--actuate-primary, #2563eb)' }}
|
|
196
196
|
>
|
|
197
|
-
<Plus className="
|
|
197
|
+
<Plus className="h-4 w-4" /> New {labels.singular}
|
|
198
198
|
</button>
|
|
199
199
|
</div>
|
|
200
200
|
|
|
201
201
|
{/* Search */}
|
|
202
202
|
<div className="relative mb-4">
|
|
203
203
|
<Search
|
|
204
|
-
className="absolute
|
|
204
|
+
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"
|
|
205
205
|
style={{ color: 'var(--actuate-text-muted, #9ca3af)' }}
|
|
206
206
|
/>
|
|
207
207
|
<input
|
|
@@ -209,7 +209,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
209
209
|
placeholder={`Search ${labels.plural.toLowerCase()}...`}
|
|
210
210
|
value={search}
|
|
211
211
|
onChange={(e) => setSearch(e.target.value)}
|
|
212
|
-
className="w-full
|
|
212
|
+
className="w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus:ring-2 focus:outline-none"
|
|
213
213
|
style={{
|
|
214
214
|
borderColor: 'var(--actuate-border, #d1d5db)',
|
|
215
215
|
color: 'var(--actuate-text, #111827)',
|
|
@@ -220,7 +220,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
220
220
|
{/* Bulk actions */}
|
|
221
221
|
{selected.size > 0 && (
|
|
222
222
|
<div
|
|
223
|
-
className="
|
|
223
|
+
className="mb-4 flex flex-wrap items-center justify-between gap-2 rounded-lg p-3"
|
|
224
224
|
style={{
|
|
225
225
|
background: 'var(--actuate-info-bg, #eff6ff)',
|
|
226
226
|
borderColor: 'var(--actuate-info-border, #bfdbfe)',
|
|
@@ -234,24 +234,24 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
234
234
|
<div className="flex gap-2">
|
|
235
235
|
<button
|
|
236
236
|
onClick={() => bulkAction('publish')}
|
|
237
|
-
className="px-3 py-1.5 text-sm text-white
|
|
237
|
+
className="rounded-lg px-3 py-1.5 text-sm text-white"
|
|
238
238
|
style={{ background: 'var(--actuate-success, #16a34a)' }}
|
|
239
239
|
>
|
|
240
240
|
Publish
|
|
241
241
|
</button>
|
|
242
242
|
<button
|
|
243
243
|
onClick={() => bulkAction('unpublish')}
|
|
244
|
-
className="px-3 py-1.5 text-sm text-white
|
|
244
|
+
className="rounded-lg px-3 py-1.5 text-sm text-white"
|
|
245
245
|
style={{ background: 'var(--actuate-warning, #ca8a04)' }}
|
|
246
246
|
>
|
|
247
247
|
Unpublish
|
|
248
248
|
</button>
|
|
249
249
|
<button
|
|
250
250
|
onClick={() => bulkAction('delete')}
|
|
251
|
-
className="px-3 py-1.5 text-sm text-white
|
|
251
|
+
className="rounded-lg px-3 py-1.5 text-sm text-white"
|
|
252
252
|
style={{ background: 'var(--actuate-danger, #dc2626)' }}
|
|
253
253
|
>
|
|
254
|
-
<Trash2 className="
|
|
254
|
+
<Trash2 className="-mt-0.5 mr-1 inline h-3.5 w-3.5" />
|
|
255
255
|
Delete
|
|
256
256
|
</button>
|
|
257
257
|
</div>
|
|
@@ -260,7 +260,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
260
260
|
|
|
261
261
|
{error && (
|
|
262
262
|
<div
|
|
263
|
-
className="rounded-lg p-3
|
|
263
|
+
className="mb-4 rounded-lg p-3 text-sm"
|
|
264
264
|
style={{
|
|
265
265
|
background: 'var(--actuate-danger-bg, #fef2f2)',
|
|
266
266
|
color: 'var(--actuate-danger-text, #991b1b)',
|
|
@@ -276,17 +276,17 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
276
276
|
{/* Table */}
|
|
277
277
|
{docs.length === 0 && !loading ? (
|
|
278
278
|
<div
|
|
279
|
-
className="flex-1 flex
|
|
279
|
+
className="flex flex-1 flex-col items-center justify-center rounded-lg border p-8"
|
|
280
280
|
style={{
|
|
281
281
|
borderColor: 'var(--actuate-border, #d1d5db)',
|
|
282
282
|
color: 'var(--actuate-text-secondary, #6b7280)',
|
|
283
283
|
}}
|
|
284
284
|
>
|
|
285
|
-
<FileText className="
|
|
286
|
-
<p className="text-sm
|
|
285
|
+
<FileText className="mb-3 h-10 w-10 opacity-40" />
|
|
286
|
+
<p className="mb-3 text-sm">No {labels.plural.toLowerCase()} found</p>
|
|
287
287
|
<button
|
|
288
288
|
onClick={() => onNavigate(`/${collectionSlug}/new`)}
|
|
289
|
-
className="px-4 py-2 text-sm text-white
|
|
289
|
+
className="rounded-lg px-4 py-2 text-sm text-white"
|
|
290
290
|
style={{ background: 'var(--actuate-primary, #2563eb)' }}
|
|
291
291
|
>
|
|
292
292
|
Create {labels.singular}
|
|
@@ -294,7 +294,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
294
294
|
</div>
|
|
295
295
|
) : (
|
|
296
296
|
<div
|
|
297
|
-
className="flex-1 rounded-lg border
|
|
297
|
+
className="flex-1 overflow-auto rounded-lg border"
|
|
298
298
|
style={{ borderColor: 'var(--actuate-border, #d1d5db)' }}
|
|
299
299
|
>
|
|
300
300
|
<table className="w-full text-sm">
|
|
@@ -350,7 +350,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
350
350
|
<button
|
|
351
351
|
type="button"
|
|
352
352
|
onClick={() => onNavigate(`/${collectionSlug}/${doc.id}`)}
|
|
353
|
-
className="font-medium
|
|
353
|
+
className="text-left font-medium hover:underline"
|
|
354
354
|
style={{ color: 'var(--actuate-text, #111827)' }}
|
|
355
355
|
>
|
|
356
356
|
{doc.title || doc.name || `#${doc.id}`}
|
|
@@ -358,7 +358,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
358
358
|
</td>
|
|
359
359
|
<td className="px-3 py-2">
|
|
360
360
|
<span
|
|
361
|
-
className="inline-block px-2 py-0.5
|
|
361
|
+
className="inline-block rounded-full px-2 py-0.5 text-xs font-medium"
|
|
362
362
|
style={{ background: statusColor(doc.status), color: statusText(doc.status) }}
|
|
363
363
|
>
|
|
364
364
|
{doc.status ?? 'DRAFT'}
|
|
@@ -374,11 +374,11 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
374
374
|
<button
|
|
375
375
|
type="button"
|
|
376
376
|
onClick={() => onNavigate(`/${collectionSlug}/${doc.id}`)}
|
|
377
|
-
className="p-1.5
|
|
377
|
+
className="rounded p-1.5 transition-opacity hover:opacity-75"
|
|
378
378
|
title="Edit"
|
|
379
379
|
>
|
|
380
380
|
<MoreHorizontal
|
|
381
|
-
className="
|
|
381
|
+
className="h-4 w-4"
|
|
382
382
|
style={{ color: 'var(--actuate-text-secondary, #6b7280)' }}
|
|
383
383
|
/>
|
|
384
384
|
</button>
|
|
@@ -393,7 +393,7 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
393
393
|
{/* Pagination */}
|
|
394
394
|
{totalPages > 1 && (
|
|
395
395
|
<div
|
|
396
|
-
className="flex items-center justify-between
|
|
396
|
+
className="mt-4 flex items-center justify-between text-sm"
|
|
397
397
|
style={{ color: 'var(--actuate-text-secondary, #6b7280)' }}
|
|
398
398
|
>
|
|
399
399
|
<span>
|
|
@@ -403,18 +403,18 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
|
|
|
403
403
|
<button
|
|
404
404
|
disabled={page <= 1}
|
|
405
405
|
onClick={() => setPage((p) => p - 1)}
|
|
406
|
-
className="flex items-center gap-1 px-3 py-1.5
|
|
406
|
+
className="flex items-center gap-1 rounded-lg border px-3 py-1.5 transition-opacity disabled:opacity-40"
|
|
407
407
|
style={{ borderColor: 'var(--actuate-border, #d1d5db)' }}
|
|
408
408
|
>
|
|
409
|
-
<ChevronLeft className="
|
|
409
|
+
<ChevronLeft className="h-4 w-4" /> Previous
|
|
410
410
|
</button>
|
|
411
411
|
<button
|
|
412
412
|
disabled={page >= totalPages}
|
|
413
413
|
onClick={() => setPage((p) => p + 1)}
|
|
414
|
-
className="flex items-center gap-1 px-3 py-1.5
|
|
414
|
+
className="flex items-center gap-1 rounded-lg border px-3 py-1.5 transition-opacity disabled:opacity-40"
|
|
415
415
|
style={{ borderColor: 'var(--actuate-border, #d1d5db)' }}
|
|
416
416
|
>
|
|
417
|
-
Next <ChevronRight className="
|
|
417
|
+
Next <ChevronRight className="h-4 w-4" />
|
|
418
418
|
</button>
|
|
419
419
|
</div>
|
|
420
420
|
</div>
|