@actuate-media/cms-admin 0.4.0 → 0.6.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.
Files changed (39) hide show
  1. package/dist/AdminRoot.d.ts.map +1 -1
  2. package/dist/AdminRoot.js +22 -0
  3. package/dist/AdminRoot.js.map +1 -1
  4. package/dist/actuate-admin.css +1 -1
  5. package/dist/components/Breadcrumbs.d.ts.map +1 -1
  6. package/dist/components/Breadcrumbs.js +1 -0
  7. package/dist/components/Breadcrumbs.js.map +1 -1
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/layout/Sidebar.d.ts.map +1 -1
  13. package/dist/layout/Sidebar.js +2 -2
  14. package/dist/layout/Sidebar.js.map +1 -1
  15. package/dist/views/ForgotPassword.d.ts +5 -0
  16. package/dist/views/ForgotPassword.d.ts.map +1 -0
  17. package/dist/views/ForgotPassword.js +41 -0
  18. package/dist/views/ForgotPassword.js.map +1 -0
  19. package/dist/views/ResetPassword.d.ts +6 -0
  20. package/dist/views/ResetPassword.d.ts.map +1 -0
  21. package/dist/views/ResetPassword.js +46 -0
  22. package/dist/views/ResetPassword.js.map +1 -0
  23. package/dist/views/ScriptTagEditor.d.ts +6 -0
  24. package/dist/views/ScriptTagEditor.d.ts.map +1 -0
  25. package/dist/views/ScriptTagEditor.js +109 -0
  26. package/dist/views/ScriptTagEditor.js.map +1 -0
  27. package/dist/views/ScriptTags.d.ts +5 -0
  28. package/dist/views/ScriptTags.d.ts.map +1 -0
  29. package/dist/views/ScriptTags.js +54 -0
  30. package/dist/views/ScriptTags.js.map +1 -0
  31. package/package.json +5 -3
  32. package/src/AdminRoot.tsx +25 -0
  33. package/src/components/Breadcrumbs.tsx +1 -0
  34. package/src/index.ts +4 -0
  35. package/src/layout/Sidebar.tsx +2 -0
  36. package/src/views/ForgotPassword.tsx +136 -0
  37. package/src/views/ResetPassword.tsx +192 -0
  38. package/src/views/ScriptTagEditor.tsx +361 -0
  39. package/src/views/ScriptTags.tsx +174 -0
@@ -0,0 +1,174 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Code2, Plus, Loader2, AlertTriangle } from 'lucide-react';
5
+ import { toast } from 'sonner';
6
+ import { useApiData } from '../lib/useApiData.js';
7
+ import { cmsApi } from '../lib/api.js';
8
+
9
+ interface ScriptTag {
10
+ id: string;
11
+ name: string;
12
+ code: string;
13
+ placement: string;
14
+ scope: string;
15
+ targetPaths: string[];
16
+ priority: number;
17
+ enabled: boolean;
18
+ createdAt: string;
19
+ updatedAt: string;
20
+ }
21
+
22
+ export interface ScriptTagsProps {
23
+ onNavigate?: (path: string) => void;
24
+ }
25
+
26
+ const PLACEMENT_LABELS: Record<string, string> = {
27
+ head: 'Head',
28
+ body_open: 'Body Open',
29
+ body_close: 'Body Close',
30
+ };
31
+
32
+ const PLACEMENT_COLORS: Record<string, string> = {
33
+ head: 'bg-purple-100 text-purple-700',
34
+ body_open: 'bg-blue-100 text-blue-700',
35
+ body_close: 'bg-green-100 text-green-700',
36
+ };
37
+
38
+ function scopeLabel(tag: ScriptTag): string {
39
+ if (tag.scope === 'site') return 'Entire Site';
40
+ if (tag.scope === 'parents') {
41
+ const count = tag.targetPaths?.length ?? 0;
42
+ return `${count} parent path${count !== 1 ? 's' : ''} + children`;
43
+ }
44
+ if (tag.scope === 'urls') {
45
+ const count = tag.targetPaths?.length ?? 0;
46
+ return `${count} URL${count !== 1 ? 's' : ''}`;
47
+ }
48
+ return tag.scope;
49
+ }
50
+
51
+ export function ScriptTags({ onNavigate }: ScriptTagsProps) {
52
+ const { data, loading, error, refetch } = useApiData<ScriptTag[]>('/script-tags');
53
+ const [togglingId, setTogglingId] = useState<string | null>(null);
54
+
55
+ const tags = data ?? [];
56
+
57
+ const toggleEnabled = async (tag: ScriptTag) => {
58
+ setTogglingId(tag.id);
59
+ const res = await cmsApi(`/script-tags/${tag.id}`, {
60
+ method: 'PUT',
61
+ body: JSON.stringify({ enabled: !tag.enabled }),
62
+ });
63
+ setTogglingId(null);
64
+ if (res.error) {
65
+ toast.error(res.error);
66
+ } else {
67
+ refetch();
68
+ }
69
+ };
70
+
71
+ if (loading) {
72
+ return (
73
+ <div className="p-3 pr-6 sm:p-4 sm:pr-8 flex items-center justify-center h-64">
74
+ <Loader2 className="w-6 h-6 animate-spin text-blue-600" />
75
+ </div>
76
+ );
77
+ }
78
+
79
+ return (
80
+ <div className="p-3 pr-6 sm:p-4 sm:pr-8">
81
+ {error && (
82
+ <div className="mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
83
+ <AlertTriangle className="w-5 h-5 text-red-600 shrink-0" />
84
+ <span className="text-sm text-red-800 flex-1">{error}</span>
85
+ <button onClick={refetch} className="px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors">Retry</button>
86
+ </div>
87
+ )}
88
+
89
+ <div className="mb-4 flex items-center justify-between">
90
+ <div>
91
+ <h1 className="mb-1 text-2xl font-semibold text-gray-900">Script Tags</h1>
92
+ <p className="text-sm text-gray-600">Manage tracking codes, analytics, and custom scripts injected into your site</p>
93
+ </div>
94
+ <button
95
+ type="button"
96
+ onClick={() => onNavigate?.('/script-tags/new')}
97
+ className="flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
98
+ >
99
+ <Plus className="w-4 h-4" />
100
+ New Tag
101
+ </button>
102
+ </div>
103
+
104
+ {tags.length === 0 && !error ? (
105
+ <div className="rounded-lg border border-gray-200 bg-white p-12 text-center">
106
+ <Code2 className="mx-auto mb-3 h-10 w-10 text-gray-300" />
107
+ <h3 className="text-sm font-semibold text-gray-900">No script tags yet</h3>
108
+ <p className="mt-1 text-sm text-gray-500">
109
+ Add tracking codes like Google Analytics, Tag Manager, or Facebook Pixel.
110
+ </p>
111
+ <button
112
+ type="button"
113
+ onClick={() => onNavigate?.('/script-tags/new')}
114
+ className="mt-4 inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
115
+ >
116
+ <Plus className="w-4 h-4" />
117
+ Add Your First Tag
118
+ </button>
119
+ </div>
120
+ ) : (
121
+ <div className="rounded-lg border border-gray-200 bg-white overflow-hidden">
122
+ <table className="w-full text-sm">
123
+ <thead>
124
+ <tr className="border-b border-gray-200 bg-gray-50">
125
+ <th className="px-4 py-3 text-left font-medium text-gray-600">Name</th>
126
+ <th className="px-4 py-3 text-left font-medium text-gray-600">Placement</th>
127
+ <th className="px-4 py-3 text-left font-medium text-gray-600">Scope</th>
128
+ <th className="px-4 py-3 text-center font-medium text-gray-600">Priority</th>
129
+ <th className="px-4 py-3 text-center font-medium text-gray-600">Enabled</th>
130
+ </tr>
131
+ </thead>
132
+ <tbody>
133
+ {tags.map((tag) => (
134
+ <tr key={tag.id} className="border-b border-gray-100 last:border-0 hover:bg-gray-50 transition-colors">
135
+ <td className="px-4 py-3">
136
+ <button
137
+ type="button"
138
+ onClick={() => onNavigate?.(`/script-tags/${tag.id}`)}
139
+ className="font-medium text-blue-600 hover:text-blue-800 hover:underline"
140
+ >
141
+ {tag.name}
142
+ </button>
143
+ </td>
144
+ <td className="px-4 py-3">
145
+ <span className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${PLACEMENT_COLORS[tag.placement] ?? 'bg-gray-100 text-gray-700'}`}>
146
+ {PLACEMENT_LABELS[tag.placement] ?? tag.placement}
147
+ </span>
148
+ </td>
149
+ <td className="px-4 py-3 text-gray-600">{scopeLabel(tag)}</td>
150
+ <td className="px-4 py-3 text-center text-gray-600 font-mono">{tag.priority}</td>
151
+ <td className="px-4 py-3 text-center">
152
+ <button
153
+ type="button"
154
+ onClick={() => toggleEnabled(tag)}
155
+ disabled={togglingId === tag.id}
156
+ className={`relative h-6 w-11 shrink-0 rounded-full transition-colors ${tag.enabled ? 'bg-blue-600' : 'bg-gray-300'}`}
157
+ aria-pressed={tag.enabled}
158
+ >
159
+ <span
160
+ className={`absolute top-0.5 block h-5 w-5 rounded-full bg-white transition-transform ${
161
+ tag.enabled ? 'translate-x-[22px]' : 'translate-x-0.5'
162
+ }`}
163
+ />
164
+ </button>
165
+ </td>
166
+ </tr>
167
+ ))}
168
+ </tbody>
169
+ </table>
170
+ </div>
171
+ )}
172
+ </div>
173
+ );
174
+ }