@actuate-media/cms-admin 0.49.0 → 0.49.3
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 +34 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +10 -11
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/modal.render.test.d.ts +2 -0
- package/dist/__tests__/components/modal.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/modal.render.test.js +37 -0
- package/dist/__tests__/components/modal.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/FolderTree.js +19 -19
- package/dist/components/FolderTree.js.map +1 -1
- package/dist/components/MediaPickerModal.d.ts +1 -1
- package/dist/components/MediaPickerModal.d.ts.map +1 -1
- package/dist/components/MediaPickerModal.js +12 -11
- package/dist/components/MediaPickerModal.js.map +1 -1
- package/dist/components/SchedulePublishDialog.d.ts +1 -1
- package/dist/components/SchedulePublishDialog.d.ts.map +1 -1
- package/dist/components/SchedulePublishDialog.js +6 -4
- package/dist/components/SchedulePublishDialog.js.map +1 -1
- package/dist/components/SharePreviewLinkDialog.d.ts +1 -1
- package/dist/components/SharePreviewLinkDialog.d.ts.map +1 -1
- package/dist/components/SharePreviewLinkDialog.js +10 -9
- package/dist/components/SharePreviewLinkDialog.js.map +1 -1
- package/dist/components/VersionHistory.d.ts +1 -1
- package/dist/components/VersionHistory.d.ts.map +1 -1
- package/dist/components/VersionHistory.js +14 -14
- package/dist/components/VersionHistory.js.map +1 -1
- package/dist/components/ui/Modal.d.ts +17 -1
- package/dist/components/ui/Modal.d.ts.map +1 -1
- package/dist/components/ui/Modal.js +16 -15
- package/dist/components/ui/Modal.js.map +1 -1
- package/dist/fields/BlockBuilderField.d.ts.map +1 -1
- package/dist/fields/BlockBuilderField.js +6 -4
- package/dist/fields/BlockBuilderField.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +16 -16
- package/dist/views/MediaBrowser.js.map +1 -1
- package/dist/views/Pages/PagesListView.d.ts.map +1 -1
- package/dist/views/Pages/PagesListView.js +3 -3
- package/dist/views/Pages/PagesListView.js.map +1 -1
- package/dist/views/Posts/PostsListView.d.ts.map +1 -1
- package/dist/views/Posts/PostsListView.js +3 -3
- package/dist/views/Posts/PostsListView.js.map +1 -1
- package/dist/views/Redirects.d.ts.map +1 -1
- package/dist/views/Redirects.js +7 -7
- package/dist/views/Redirects.js.map +1 -1
- package/dist/views/ScriptTagEditor.js +4 -4
- package/dist/views/ScriptTagEditor.js.map +1 -1
- package/dist/views/ScriptTags.d.ts.map +1 -1
- package/dist/views/ScriptTags.js +5 -5
- package/dist/views/ScriptTags.js.map +1 -1
- package/dist/views/page-builder/AIGenerateDialog.d.ts +1 -1
- package/dist/views/page-builder/AIGenerateDialog.d.ts.map +1 -1
- package/dist/views/page-builder/AIGenerateDialog.js +23 -23
- package/dist/views/page-builder/AIGenerateDialog.js.map +1 -1
- package/package.json +1 -1
- package/src/AdminRoot.tsx +35 -30
- package/src/__tests__/components/modal.render.test.tsx +57 -0
- package/src/components/FolderTree.tsx +32 -32
- package/src/components/MediaPickerModal.tsx +143 -119
- package/src/components/SchedulePublishDialog.tsx +118 -93
- package/src/components/SharePreviewLinkDialog.tsx +115 -93
- package/src/components/VersionHistory.tsx +98 -82
- package/src/components/ui/Modal.tsx +48 -39
- package/src/fields/BlockBuilderField.tsx +26 -19
- package/src/views/MediaBrowser.tsx +128 -120
- package/src/views/Pages/PagesListView.tsx +123 -115
- package/src/views/Posts/PostsListView.tsx +114 -110
- package/src/views/Redirects.tsx +48 -44
- package/src/views/ScriptTagEditor.tsx +40 -40
- package/src/views/ScriptTags.tsx +34 -30
- package/src/views/page-builder/AIGenerateDialog.tsx +347 -335
package/src/views/Redirects.tsx
CHANGED
|
@@ -102,7 +102,7 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
102
102
|
<button
|
|
103
103
|
type="button"
|
|
104
104
|
onClick={() => setSortConfig(toggleSort(sortConfig, sortKey))}
|
|
105
|
-
className="flex items-center gap-1 text-xs font-medium
|
|
105
|
+
className="text-foreground hover:text-foreground flex items-center gap-1 text-xs font-medium transition-colors"
|
|
106
106
|
>
|
|
107
107
|
{label}
|
|
108
108
|
{active ? (
|
|
@@ -112,7 +112,7 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
112
112
|
<ArrowDown className="h-3 w-3" />
|
|
113
113
|
)
|
|
114
114
|
) : (
|
|
115
|
-
<ArrowUpDown className="h-3 w-3
|
|
115
|
+
<ArrowUpDown className="text-muted-foreground h-3 w-3" />
|
|
116
116
|
)}
|
|
117
117
|
</button>
|
|
118
118
|
)
|
|
@@ -121,7 +121,7 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
121
121
|
if (loading) {
|
|
122
122
|
return (
|
|
123
123
|
<div className="flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8">
|
|
124
|
-
<Loader2 className="h-6 w-6 animate-spin
|
|
124
|
+
<Loader2 className="text-primary h-6 w-6 animate-spin" />
|
|
125
125
|
</div>
|
|
126
126
|
)
|
|
127
127
|
}
|
|
@@ -129,12 +129,12 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
129
129
|
return (
|
|
130
130
|
<div className="p-3 pr-6 sm:p-4 sm:pr-8">
|
|
131
131
|
{error && (
|
|
132
|
-
<div className="mb-4 flex items-center gap-3 rounded-lg border
|
|
133
|
-
<AlertTriangle className="h-5 w-5 shrink-0
|
|
134
|
-
<span className="flex-1 text-sm
|
|
132
|
+
<div className="border-destructive/30 bg-destructive/10 mb-4 flex items-center gap-3 rounded-lg border p-3">
|
|
133
|
+
<AlertTriangle className="text-destructive h-5 w-5 shrink-0" />
|
|
134
|
+
<span className="text-destructive flex-1 text-sm">{error}</span>
|
|
135
135
|
<button
|
|
136
136
|
onClick={refetch}
|
|
137
|
-
className="rounded-lg border
|
|
137
|
+
className="border-destructive/40 text-destructive hover:bg-destructive/15 rounded-lg border px-3 py-1 text-sm transition-colors"
|
|
138
138
|
>
|
|
139
139
|
Retry
|
|
140
140
|
</button>
|
|
@@ -143,35 +143,37 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
143
143
|
|
|
144
144
|
<div className="mb-4 flex items-center justify-between">
|
|
145
145
|
<div>
|
|
146
|
-
<h1 className="mb-1 text-xl font-semibold
|
|
147
|
-
<p className="text-
|
|
146
|
+
<h1 className="text-foreground mb-1 text-xl font-semibold sm:text-2xl">Redirects</h1>
|
|
147
|
+
<p className="text-muted-foreground text-sm">
|
|
148
|
+
{filteredAndSorted.length} total redirects
|
|
149
|
+
</p>
|
|
148
150
|
</div>
|
|
149
151
|
<button
|
|
150
152
|
type="button"
|
|
151
153
|
onClick={() => setShowAddDialog(true)}
|
|
152
|
-
className="flex items-center gap-2 rounded-lg
|
|
154
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 rounded-lg px-4 py-2 text-sm transition-colors"
|
|
153
155
|
>
|
|
154
156
|
<Plus className="h-4 w-4" />
|
|
155
157
|
Add Redirect
|
|
156
158
|
</button>
|
|
157
159
|
</div>
|
|
158
160
|
|
|
159
|
-
<div className="mb-4 rounded-lg border
|
|
161
|
+
<div className="border-border bg-card mb-4 rounded-lg border">
|
|
160
162
|
<div className="flex flex-col gap-3 p-3 sm:flex-row">
|
|
161
163
|
<div className="relative flex-1">
|
|
162
|
-
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2
|
|
164
|
+
<Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
|
|
163
165
|
<input
|
|
164
166
|
type="text"
|
|
165
167
|
placeholder="Search by source or destination URL..."
|
|
166
168
|
value={searchQuery}
|
|
167
169
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
168
|
-
className="w-full rounded-lg border
|
|
170
|
+
className="border-input focus:ring-ring w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus:ring-2 focus:outline-none"
|
|
169
171
|
/>
|
|
170
172
|
</div>
|
|
171
173
|
<select
|
|
172
174
|
value={filterType}
|
|
173
175
|
onChange={(e) => setFilterType(e.target.value)}
|
|
174
|
-
className="rounded-lg border
|
|
176
|
+
className="border-input focus:ring-ring rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
175
177
|
>
|
|
176
178
|
<option value="all">All Types</option>
|
|
177
179
|
<option value="301">301 (Permanent)</option>
|
|
@@ -182,17 +184,17 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
182
184
|
|
|
183
185
|
{filteredAndSorted.length === 0 && !loading ? (
|
|
184
186
|
<div className="flex flex-col items-center justify-center py-16 text-center">
|
|
185
|
-
<div className="mb-4 flex h-12 w-12 items-center justify-center rounded-full
|
|
186
|
-
<ArrowRightLeft className="h-6 w-6
|
|
187
|
+
<div className="bg-muted mb-4 flex h-12 w-12 items-center justify-center rounded-full">
|
|
188
|
+
<ArrowRightLeft className="text-muted-foreground h-6 w-6" />
|
|
187
189
|
</div>
|
|
188
|
-
<h3 className="mb-1 text-sm font-medium
|
|
189
|
-
<p className="text-
|
|
190
|
+
<h3 className="text-foreground mb-1 text-sm font-medium">No redirects yet</h3>
|
|
191
|
+
<p className="text-muted-foreground text-sm">Add your first redirect to get started.</p>
|
|
190
192
|
</div>
|
|
191
193
|
) : (
|
|
192
|
-
<div className="
|
|
194
|
+
<div className="border-border bg-card rounded-lg border">
|
|
193
195
|
<div className="overflow-x-auto">
|
|
194
196
|
<table className="w-full">
|
|
195
|
-
<thead className="border-
|
|
197
|
+
<thead className="border-border bg-muted border-b">
|
|
196
198
|
<tr>
|
|
197
199
|
<th className="px-4 py-2 text-left">
|
|
198
200
|
<SortHeader label="Source URL" sortKey="from" />
|
|
@@ -209,19 +211,21 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
209
211
|
<th className="px-4 py-2 text-left">
|
|
210
212
|
<SortHeader label="Status" sortKey="active" />
|
|
211
213
|
</th>
|
|
212
|
-
<th className="px-4 py-2 text-left text-xs font-medium
|
|
214
|
+
<th className="text-foreground px-4 py-2 text-left text-xs font-medium">
|
|
215
|
+
Actions
|
|
216
|
+
</th>
|
|
213
217
|
</tr>
|
|
214
218
|
</thead>
|
|
215
|
-
<tbody className="divide-
|
|
219
|
+
<tbody className="divide-border divide-y">
|
|
216
220
|
{filteredAndSorted.map((redirect) => (
|
|
217
|
-
<tr key={redirect.id} className="
|
|
221
|
+
<tr key={redirect.id} className="hover:bg-muted transition-colors">
|
|
218
222
|
<td className="px-4 py-3">
|
|
219
|
-
<code className="
|
|
223
|
+
<code className="bg-muted text-foreground rounded px-2 py-1 text-xs">
|
|
220
224
|
{redirect.from}
|
|
221
225
|
</code>
|
|
222
226
|
</td>
|
|
223
227
|
<td className="px-4 py-3">
|
|
224
|
-
<code className="
|
|
228
|
+
<code className="bg-muted text-foreground rounded px-2 py-1 text-xs">
|
|
225
229
|
{redirect.to}
|
|
226
230
|
</code>
|
|
227
231
|
</td>
|
|
@@ -229,22 +233,22 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
229
233
|
<span
|
|
230
234
|
className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${
|
|
231
235
|
redirect.type === '301'
|
|
232
|
-
? 'bg-
|
|
233
|
-
: 'bg-
|
|
236
|
+
? 'bg-primary/15 text-primary'
|
|
237
|
+
: 'bg-accent text-accent-foreground'
|
|
234
238
|
}`}
|
|
235
239
|
>
|
|
236
240
|
{redirect.type}
|
|
237
241
|
</span>
|
|
238
242
|
</td>
|
|
239
|
-
<td className="px-4 py-3 text-sm
|
|
243
|
+
<td className="text-muted-foreground px-4 py-3 text-sm">
|
|
240
244
|
{redirect.hits.toLocaleString()}
|
|
241
245
|
</td>
|
|
242
246
|
<td className="px-4 py-3">
|
|
243
247
|
<span
|
|
244
248
|
className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${
|
|
245
249
|
redirect.active
|
|
246
|
-
? 'bg-
|
|
247
|
-
: 'bg-
|
|
250
|
+
? 'bg-success/15 text-success'
|
|
251
|
+
: 'bg-muted text-foreground'
|
|
248
252
|
}`}
|
|
249
253
|
>
|
|
250
254
|
{redirect.active ? 'Active' : 'Inactive'}
|
|
@@ -255,18 +259,18 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
255
259
|
<button
|
|
256
260
|
type="button"
|
|
257
261
|
onClick={() => onNavigate?.(`/redirects/${redirect.id}/edit`)}
|
|
258
|
-
className="rounded p-1.5 transition-colors
|
|
262
|
+
className="hover:bg-muted rounded p-1.5 transition-colors"
|
|
259
263
|
aria-label="Edit redirect"
|
|
260
264
|
>
|
|
261
|
-
<Pencil className="h-4 w-4
|
|
265
|
+
<Pencil className="text-muted-foreground h-4 w-4" />
|
|
262
266
|
</button>
|
|
263
267
|
<button
|
|
264
268
|
type="button"
|
|
265
269
|
onClick={() => handleDelete(redirect.id)}
|
|
266
|
-
className="rounded p-1.5 transition-colors
|
|
270
|
+
className="hover:bg-muted rounded p-1.5 transition-colors"
|
|
267
271
|
aria-label="Delete redirect"
|
|
268
272
|
>
|
|
269
|
-
<Trash2 className="h-4 w-4
|
|
273
|
+
<Trash2 className="text-destructive h-4 w-4" />
|
|
270
274
|
</button>
|
|
271
275
|
</div>
|
|
272
276
|
</td>
|
|
@@ -281,24 +285,24 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
281
285
|
<Dialog.Root open={showAddDialog} onOpenChange={setShowAddDialog}>
|
|
282
286
|
<Dialog.Portal container={adminPortalContainer()}>
|
|
283
287
|
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/50" />
|
|
284
|
-
<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
|
|
285
|
-
<Dialog.Title className="mb-4 text-lg font-semibold
|
|
288
|
+
<Dialog.Content className="bg-card fixed top-1/2 left-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg p-6 shadow-lg">
|
|
289
|
+
<Dialog.Title className="text-foreground mb-4 text-lg font-semibold">
|
|
286
290
|
Add Redirect
|
|
287
291
|
</Dialog.Title>
|
|
288
292
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
289
293
|
<div>
|
|
290
|
-
<label className="mb-1 block text-sm font-medium
|
|
294
|
+
<label className="text-foreground mb-1 block text-sm font-medium">Source URL</label>
|
|
291
295
|
<input
|
|
292
296
|
type="text"
|
|
293
297
|
value={newRedirect.source}
|
|
294
298
|
onChange={(e) => setNewRedirect({ ...newRedirect, source: e.target.value })}
|
|
295
299
|
placeholder="/old-page"
|
|
296
|
-
className="w-full rounded-lg border
|
|
300
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
297
301
|
required
|
|
298
302
|
/>
|
|
299
303
|
</div>
|
|
300
304
|
<div>
|
|
301
|
-
<label className="mb-1 block text-sm font-medium
|
|
305
|
+
<label className="text-foreground mb-1 block text-sm font-medium">
|
|
302
306
|
Destination URL
|
|
303
307
|
</label>
|
|
304
308
|
<input
|
|
@@ -306,12 +310,12 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
306
310
|
value={newRedirect.destination}
|
|
307
311
|
onChange={(e) => setNewRedirect({ ...newRedirect, destination: e.target.value })}
|
|
308
312
|
placeholder="/new-page"
|
|
309
|
-
className="w-full rounded-lg border
|
|
313
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
310
314
|
required
|
|
311
315
|
/>
|
|
312
316
|
</div>
|
|
313
317
|
<div>
|
|
314
|
-
<label className="mb-1 block text-sm font-medium
|
|
318
|
+
<label className="text-foreground mb-1 block text-sm font-medium">
|
|
315
319
|
Redirect Type
|
|
316
320
|
</label>
|
|
317
321
|
<select
|
|
@@ -319,7 +323,7 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
319
323
|
onChange={(e) =>
|
|
320
324
|
setNewRedirect({ ...newRedirect, type: e.target.value as '301' | '302' })
|
|
321
325
|
}
|
|
322
|
-
className="w-full rounded-lg border
|
|
326
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
323
327
|
>
|
|
324
328
|
<option value="301">301 (Permanent)</option>
|
|
325
329
|
<option value="302">302 (Temporary)</option>
|
|
@@ -329,14 +333,14 @@ export function Redirects({ onNavigate }: RedirectsProps) {
|
|
|
329
333
|
<Dialog.Close asChild>
|
|
330
334
|
<button
|
|
331
335
|
type="button"
|
|
332
|
-
className="rounded-lg border
|
|
336
|
+
className="border-input hover:bg-muted rounded-lg border px-4 py-2 text-sm transition-colors"
|
|
333
337
|
>
|
|
334
338
|
Cancel
|
|
335
339
|
</button>
|
|
336
340
|
</Dialog.Close>
|
|
337
341
|
<button
|
|
338
342
|
type="submit"
|
|
339
|
-
className="
|
|
343
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-lg px-4 py-2 text-sm transition-colors"
|
|
340
344
|
>
|
|
341
345
|
Add Redirect
|
|
342
346
|
</button>
|
|
@@ -111,7 +111,7 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
111
111
|
if (loading && !isNew) {
|
|
112
112
|
return (
|
|
113
113
|
<div className="flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8">
|
|
114
|
-
<Loader2 className="h-6 w-6 animate-spin
|
|
114
|
+
<Loader2 className="text-primary h-6 w-6 animate-spin" />
|
|
115
115
|
</div>
|
|
116
116
|
)
|
|
117
117
|
}
|
|
@@ -119,9 +119,9 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
119
119
|
return (
|
|
120
120
|
<div className="max-w-3xl p-3 pr-6 sm:p-4 sm:pr-8">
|
|
121
121
|
{error && (
|
|
122
|
-
<div className="mb-4 flex items-center gap-3 rounded-lg border
|
|
123
|
-
<AlertTriangle className="h-5 w-5 shrink-0
|
|
124
|
-
<span className="flex-1 text-sm
|
|
122
|
+
<div className="border-destructive/30 bg-destructive/10 mb-4 flex items-center gap-3 rounded-lg border p-3">
|
|
123
|
+
<AlertTriangle className="text-destructive h-5 w-5 shrink-0" />
|
|
124
|
+
<span className="text-destructive flex-1 text-sm">{error}</span>
|
|
125
125
|
</div>
|
|
126
126
|
)}
|
|
127
127
|
|
|
@@ -129,20 +129,20 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
129
129
|
<button
|
|
130
130
|
type="button"
|
|
131
131
|
onClick={() => onNavigate?.('/script-tags')}
|
|
132
|
-
className="mb-3 flex items-center gap-1.5 text-sm
|
|
132
|
+
className="text-muted-foreground hover:text-foreground mb-3 flex items-center gap-1.5 text-sm transition-colors"
|
|
133
133
|
>
|
|
134
134
|
<ArrowLeft className="h-4 w-4" />
|
|
135
135
|
Back to Script Tags
|
|
136
136
|
</button>
|
|
137
|
-
<h1 className="text-2xl font-semibold
|
|
137
|
+
<h1 className="text-foreground text-2xl font-semibold">
|
|
138
138
|
{isNew ? 'New Script Tag' : 'Edit Script Tag'}
|
|
139
139
|
</h1>
|
|
140
140
|
</div>
|
|
141
141
|
|
|
142
|
-
<div className="mb-6 rounded-lg border
|
|
142
|
+
<div className="border-warning/30 bg-warning/10 mb-6 rounded-lg border p-3">
|
|
143
143
|
<div className="flex items-start gap-2">
|
|
144
|
-
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0
|
|
145
|
-
<p className="text-
|
|
144
|
+
<AlertTriangle className="text-warning mt-0.5 h-4 w-4 shrink-0" />
|
|
145
|
+
<p className="text-warning text-sm">
|
|
146
146
|
This code will run on public pages matching the scope below. Only add trusted code from
|
|
147
147
|
verified sources (e.g. Google Analytics, Meta Pixel).
|
|
148
148
|
</p>
|
|
@@ -150,49 +150,49 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
150
150
|
</div>
|
|
151
151
|
|
|
152
152
|
<div className="space-y-6">
|
|
153
|
-
<div className="space-y-4 rounded-lg border
|
|
153
|
+
<div className="border-border bg-card space-y-4 rounded-lg border p-4">
|
|
154
154
|
<div>
|
|
155
|
-
<label className="mb-1 block text-sm font-medium
|
|
155
|
+
<label className="text-foreground mb-1 block text-sm font-medium">Name</label>
|
|
156
156
|
<input
|
|
157
157
|
type="text"
|
|
158
158
|
value={name}
|
|
159
159
|
onChange={(e) => setName(e.target.value)}
|
|
160
160
|
placeholder="e.g. Google Tag Manager, Facebook Pixel"
|
|
161
|
-
className="w-full rounded-lg border
|
|
161
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
162
162
|
/>
|
|
163
163
|
</div>
|
|
164
164
|
|
|
165
165
|
<div>
|
|
166
|
-
<label className="mb-1 block text-sm font-medium
|
|
166
|
+
<label className="text-foreground mb-1 block text-sm font-medium">Code</label>
|
|
167
167
|
<textarea
|
|
168
168
|
value={code}
|
|
169
169
|
onChange={(e) => setCode(e.target.value)}
|
|
170
170
|
placeholder="Paste your HTML/JavaScript snippet here..."
|
|
171
171
|
rows={10}
|
|
172
|
-
className="w-full rounded-lg border
|
|
172
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 font-mono text-sm focus:ring-2 focus:outline-none"
|
|
173
173
|
spellCheck={false}
|
|
174
174
|
/>
|
|
175
|
-
<p className="mt-1 text-xs
|
|
175
|
+
<p className="text-muted-foreground mt-1 text-xs">
|
|
176
176
|
Paste the full code snippet including {'<script>'} tags.
|
|
177
177
|
</p>
|
|
178
178
|
</div>
|
|
179
179
|
</div>
|
|
180
180
|
|
|
181
|
-
<div className="space-y-4 rounded-lg border
|
|
182
|
-
<h3 className="text-sm font-semibold
|
|
181
|
+
<div className="border-border bg-card space-y-4 rounded-lg border p-4">
|
|
182
|
+
<h3 className="text-foreground text-sm font-semibold">Placement & Scope</h3>
|
|
183
183
|
|
|
184
184
|
<div>
|
|
185
|
-
<label className="mb-1 block text-sm font-medium
|
|
185
|
+
<label className="text-foreground mb-1 block text-sm font-medium">Placement</label>
|
|
186
186
|
<select
|
|
187
187
|
value={placement}
|
|
188
188
|
onChange={(e) => setPlacement(e.target.value)}
|
|
189
|
-
className="w-full rounded-lg border
|
|
189
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
190
190
|
>
|
|
191
191
|
<option value="head">Head — inside {'<head>'}</option>
|
|
192
192
|
<option value="body_open">Body Open — right after {'<body>'}</option>
|
|
193
193
|
<option value="body_close">Body Close — before {'</body>'}</option>
|
|
194
194
|
</select>
|
|
195
|
-
<p className="mt-1 text-xs
|
|
195
|
+
<p className="text-muted-foreground mt-1 text-xs">
|
|
196
196
|
{placement === 'head' && 'Best for analytics scripts, meta tags, and custom CSS.'}
|
|
197
197
|
{placement === 'body_open' && 'Best for GTM noscript tags and early-loading scripts.'}
|
|
198
198
|
{placement === 'body_close' &&
|
|
@@ -201,11 +201,11 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
201
201
|
</div>
|
|
202
202
|
|
|
203
203
|
<div>
|
|
204
|
-
<label className="mb-1 block text-sm font-medium
|
|
204
|
+
<label className="text-foreground mb-1 block text-sm font-medium">Scope</label>
|
|
205
205
|
<select
|
|
206
206
|
value={scope}
|
|
207
207
|
onChange={(e) => setScope(e.target.value)}
|
|
208
|
-
className="w-full rounded-lg border
|
|
208
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
209
209
|
>
|
|
210
210
|
<option value="site">Entire Website</option>
|
|
211
211
|
<option value="parents">Specific Parent Pages (includes child pages)</option>
|
|
@@ -215,7 +215,7 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
215
215
|
|
|
216
216
|
{scope !== 'site' && (
|
|
217
217
|
<div>
|
|
218
|
-
<label className="mb-1 block text-sm font-medium
|
|
218
|
+
<label className="text-foreground mb-1 block text-sm font-medium">
|
|
219
219
|
{scope === 'parents' ? 'Parent Paths' : 'URL Paths'}
|
|
220
220
|
</label>
|
|
221
221
|
<div className="flex gap-2">
|
|
@@ -230,18 +230,18 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
230
230
|
}
|
|
231
231
|
}}
|
|
232
232
|
placeholder="/services"
|
|
233
|
-
className="flex-1 rounded-lg border
|
|
233
|
+
className="border-input focus:ring-ring flex-1 rounded-lg border px-3 py-2 font-mono text-sm focus:ring-2 focus:outline-none"
|
|
234
234
|
/>
|
|
235
235
|
<button
|
|
236
236
|
type="button"
|
|
237
237
|
onClick={addPath}
|
|
238
|
-
className="rounded-lg border
|
|
238
|
+
className="border-input hover:bg-muted rounded-lg border px-3 py-2 text-sm transition-colors"
|
|
239
239
|
>
|
|
240
240
|
<Plus className="h-4 w-4" />
|
|
241
241
|
</button>
|
|
242
242
|
</div>
|
|
243
243
|
{scope === 'parents' && (
|
|
244
|
-
<p className="mt-1 text-xs
|
|
244
|
+
<p className="text-muted-foreground mt-1 text-xs">
|
|
245
245
|
This tag will also apply to all child pages under each path.
|
|
246
246
|
</p>
|
|
247
247
|
)}
|
|
@@ -250,13 +250,13 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
250
250
|
{targetPaths.map((p) => (
|
|
251
251
|
<span
|
|
252
252
|
key={p}
|
|
253
|
-
className="inline-flex items-center gap-1 rounded-full
|
|
253
|
+
className="bg-muted text-foreground inline-flex items-center gap-1 rounded-full px-3 py-1 font-mono text-xs"
|
|
254
254
|
>
|
|
255
255
|
{p}
|
|
256
256
|
<button
|
|
257
257
|
type="button"
|
|
258
258
|
onClick={() => removePath(p)}
|
|
259
|
-
className="
|
|
259
|
+
className="text-muted-foreground hover:text-muted-foreground ml-0.5"
|
|
260
260
|
>
|
|
261
261
|
<X className="h-3 w-3" />
|
|
262
262
|
</button>
|
|
@@ -269,29 +269,29 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
269
269
|
|
|
270
270
|
<div className="grid grid-cols-2 gap-4">
|
|
271
271
|
<div>
|
|
272
|
-
<label className="mb-1 block text-sm font-medium
|
|
272
|
+
<label className="text-foreground mb-1 block text-sm font-medium">Priority</label>
|
|
273
273
|
<input
|
|
274
274
|
type="number"
|
|
275
275
|
value={priority}
|
|
276
276
|
onChange={(e) => setPriority(parseInt(e.target.value, 10) || 0)}
|
|
277
277
|
min={0}
|
|
278
|
-
className="w-full rounded-lg border
|
|
278
|
+
className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
|
|
279
279
|
/>
|
|
280
|
-
<p className="mt-1 text-xs
|
|
280
|
+
<p className="text-muted-foreground mt-1 text-xs">
|
|
281
281
|
Lower numbers load first (e.g. 1 = first, 100 = default)
|
|
282
282
|
</p>
|
|
283
283
|
</div>
|
|
284
284
|
<div>
|
|
285
|
-
<label className="mb-1 block text-sm font-medium
|
|
285
|
+
<label className="text-foreground mb-1 block text-sm font-medium">Enabled</label>
|
|
286
286
|
<div className="pt-2">
|
|
287
287
|
<button
|
|
288
288
|
type="button"
|
|
289
289
|
onClick={() => setEnabled(!enabled)}
|
|
290
|
-
className={`relative h-6 w-11 shrink-0 rounded-full transition-colors ${enabled ? 'bg-
|
|
290
|
+
className={`relative h-6 w-11 shrink-0 rounded-full transition-colors ${enabled ? 'bg-primary' : 'bg-switch-background'}`}
|
|
291
291
|
aria-pressed={enabled}
|
|
292
292
|
>
|
|
293
293
|
<span
|
|
294
|
-
className={`absolute top-0.5 block h-5 w-5 rounded-full
|
|
294
|
+
className={`bg-card absolute top-0.5 block h-5 w-5 rounded-full transition-transform ${
|
|
295
295
|
enabled ? 'translate-x-[22px]' : 'translate-x-0.5'
|
|
296
296
|
}`}
|
|
297
297
|
/>
|
|
@@ -307,19 +307,19 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
307
307
|
<>
|
|
308
308
|
{showDeleteConfirm ? (
|
|
309
309
|
<div className="flex items-center gap-2">
|
|
310
|
-
<span className="text-
|
|
310
|
+
<span className="text-destructive text-sm">Delete this tag?</span>
|
|
311
311
|
<button
|
|
312
312
|
type="button"
|
|
313
313
|
onClick={handleDelete}
|
|
314
314
|
disabled={deleting}
|
|
315
|
-
className="
|
|
315
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90 rounded-lg px-3 py-1.5 text-sm disabled:opacity-50"
|
|
316
316
|
>
|
|
317
317
|
{deleting ? 'Deleting...' : 'Confirm'}
|
|
318
318
|
</button>
|
|
319
319
|
<button
|
|
320
320
|
type="button"
|
|
321
321
|
onClick={() => setShowDeleteConfirm(false)}
|
|
322
|
-
className="rounded-lg border
|
|
322
|
+
className="border-input hover:bg-muted rounded-lg border px-3 py-1.5 text-sm"
|
|
323
323
|
>
|
|
324
324
|
Cancel
|
|
325
325
|
</button>
|
|
@@ -328,7 +328,7 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
328
328
|
<button
|
|
329
329
|
type="button"
|
|
330
330
|
onClick={() => setShowDeleteConfirm(true)}
|
|
331
|
-
className="flex items-center gap-1.5 text-sm
|
|
331
|
+
className="text-destructive hover:text-destructive flex items-center gap-1.5 text-sm transition-colors"
|
|
332
332
|
>
|
|
333
333
|
<Trash2 className="h-4 w-4" />
|
|
334
334
|
Delete
|
|
@@ -341,7 +341,7 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
341
341
|
<button
|
|
342
342
|
type="button"
|
|
343
343
|
onClick={() => onNavigate?.('/script-tags')}
|
|
344
|
-
className="rounded-lg border
|
|
344
|
+
className="border-input text-foreground hover:bg-muted rounded-lg border px-4 py-2 text-sm transition-colors"
|
|
345
345
|
>
|
|
346
346
|
Cancel
|
|
347
347
|
</button>
|
|
@@ -349,7 +349,7 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
|
|
|
349
349
|
type="button"
|
|
350
350
|
onClick={handleSave}
|
|
351
351
|
disabled={saving}
|
|
352
|
-
className="
|
|
352
|
+
className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-lg px-6 py-2 text-sm transition-colors disabled:opacity-50"
|
|
353
353
|
>
|
|
354
354
|
{saving ? 'Saving...' : isNew ? 'Create Tag' : 'Save Changes'}
|
|
355
355
|
</button>
|