@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 @@
1
+ {"version":3,"file":"ForgotPassword.js","sourceRoot":"","sources":["../../src/views/ForgotPassword.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface ResetPasswordProps {
2
+ onNavigate: (path: string) => void;
3
+ token: string | null;
4
+ }
5
+ export declare function ResetPassword({ onNavigate, token }: ResetPasswordProps): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=ResetPassword.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResetPassword.d.ts","sourceRoot":"","sources":["../../src/views/ResetPassword.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,kBAAkB,2CAoLtE"}
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Shield, Eye, EyeOff, Loader2, CheckCircle2, AlertTriangle, XCircle } from 'lucide-react';
5
+ import { cmsApi } from '../lib/api.js';
6
+ export function ResetPassword({ onNavigate, token }) {
7
+ const [password, setPassword] = useState('');
8
+ const [confirmPassword, setConfirmPassword] = useState('');
9
+ const [showPassword, setShowPassword] = useState(false);
10
+ const [submitting, setSubmitting] = useState(false);
11
+ const [success, setSuccess] = useState(false);
12
+ const [error, setError] = useState('');
13
+ if (!token) {
14
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-gray-50 px-4", children: _jsxs("div", { className: "w-full max-w-md", children: [_jsxs("div", { className: "text-center mb-8", children: [_jsx("div", { className: "mx-auto mb-4 w-14 h-14 bg-red-100 rounded-xl flex items-center justify-center", children: _jsx(XCircle, { className: "w-7 h-7 text-red-600" }) }), _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "Invalid Reset Link" }), _jsx("p", { className: "text-gray-600 mt-2", children: "This password reset link is invalid or has expired." })] }), _jsxs("div", { className: "bg-white rounded-xl border border-gray-200 p-6 shadow-sm text-center space-y-4", children: [_jsx("button", { type: "button", onClick: () => onNavigate('/forgot-password'), className: "w-full py-2.5 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors", children: "Request New Reset Link" }), _jsx("button", { type: "button", onClick: () => onNavigate('/login'), className: "w-full text-sm text-gray-600 hover:text-gray-800 transition-colors", children: "Back to Sign In" })] })] }) }));
15
+ }
16
+ const passwordsMatch = password === confirmPassword;
17
+ const meetsLength = password.length >= 8;
18
+ const canSubmit = password && confirmPassword && passwordsMatch && meetsLength && !submitting;
19
+ const handleSubmit = async (e) => {
20
+ e.preventDefault();
21
+ if (!canSubmit)
22
+ return;
23
+ setError('');
24
+ setSubmitting(true);
25
+ try {
26
+ const result = await cmsApi('/auth/reset-password', {
27
+ method: 'POST',
28
+ body: JSON.stringify({ token, password }),
29
+ });
30
+ if (result.error) {
31
+ setError(result.error);
32
+ }
33
+ else {
34
+ setSuccess(true);
35
+ }
36
+ }
37
+ catch {
38
+ setError('An unexpected error occurred. Please try again.');
39
+ }
40
+ finally {
41
+ setSubmitting(false);
42
+ }
43
+ };
44
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-gray-50 px-4", children: _jsxs("div", { className: "w-full max-w-md", children: [_jsxs("div", { className: "text-center mb-8", children: [_jsx("div", { className: "mx-auto mb-4 w-14 h-14 bg-blue-600 rounded-xl flex items-center justify-center", children: _jsx(Shield, { className: "w-7 h-7 text-white" }) }), _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: success ? 'Password Reset' : 'Choose New Password' }), _jsx("p", { className: "text-gray-600 mt-2", children: success ? 'Your password has been updated' : 'Enter your new password below' })] }), _jsx("div", { className: "bg-white rounded-xl border border-gray-200 p-6 shadow-sm space-y-5", children: success ? (_jsxs("div", { className: "text-center space-y-4", children: [_jsx("div", { className: "mx-auto w-12 h-12 bg-green-100 rounded-full flex items-center justify-center", children: _jsx(CheckCircle2, { className: "w-6 h-6 text-green-600" }) }), _jsx("p", { className: "text-sm text-gray-600", children: "Your password has been successfully reset. All existing sessions have been revoked for security." }), _jsx("button", { type: "button", onClick: () => onNavigate('/login'), className: "w-full py-2.5 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors", children: "Sign In with New Password" })] })) : (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [error && (_jsxs("div", { className: "flex items-start gap-3 p-3 bg-red-50 border border-red-200 rounded-lg", children: [_jsx(AlertTriangle, { className: "w-5 h-5 text-red-600 mt-0.5 shrink-0" }), _jsx("p", { className: "text-sm text-red-800", children: error })] })), _jsxs("div", { children: [_jsx("label", { htmlFor: "reset-password", className: "block text-sm font-medium text-gray-700 mb-1.5", children: "New Password" }), _jsxs("div", { className: "relative", children: [_jsx("input", { id: "reset-password", type: showPassword ? 'text' : 'password', value: password, onChange: (e) => setPassword(e.target.value), placeholder: "Enter new password", className: "w-full px-3 py-2.5 pr-10 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500", required: true, autoFocus: true, autoComplete: "new-password", minLength: 8 }), _jsx("button", { type: "button", onClick: () => setShowPassword(!showPassword), className: "absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600", tabIndex: -1, children: showPassword ? _jsx(EyeOff, { className: "w-4 h-4" }) : _jsx(Eye, { className: "w-4 h-4" }) })] }), password && !meetsLength && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: "Password must be at least 8 characters" }))] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "reset-confirm", className: "block text-sm font-medium text-gray-700 mb-1.5", children: "Confirm Password" }), _jsx("input", { id: "reset-confirm", type: showPassword ? 'text' : 'password', value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), placeholder: "Confirm new password", className: "w-full px-3 py-2.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500", required: true, autoComplete: "new-password" }), confirmPassword && !passwordsMatch && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: "Passwords do not match" }))] }), _jsx("button", { type: "submit", disabled: !canSubmit, className: "w-full py-2.5 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2", children: submitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), "Resetting..."] })) : ('Reset Password') })] })) })] }) }));
45
+ }
46
+ //# sourceMappingURL=ResetPassword.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResetPassword.js","sourceRoot":"","sources":["../../src/views/ResetPassword.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface ScriptTagEditorProps {
2
+ tagId?: string;
3
+ onNavigate?: (path: string) => void;
4
+ }
5
+ export declare function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=ScriptTagEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScriptTagEditor.d.ts","sourceRoot":"","sources":["../../src/views/ScriptTagEditor.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,oBAAoB,2CA2V1E"}
@@ -0,0 +1,109 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import { ArrowLeft, Loader2, AlertTriangle, Trash2, X, Plus } from 'lucide-react';
5
+ import { toast } from 'sonner';
6
+ import { useApiData } from '../lib/useApiData.js';
7
+ import { cmsApi } from '../lib/api.js';
8
+ export function ScriptTagEditor({ tagId, onNavigate }) {
9
+ const isNew = !tagId;
10
+ const { data, loading, error } = useApiData(tagId ? `/script-tags` : null);
11
+ const [name, setName] = useState('');
12
+ const [code, setCode] = useState('');
13
+ const [placement, setPlacement] = useState('head');
14
+ const [scope, setScope] = useState('site');
15
+ const [targetPaths, setTargetPaths] = useState([]);
16
+ const [pathInput, setPathInput] = useState('');
17
+ const [priority, setPriority] = useState(100);
18
+ const [enabled, setEnabled] = useState(true);
19
+ const [saving, setSaving] = useState(false);
20
+ const [deleting, setDeleting] = useState(false);
21
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
22
+ useEffect(() => {
23
+ if (data && tagId) {
24
+ const tags = Array.isArray(data) ? data : [];
25
+ const tag = tags.find((t) => t.id === tagId);
26
+ if (tag) {
27
+ setName(tag.name ?? '');
28
+ setCode(tag.code ?? '');
29
+ setPlacement(tag.placement ?? 'head');
30
+ setScope(tag.scope ?? 'site');
31
+ setTargetPaths(tag.targetPaths ?? []);
32
+ setPriority(tag.priority ?? 100);
33
+ setEnabled(tag.enabled ?? true);
34
+ }
35
+ }
36
+ }, [data, tagId]);
37
+ const addPath = () => {
38
+ const trimmed = pathInput.trim();
39
+ if (!trimmed)
40
+ return;
41
+ const normalized = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
42
+ if (!targetPaths.includes(normalized)) {
43
+ setTargetPaths([...targetPaths, normalized]);
44
+ }
45
+ setPathInput('');
46
+ };
47
+ const removePath = (path) => {
48
+ setTargetPaths(targetPaths.filter((p) => p !== path));
49
+ };
50
+ const handleSave = async () => {
51
+ if (!name.trim()) {
52
+ toast.error('Name is required');
53
+ return;
54
+ }
55
+ if (!code.trim()) {
56
+ toast.error('Code snippet is required');
57
+ return;
58
+ }
59
+ if (scope !== 'site' && targetPaths.length === 0) {
60
+ toast.error('At least one target path is required for this scope');
61
+ return;
62
+ }
63
+ setSaving(true);
64
+ const payload = {
65
+ name: name.trim(),
66
+ code,
67
+ placement,
68
+ scope,
69
+ targetPaths: scope === 'site' ? [] : targetPaths,
70
+ priority,
71
+ enabled,
72
+ };
73
+ const res = isNew
74
+ ? await cmsApi('/script-tags', { method: 'POST', body: JSON.stringify(payload) })
75
+ : await cmsApi(`/script-tags/${tagId}`, { method: 'PUT', body: JSON.stringify(payload) });
76
+ setSaving(false);
77
+ if (res.error) {
78
+ toast.error(res.error);
79
+ }
80
+ else {
81
+ toast.success(isNew ? 'Tag created' : 'Tag updated');
82
+ onNavigate?.('/script-tags');
83
+ }
84
+ };
85
+ const handleDelete = async () => {
86
+ if (!tagId)
87
+ return;
88
+ setDeleting(true);
89
+ const res = await cmsApi(`/script-tags/${tagId}`, { method: 'DELETE' });
90
+ setDeleting(false);
91
+ if (res.error) {
92
+ toast.error(res.error);
93
+ }
94
+ else {
95
+ toast.success('Tag deleted');
96
+ onNavigate?.('/script-tags');
97
+ }
98
+ };
99
+ if (loading && !isNew) {
100
+ return (_jsx("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8 flex items-center justify-center h-64", children: _jsx(Loader2, { className: "w-6 h-6 animate-spin text-blue-600" }) }));
101
+ }
102
+ return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8 max-w-3xl", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "w-5 h-5 text-red-600 shrink-0" }), _jsx("span", { className: "text-sm text-red-800 flex-1", children: error })] })), _jsxs("div", { className: "mb-6", children: [_jsxs("button", { type: "button", onClick: () => onNavigate?.('/script-tags'), className: "flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-700 transition-colors mb-3", children: [_jsx(ArrowLeft, { className: "w-4 h-4" }), "Back to Script Tags"] }), _jsx("h1", { className: "text-2xl font-semibold text-gray-900", children: isNew ? 'New Script Tag' : 'Edit Script Tag' })] }), _jsx("div", { className: "rounded-lg border border-amber-200 bg-amber-50 p-3 mb-6", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "w-4 h-4 text-amber-600 mt-0.5 shrink-0" }), _jsx("p", { className: "text-sm text-amber-800", children: "This code will run on public pages matching the scope below. Only add trusted code from verified sources (e.g. Google Analytics, Meta Pixel)." })] }) }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4 space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Name" }), _jsx("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: "e.g. Google Tag Manager, Facebook Pixel", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Code" }), _jsx("textarea", { value: code, onChange: (e) => setCode(e.target.value), placeholder: "Paste your HTML/JavaScript snippet here...", rows: 10, className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-500", spellCheck: false }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["Paste the full code snippet including ", '<script>', " tags."] })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4 space-y-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Placement & Scope" }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Placement" }), _jsxs("select", { value: placement, onChange: (e) => setPlacement(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500", children: [_jsxs("option", { value: "head", children: ["Head \u2014 inside ", '<head>'] }), _jsxs("option", { value: "body_open", children: ["Body Open \u2014 right after ", '<body>'] }), _jsxs("option", { value: "body_close", children: ["Body Close \u2014 before ", '</body>'] })] }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: [placement === 'head' && 'Best for analytics scripts, meta tags, and custom CSS.', placement === 'body_open' && 'Best for GTM noscript tags and early-loading scripts.', placement === 'body_close' && 'Best for chat widgets, deferred scripts, and tracking pixels.'] })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Scope" }), _jsxs("select", { value: scope, onChange: (e) => setScope(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500", children: [_jsx("option", { value: "site", children: "Entire Website" }), _jsx("option", { value: "parents", children: "Specific Parent Pages (includes child pages)" }), _jsx("option", { value: "urls", children: "Specific URLs (exact match)" })] })] }), scope !== 'site' && (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: scope === 'parents' ? 'Parent Paths' : 'URL Paths' }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { type: "text", value: pathInput, onChange: (e) => setPathInput(e.target.value), onKeyDown: (e) => {
103
+ if (e.key === 'Enter') {
104
+ e.preventDefault();
105
+ addPath();
106
+ }
107
+ }, placeholder: "/services", className: "flex-1 rounded-lg border border-gray-300 px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-blue-500" }), _jsx("button", { type: "button", onClick: addPath, className: "rounded-lg border border-gray-300 px-3 py-2 text-sm hover:bg-gray-50 transition-colors", children: _jsx(Plus, { className: "w-4 h-4" }) })] }), scope === 'parents' && (_jsx("p", { className: "mt-1 text-xs text-gray-500", children: "This tag will also apply to all child pages under each path." })), targetPaths.length > 0 && (_jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: targetPaths.map((p) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-gray-100 px-3 py-1 text-xs font-mono text-gray-700", children: [p, _jsx("button", { type: "button", onClick: () => removePath(p), className: "ml-0.5 text-gray-400 hover:text-gray-600", children: _jsx(X, { className: "w-3 h-3" }) })] }, p))) }))] })), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Priority" }), _jsx("input", { type: "number", value: priority, onChange: (e) => setPriority(parseInt(e.target.value, 10) || 0), min: 0, className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Lower numbers load first (e.g. 1 = first, 100 = default)" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Enabled" }), _jsx("div", { className: "pt-2", children: _jsx("button", { type: "button", onClick: () => setEnabled(!enabled), className: `relative h-6 w-11 shrink-0 rounded-full transition-colors ${enabled ? 'bg-blue-600' : 'bg-gray-300'}`, "aria-pressed": enabled, children: _jsx("span", { className: `absolute top-0.5 block h-5 w-5 rounded-full bg-white transition-transform ${enabled ? 'translate-x-[22px]' : 'translate-x-0.5'}` }) }) })] })] })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { children: !isNew && (_jsx(_Fragment, { children: showDeleteConfirm ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm text-red-600", children: "Delete this tag?" }), _jsx("button", { type: "button", onClick: handleDelete, disabled: deleting, className: "rounded-lg bg-red-600 px-3 py-1.5 text-sm text-white hover:bg-red-700 disabled:opacity-50", children: deleting ? 'Deleting...' : 'Confirm' }), _jsx("button", { type: "button", onClick: () => setShowDeleteConfirm(false), className: "rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:bg-gray-50", children: "Cancel" })] })) : (_jsxs("button", { type: "button", onClick: () => setShowDeleteConfirm(true), className: "flex items-center gap-1.5 text-sm text-red-600 hover:text-red-700 transition-colors", children: [_jsx(Trash2, { className: "w-4 h-4" }), "Delete"] })) })) }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("button", { type: "button", onClick: () => onNavigate?.('/script-tags'), className: "rounded-lg border border-gray-300 px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 transition-colors", children: "Cancel" }), _jsx("button", { type: "button", onClick: handleSave, disabled: saving, className: "rounded-lg bg-blue-600 px-6 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50", children: saving ? 'Saving...' : isNew ? 'Create Tag' : 'Save Changes' })] })] })] })] }));
108
+ }
109
+ //# sourceMappingURL=ScriptTagEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScriptTagEditor.js","sourceRoot":"","sources":["../../src/views/ScriptTagEditor.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAOvC,MAAM,UAAU,eAAe,CAAC,EAAE,KAAK,EAAE,UAAU,EAAwB;IACzE,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC;IACrB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,UAAU,CACzC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC;IAEF,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC9C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;YAClD,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBACxB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBACxB,YAAY,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;gBACtC,QAAQ,CAAC,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;gBAC9B,cAAc,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;gBACtC,WAAW,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;gBACjC,UAAU,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAElB,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;QACrE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACtC,cAAc,CAAC,CAAC,GAAG,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,YAAY,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE;QAClC,cAAc,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,KAAK,IAAI,EAAE;QAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACjB,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACjB,KAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACxC,OAAO;QACT,CAAC;QACD,IAAI,KAAK,KAAK,MAAM,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjD,KAAK,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;YACnE,OAAO;QACT,CAAC;QAED,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,OAAO,GAAG;YACd,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;YACjB,IAAI;YACJ,SAAS;YACT,KAAK;YACL,WAAW,EAAE,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW;YAChD,QAAQ;YACR,OAAO;SACR,CAAC;QAEF,MAAM,GAAG,GAAG,KAAK;YACf,CAAC,CAAC,MAAM,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YACjF,CAAC,CAAC,MAAM,MAAM,CAAC,gBAAgB,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAE5F,SAAS,CAAC,KAAK,CAAC,CAAC;QACjB,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;YACrD,UAAU,EAAE,CAAC,cAAc,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;QAC9B,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,gBAAgB,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACxE,WAAW,CAAC,KAAK,CAAC,CAAC;QACnB,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAC7B,UAAU,EAAE,CAAC,cAAc,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,CACL,cAAK,SAAS,EAAC,+DAA+D,YAC5E,KAAC,OAAO,IAAC,SAAS,EAAC,oCAAoC,GAAG,GACtD,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,mCAAmC,aAC/C,KAAK,IAAI,CACR,eAAK,SAAS,EAAC,6EAA6E,aAC1F,KAAC,aAAa,IAAC,SAAS,EAAC,+BAA+B,GAAG,EAC3D,eAAM,SAAS,EAAC,6BAA6B,YAAE,KAAK,GAAQ,IACxD,CACP,EAED,eAAK,SAAS,EAAC,MAAM,aACnB,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,cAAc,CAAC,EAC3C,SAAS,EAAC,4FAA4F,aAEtG,KAAC,SAAS,IAAC,SAAS,EAAC,SAAS,GAAG,2BAE1B,EACT,aAAI,SAAS,EAAC,sCAAsC,YACjD,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,GAC1C,IACD,EAEN,cAAK,SAAS,EAAC,yDAAyD,YACtE,eAAK,SAAS,EAAC,wBAAwB,aACrC,KAAC,aAAa,IAAC,SAAS,EAAC,wCAAwC,GAAG,EACpE,YAAG,SAAS,EAAC,wBAAwB,8JAEjC,IACA,GACF,EAEN,eAAK,SAAS,EAAC,WAAW,aACxB,eAAK,SAAS,EAAC,0DAA0D,aACvE,0BACE,gBAAO,SAAS,EAAC,8CAA8C,qBAAa,EAC5E,gBACE,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACxC,WAAW,EAAC,yCAAyC,EACrD,SAAS,EAAC,gHAAgH,GAC1H,IACE,EAEN,0BACE,gBAAO,SAAS,EAAC,8CAA8C,qBAAa,EAC5E,mBACE,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACxC,WAAW,EAAC,4CAA4C,EACxD,IAAI,EAAE,EAAE,EACR,SAAS,EAAC,0HAA0H,EACpI,UAAU,EAAE,KAAK,GACjB,EACF,aAAG,SAAS,EAAC,4BAA4B,uDACA,UAAU,cAC/C,IACA,IACF,EAEN,eAAK,SAAS,EAAC,0DAA0D,aACvE,aAAI,SAAS,EAAC,qCAAqC,kCAAuB,EAE1E,0BACE,gBAAO,SAAS,EAAC,8CAA8C,0BAAkB,EACjF,kBACE,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC7C,SAAS,EAAC,gHAAgH,aAE1H,kBAAQ,KAAK,EAAC,MAAM,oCAAgB,QAAQ,IAAU,EACtD,kBAAQ,KAAK,EAAC,WAAW,8CAA0B,QAAQ,IAAU,EACrE,kBAAQ,KAAK,EAAC,YAAY,0CAAsB,SAAS,IAAU,IAC5D,EACT,aAAG,SAAS,EAAC,4BAA4B,aACtC,SAAS,KAAK,MAAM,IAAI,wDAAwD,EAChF,SAAS,KAAK,WAAW,IAAI,uDAAuD,EACpF,SAAS,KAAK,YAAY,IAAI,+DAA+D,IAC5F,IACA,EAEN,0BACE,gBAAO,SAAS,EAAC,8CAA8C,sBAAc,EAC7E,kBACE,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACzC,SAAS,EAAC,gHAAgH,aAE1H,iBAAQ,KAAK,EAAC,MAAM,+BAAwB,EAC5C,iBAAQ,KAAK,EAAC,SAAS,6DAAsD,EAC7E,iBAAQ,KAAK,EAAC,MAAM,4CAAqC,IAClD,IACL,EAEL,KAAK,KAAK,MAAM,IAAI,CACnB,0BACE,gBAAO,SAAS,EAAC,8CAA8C,YAC5D,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,GAC7C,EACR,eAAK,SAAS,EAAC,YAAY,aACzB,gBACE,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC7C,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;oDACf,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;wDACtB,CAAC,CAAC,cAAc,EAAE,CAAC;wDACnB,OAAO,EAAE,CAAC;oDACZ,CAAC;gDACH,CAAC,EACD,WAAW,EAAC,WAAW,EACvB,SAAS,EAAC,0HAA0H,GACpI,EACF,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,OAAO,EAChB,SAAS,EAAC,wFAAwF,YAElG,KAAC,IAAI,IAAC,SAAS,EAAC,SAAS,GAAG,GACrB,IACL,EACL,KAAK,KAAK,SAAS,IAAI,CACtB,YAAG,SAAS,EAAC,4BAA4B,6EAErC,CACL,EACA,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CACzB,cAAK,SAAS,EAAC,2BAA2B,YACvC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACtB,gBAEE,SAAS,EAAC,mGAAmG,aAE5G,CAAC,EACF,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAC5B,SAAS,EAAC,0CAA0C,YAEpD,KAAC,CAAC,IAAC,SAAS,EAAC,SAAS,GAAG,GAClB,KAVJ,CAAC,CAWD,CACR,CAAC,GACE,CACP,IACG,CACP,EAED,eAAK,SAAS,EAAC,wBAAwB,aACrC,0BACE,gBAAO,SAAS,EAAC,8CAA8C,yBAAiB,EAChF,gBACE,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAC/D,GAAG,EAAE,CAAC,EACN,SAAS,EAAC,gHAAgH,GAC1H,EACF,YAAG,SAAS,EAAC,4BAA4B,yEAErC,IACA,EACN,0BACE,gBAAO,SAAS,EAAC,8CAA8C,wBAAgB,EAC/E,cAAK,SAAS,EAAC,MAAM,YACnB,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,EACnC,SAAS,EAAE,6DAA6D,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,EAAE,kBACnG,OAAO,YAErB,eACE,SAAS,EAAE,6EACT,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,iBACnC,EAAE,GACF,GACK,GACL,IACF,IACF,IACF,EAEN,eAAK,SAAS,EAAC,mCAAmC,aAChD,wBACG,CAAC,KAAK,IAAI,CACT,4BACG,iBAAiB,CAAC,CAAC,CAAC,CACnB,eAAK,SAAS,EAAC,yBAAyB,aACtC,eAAM,SAAS,EAAC,sBAAsB,iCAAwB,EAC9D,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAC,2FAA2F,YAEpG,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,GAC9B,EACT,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAC1C,SAAS,EAAC,wEAAwE,uBAG3E,IACL,CACP,CAAC,CAAC,CAAC,CACF,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,EACzC,SAAS,EAAC,qFAAqF,aAE/F,KAAC,MAAM,IAAC,SAAS,EAAC,SAAS,GAAG,cAEvB,CACV,GACA,CACJ,GACG,EACN,eAAK,SAAS,EAAC,yBAAyB,aACtC,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,cAAc,CAAC,EAC3C,SAAS,EAAC,sGAAsG,uBAGzG,EACT,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAC,6GAA6G,YAEtH,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,GACtD,IACL,IACF,IACF,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface ScriptTagsProps {
2
+ onNavigate?: (path: string) => void;
3
+ }
4
+ export declare function ScriptTags({ onNavigate }: ScriptTagsProps): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=ScriptTags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScriptTags.d.ts","sourceRoot":"","sources":["../../src/views/ScriptTags.tsx"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AA2BD,wBAAgB,UAAU,CAAC,EAAE,UAAU,EAAE,EAAE,eAAe,2CA2HzD"}
@@ -0,0 +1,54 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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
+ const PLACEMENT_LABELS = {
9
+ head: 'Head',
10
+ body_open: 'Body Open',
11
+ body_close: 'Body Close',
12
+ };
13
+ const PLACEMENT_COLORS = {
14
+ head: 'bg-purple-100 text-purple-700',
15
+ body_open: 'bg-blue-100 text-blue-700',
16
+ body_close: 'bg-green-100 text-green-700',
17
+ };
18
+ function scopeLabel(tag) {
19
+ if (tag.scope === 'site')
20
+ return 'Entire Site';
21
+ if (tag.scope === 'parents') {
22
+ const count = tag.targetPaths?.length ?? 0;
23
+ return `${count} parent path${count !== 1 ? 's' : ''} + children`;
24
+ }
25
+ if (tag.scope === 'urls') {
26
+ const count = tag.targetPaths?.length ?? 0;
27
+ return `${count} URL${count !== 1 ? 's' : ''}`;
28
+ }
29
+ return tag.scope;
30
+ }
31
+ export function ScriptTags({ onNavigate }) {
32
+ const { data, loading, error, refetch } = useApiData('/script-tags');
33
+ const [togglingId, setTogglingId] = useState(null);
34
+ const tags = data ?? [];
35
+ const toggleEnabled = async (tag) => {
36
+ setTogglingId(tag.id);
37
+ const res = await cmsApi(`/script-tags/${tag.id}`, {
38
+ method: 'PUT',
39
+ body: JSON.stringify({ enabled: !tag.enabled }),
40
+ });
41
+ setTogglingId(null);
42
+ if (res.error) {
43
+ toast.error(res.error);
44
+ }
45
+ else {
46
+ refetch();
47
+ }
48
+ };
49
+ if (loading) {
50
+ return (_jsx("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8 flex items-center justify-center h-64", children: _jsx(Loader2, { className: "w-6 h-6 animate-spin text-blue-600" }) }));
51
+ }
52
+ return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "w-5 h-5 text-red-600 shrink-0" }), _jsx("span", { className: "text-sm text-red-800 flex-1", children: error }), _jsx("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", children: "Retry" })] })), _jsxs("div", { className: "mb-4 flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "mb-1 text-2xl font-semibold text-gray-900", children: "Script Tags" }), _jsx("p", { className: "text-sm text-gray-600", children: "Manage tracking codes, analytics, and custom scripts injected into your site" })] }), _jsxs("button", { type: "button", onClick: () => onNavigate?.('/script-tags/new'), 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", children: [_jsx(Plus, { className: "w-4 h-4" }), "New Tag"] })] }), tags.length === 0 && !error ? (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-12 text-center", children: [_jsx(Code2, { className: "mx-auto mb-3 h-10 w-10 text-gray-300" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "No script tags yet" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Add tracking codes like Google Analytics, Tag Manager, or Facebook Pixel." }), _jsxs("button", { type: "button", onClick: () => onNavigate?.('/script-tags/new'), 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", children: [_jsx(Plus, { className: "w-4 h-4" }), "Add Your First Tag"] })] })) : (_jsx("div", { className: "rounded-lg border border-gray-200 bg-white overflow-hidden", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-gray-200 bg-gray-50", children: [_jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-600", children: "Name" }), _jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-600", children: "Placement" }), _jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-600", children: "Scope" }), _jsx("th", { className: "px-4 py-3 text-center font-medium text-gray-600", children: "Priority" }), _jsx("th", { className: "px-4 py-3 text-center font-medium text-gray-600", children: "Enabled" })] }) }), _jsx("tbody", { children: tags.map((tag) => (_jsxs("tr", { className: "border-b border-gray-100 last:border-0 hover:bg-gray-50 transition-colors", children: [_jsx("td", { className: "px-4 py-3", children: _jsx("button", { type: "button", onClick: () => onNavigate?.(`/script-tags/${tag.id}`), className: "font-medium text-blue-600 hover:text-blue-800 hover:underline", children: tag.name }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("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'}`, children: PLACEMENT_LABELS[tag.placement] ?? tag.placement }) }), _jsx("td", { className: "px-4 py-3 text-gray-600", children: scopeLabel(tag) }), _jsx("td", { className: "px-4 py-3 text-center text-gray-600 font-mono", children: tag.priority }), _jsx("td", { className: "px-4 py-3 text-center", children: _jsx("button", { type: "button", onClick: () => toggleEnabled(tag), disabled: togglingId === tag.id, className: `relative h-6 w-11 shrink-0 rounded-full transition-colors ${tag.enabled ? 'bg-blue-600' : 'bg-gray-300'}`, "aria-pressed": tag.enabled, children: _jsx("span", { className: `absolute top-0.5 block h-5 w-5 rounded-full bg-white transition-transform ${tag.enabled ? 'translate-x-[22px]' : 'translate-x-0.5'}` }) }) })] }, tag.id))) })] }) }))] }));
53
+ }
54
+ //# sourceMappingURL=ScriptTags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScriptTags.js","sourceRoot":"","sources":["../../src/views/ScriptTags.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actuate-media/cms-admin",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/actuate-media/actuatecms.git",
@@ -58,8 +58,10 @@
58
58
  "react-dom": "^19.2.0",
59
59
  "recharts": "^3.8.1",
60
60
  "sonner": "^2.0.7",
61
- "tailwind-merge": "^3.5.0",
62
- "@actuate-media/cms-core": "0.5.0"
61
+ "tailwind-merge": "^3.5.0"
62
+ },
63
+ "peerDependencies": {
64
+ "@actuate-media/cms-core": ">=0.1.0"
63
65
  },
64
66
  "devDependencies": {
65
67
  "@tailwindcss/cli": "^4.0.0",
package/src/AdminRoot.tsx CHANGED
@@ -13,8 +13,12 @@ import { FormEditor } from './views/FormEditor.js';
13
13
  import { FormSubmissions } from './views/FormSubmissions.js';
14
14
  import { Users } from './views/Users.js';
15
15
  import { SEO } from './views/SEO.js';
16
+ import { ScriptTags } from './views/ScriptTags.js';
17
+ import { ScriptTagEditor } from './views/ScriptTagEditor.js';
16
18
  import { SetupWizard } from './views/SetupWizard.js';
17
19
  import { Login } from './views/Login.js';
20
+ import { ForgotPassword } from './views/ForgotPassword.js';
21
+ import { ResetPassword } from './views/ResetPassword.js';
18
22
  import { ErrorBoundary } from './components/ErrorBoundary.js';
19
23
  import { ThemeProvider } from './components/ThemeProvider.js';
20
24
  import { LocaleProvider } from './components/LocaleProvider.js';
@@ -63,6 +67,16 @@ function AdminShell({ config, session, basePath = '/admin', initialPath = '/', s
63
67
  }
64
68
 
65
69
  if (!session && !setupRequired) {
70
+ if (matchRoute('/forgot-password')) {
71
+ return <ForgotPassword onNavigate={navigate} />;
72
+ }
73
+
74
+ const resetMatch = matchRoute('/reset-password');
75
+ if (resetMatch) {
76
+ const params = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
77
+ return <ResetPassword onNavigate={navigate} token={params.get('token')} />;
78
+ }
79
+
66
80
  if (onLogin) {
67
81
  return <Login onLogin={onLogin} onNavigate={navigate} captchaConfig={captchaConfig} />;
68
82
  }
@@ -146,6 +160,17 @@ function AdminShell({ config, session, basePath = '/admin', initialPath = '/', s
146
160
  return <SEO onNavigate={navigate} initialTab="pages" />;
147
161
  }
148
162
 
163
+ if (matchRoute('/script-tags/new')) {
164
+ return <ScriptTagEditor onNavigate={navigate} />;
165
+ }
166
+ const scriptTagEdit = matchRoute('/script-tags/:id');
167
+ if (scriptTagEdit?.id) {
168
+ return <ScriptTagEditor tagId={scriptTagEdit.id} onNavigate={navigate} />;
169
+ }
170
+ if (matchRoute('/script-tags')) {
171
+ return <ScriptTags onNavigate={navigate} />;
172
+ }
173
+
149
174
  if (matchRoute('/users')) {
150
175
  return <Users onNavigate={navigate} />;
151
176
  }
@@ -12,6 +12,7 @@ const LABEL_MAP: Record<string, string> = {
12
12
  canonicals: 'Canonicalization',
13
13
  links: 'Link Health',
14
14
  users: 'Users',
15
+ 'script-tags': 'Script Tags',
15
16
  settings: 'Settings',
16
17
  collections: 'Collections',
17
18
  submissions: 'Submissions',
package/src/index.ts CHANGED
@@ -26,6 +26,10 @@ export { SetupWizard } from './views/SetupWizard.js';
26
26
  export type { SetupWizardProps } from './views/SetupWizard.js';
27
27
  export { Login } from './views/Login.js';
28
28
  export type { LoginProps, CaptchaConfig } from './views/Login.js';
29
+ export { ForgotPassword } from './views/ForgotPassword.js';
30
+ export type { ForgotPasswordProps } from './views/ForgotPassword.js';
31
+ export { ResetPassword } from './views/ResetPassword.js';
32
+ export type { ResetPasswordProps } from './views/ResetPassword.js';
29
33
  export { CollectionList } from './views/CollectionList.js';
30
34
  export { DocumentEdit } from './views/DocumentEdit.js';
31
35
 
@@ -19,6 +19,7 @@ import {
19
19
  PanelTop,
20
20
  PanelBottom,
21
21
  Layers,
22
+ Code2,
22
23
  } from 'lucide-react';
23
24
  import type { LucideIcon } from 'lucide-react';
24
25
 
@@ -256,6 +257,7 @@ function buildNavItems(config: any): NavItem[] {
256
257
  { path: '/media', label: 'Media', icon: Image },
257
258
  { path: '/forms', label: 'Forms', icon: ClipboardList },
258
259
  { path: '/seo', label: 'SEO', icon: SearchIcon },
260
+ { path: '/script-tags', label: 'Script Tags', icon: Code2 },
259
261
  { path: '/users', label: 'Users', icon: Users },
260
262
  { path: '/settings', label: 'Settings', icon: Settings },
261
263
  );
@@ -0,0 +1,136 @@
1
+ 'use client';
2
+
3
+ import { useState, type FormEvent } from 'react';
4
+ import { Shield, ArrowLeft, Loader2, CheckCircle2, AlertTriangle } from 'lucide-react';
5
+ import { cmsApi } from '../lib/api.js';
6
+
7
+ export interface ForgotPasswordProps {
8
+ onNavigate: (path: string) => void;
9
+ }
10
+
11
+ export function ForgotPassword({ onNavigate }: ForgotPasswordProps) {
12
+ const [email, setEmail] = useState('');
13
+ const [submitting, setSubmitting] = useState(false);
14
+ const [sent, setSent] = useState(false);
15
+ const [error, setError] = useState('');
16
+
17
+ const canSubmit = email.trim() && !submitting;
18
+
19
+ const handleSubmit = async (e: FormEvent) => {
20
+ e.preventDefault();
21
+ if (!canSubmit) return;
22
+
23
+ setError('');
24
+ setSubmitting(true);
25
+
26
+ try {
27
+ const result = await cmsApi('/auth/forgot-password', {
28
+ method: 'POST',
29
+ body: JSON.stringify({ email: email.trim() }),
30
+ });
31
+
32
+ if (result.error && result.status === 429) {
33
+ setError('Too many requests. Please try again later.');
34
+ } else {
35
+ setSent(true);
36
+ }
37
+ } catch {
38
+ setError('An unexpected error occurred. Please try again.');
39
+ } finally {
40
+ setSubmitting(false);
41
+ }
42
+ };
43
+
44
+ return (
45
+ <div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
46
+ <div className="w-full max-w-md">
47
+ <div className="text-center mb-8">
48
+ <div className="mx-auto mb-4 w-14 h-14 bg-blue-600 rounded-xl flex items-center justify-center">
49
+ <Shield className="w-7 h-7 text-white" />
50
+ </div>
51
+ <h1 className="text-2xl font-bold text-gray-900">Reset Password</h1>
52
+ <p className="text-gray-600 mt-2">
53
+ {sent
54
+ ? 'Check your inbox for a reset link'
55
+ : "Enter your email and we'll send you a reset link"}
56
+ </p>
57
+ </div>
58
+
59
+ <div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm space-y-5">
60
+ {sent ? (
61
+ <div className="text-center space-y-4">
62
+ <div className="mx-auto w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
63
+ <CheckCircle2 className="w-6 h-6 text-green-600" />
64
+ </div>
65
+ <p className="text-sm text-gray-600">
66
+ If an account exists for <strong>{email}</strong>, you will receive a password reset email shortly.
67
+ </p>
68
+ <p className="text-xs text-gray-500">
69
+ The link expires in 1 hour. Check your spam folder if you don't see it.
70
+ </p>
71
+ <button
72
+ type="button"
73
+ onClick={() => onNavigate('/login')}
74
+ className="w-full py-2.5 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors"
75
+ >
76
+ Back to Sign In
77
+ </button>
78
+ </div>
79
+ ) : (
80
+ <form onSubmit={handleSubmit} className="space-y-5">
81
+ {error && (
82
+ <div className="flex items-start gap-3 p-3 bg-red-50 border border-red-200 rounded-lg">
83
+ <AlertTriangle className="w-5 h-5 text-red-600 mt-0.5 shrink-0" />
84
+ <p className="text-sm text-red-800">{error}</p>
85
+ </div>
86
+ )}
87
+
88
+ <div>
89
+ <label htmlFor="forgot-email" className="block text-sm font-medium text-gray-700 mb-1.5">
90
+ Email Address
91
+ </label>
92
+ <input
93
+ id="forgot-email"
94
+ type="email"
95
+ value={email}
96
+ onChange={(e) => setEmail(e.target.value)}
97
+ placeholder="admin@example.com"
98
+ className="w-full px-3 py-2.5 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
99
+ required
100
+ autoFocus
101
+ autoComplete="email"
102
+ />
103
+ </div>
104
+
105
+ <button
106
+ type="submit"
107
+ disabled={!canSubmit}
108
+ className="w-full py-2.5 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
109
+ >
110
+ {submitting ? (
111
+ <>
112
+ <Loader2 className="w-4 h-4 animate-spin" />
113
+ Sending...
114
+ </>
115
+ ) : (
116
+ 'Send Reset Link'
117
+ )}
118
+ </button>
119
+ </form>
120
+ )}
121
+
122
+ {!sent && (
123
+ <button
124
+ type="button"
125
+ onClick={() => onNavigate('/login')}
126
+ className="w-full flex items-center justify-center gap-2 text-sm text-gray-600 hover:text-gray-800 transition-colors"
127
+ >
128
+ <ArrowLeft className="w-4 h-4" />
129
+ Back to Sign In
130
+ </button>
131
+ )}
132
+ </div>
133
+ </div>
134
+ </div>
135
+ );
136
+ }