@dyrected/admin 2.4.0 → 2.4.1

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 (170) hide show
  1. package/dist/App.d.ts +1 -0
  2. package/dist/admin.css +2 -0
  3. package/dist/components/auth/auth-gate.d.ts +13 -0
  4. package/dist/components/error-boundary.d.ts +16 -0
  5. package/dist/components/forms/field-renderer.d.ts +22 -0
  6. package/dist/components/forms/fields/block-builder.d.ts +9 -0
  7. package/dist/components/forms/fields/date-picker.d.ts +8 -0
  8. package/dist/components/forms/fields/json-editor.d.ts +8 -0
  9. package/dist/components/forms/fields/media-picker.d.ts +12 -0
  10. package/dist/components/forms/fields/multi-select.d.ts +19 -0
  11. package/dist/components/forms/fields/radio-field.d.ts +8 -0
  12. package/dist/components/forms/fields/relationship-picker.d.ts +10 -0
  13. package/dist/components/forms/fields/rich-text-editor.d.ts +9 -0
  14. package/dist/components/forms/fields/select-field.d.ts +8 -0
  15. package/dist/components/forms/fields/switch-field.d.ts +6 -0
  16. package/dist/components/forms/fields/text-area-field.d.ts +8 -0
  17. package/dist/components/forms/fields/text-field.d.ts +8 -0
  18. package/dist/components/forms/form-engine.d.ts +14 -0
  19. package/dist/components/forms/form-field-renderer.d.ts +20 -0
  20. package/dist/components/forms/utils.d.ts +11 -0
  21. package/dist/components/layout/admin-shell.d.ts +5 -0
  22. package/dist/components/layout/branding-provider.d.ts +4 -0
  23. package/dist/components/live-preview/LivePreviewPane.d.ts +7 -0
  24. package/dist/components/media/focal-point-picker.d.ts +12 -0
  25. package/dist/components/media/media-card.d.ts +8 -0
  26. package/dist/components/media/media-grid.d.ts +8 -0
  27. package/dist/components/media/media-library-dialog.d.ts +11 -0
  28. package/dist/components/ui/aspect-ratio.d.ts +3 -0
  29. package/dist/components/ui/badge.d.ts +9 -0
  30. package/dist/components/ui/button.d.ts +11 -0
  31. package/dist/components/ui/calendar.d.ts +8 -0
  32. package/dist/components/ui/card.d.ts +8 -0
  33. package/dist/components/ui/checkbox.d.ts +4 -0
  34. package/dist/components/ui/command.d.ts +80 -0
  35. package/dist/components/ui/data-table.d.ts +14 -0
  36. package/dist/components/ui/dialog.d.ts +19 -0
  37. package/dist/components/ui/dropdown-menu.d.ts +27 -0
  38. package/dist/components/ui/form.d.ts +23 -0
  39. package/dist/components/ui/input.d.ts +3 -0
  40. package/dist/components/ui/label.d.ts +5 -0
  41. package/dist/components/ui/page-header.d.ts +10 -0
  42. package/dist/components/ui/pagination.d.ts +11 -0
  43. package/dist/components/ui/popover.d.ts +6 -0
  44. package/dist/components/ui/progress.d.ts +4 -0
  45. package/dist/components/ui/radio-group.d.ts +5 -0
  46. package/dist/components/ui/render-cell.d.ts +8 -0
  47. package/dist/components/ui/scroll-area.d.ts +5 -0
  48. package/dist/components/ui/select.d.ts +13 -0
  49. package/dist/components/ui/separator.d.ts +4 -0
  50. package/dist/components/ui/sheet.d.ts +25 -0
  51. package/dist/components/ui/sidebar.d.ts +65 -0
  52. package/dist/components/ui/skeleton.d.ts +2 -0
  53. package/dist/components/ui/sonner.d.ts +4 -0
  54. package/dist/components/ui/switch.d.ts +4 -0
  55. package/dist/components/ui/table.d.ts +10 -0
  56. package/dist/components/ui/tabs.d.ts +7 -0
  57. package/dist/components/ui/textarea.d.ts +3 -0
  58. package/dist/components/ui/toggle.d.ts +12 -0
  59. package/dist/components/ui/tooltip.d.ts +7 -0
  60. package/dist/hooks/use-mobile.d.ts +1 -0
  61. package/dist/hooks/use-preferences.d.ts +6 -0
  62. package/dist/index.d.ts +38 -0
  63. package/dist/index.mjs +69091 -0
  64. package/dist/lib/utils.d.ts +3 -0
  65. package/dist/main.d.ts +0 -0
  66. package/dist/pages/auth/first-user-page.d.ts +4 -0
  67. package/dist/pages/auth/login-page.d.ts +4 -0
  68. package/dist/pages/collections/edit-page.d.ts +1 -0
  69. package/dist/pages/collections/list-page.d.ts +5 -0
  70. package/dist/pages/dashboard/dashboard.d.ts +1 -0
  71. package/dist/pages/globals/editor-page.d.ts +1 -0
  72. package/dist/pages/media/media-page.d.ts +4 -0
  73. package/dist/pages/setup/setup-prompt.d.ts +6 -0
  74. package/dist/providers/dyrected-provider.d.ts +29 -0
  75. package/dist/providers/query-provider.d.ts +3 -0
  76. package/package.json +6 -3
  77. package/CHANGELOG.md +0 -153
  78. package/components.json +0 -17
  79. package/eslint.config.js +0 -22
  80. package/index.html +0 -13
  81. package/postcss.config.js +0 -6
  82. package/scripts/prefix-tailwind-precision.py +0 -98
  83. package/scripts/prefix-tailwind.py +0 -67
  84. package/src/App.css +0 -184
  85. package/src/App.tsx +0 -25
  86. package/src/assets/dyrected.svg +0 -155
  87. package/src/assets/hero.png +0 -0
  88. package/src/assets/react.svg +0 -1
  89. package/src/assets/vite.svg +0 -1
  90. package/src/components/auth/auth-gate.tsx +0 -64
  91. package/src/components/error-boundary.tsx +0 -45
  92. package/src/components/forms/field-renderer.tsx +0 -111
  93. package/src/components/forms/fields/block-builder.tsx +0 -213
  94. package/src/components/forms/fields/date-picker.tsx +0 -60
  95. package/src/components/forms/fields/json-editor.tsx +0 -62
  96. package/src/components/forms/fields/media-picker.tsx +0 -286
  97. package/src/components/forms/fields/multi-select.tsx +0 -145
  98. package/src/components/forms/fields/radio-field.tsx +0 -51
  99. package/src/components/forms/fields/relationship-picker.tsx +0 -143
  100. package/src/components/forms/fields/rich-text-editor.tsx +0 -224
  101. package/src/components/forms/fields/select-field.tsx +0 -35
  102. package/src/components/forms/fields/switch-field.tsx +0 -16
  103. package/src/components/forms/fields/text-area-field.tsx +0 -15
  104. package/src/components/forms/fields/text-field.tsx +0 -24
  105. package/src/components/forms/form-engine.tsx +0 -87
  106. package/src/components/forms/form-field-renderer.tsx +0 -269
  107. package/src/components/forms/utils.ts +0 -97
  108. package/src/components/layout/admin-shell.tsx +0 -479
  109. package/src/components/layout/branding-provider.tsx +0 -112
  110. package/src/components/live-preview/LivePreviewPane.tsx +0 -128
  111. package/src/components/media/focal-point-picker.tsx +0 -66
  112. package/src/components/media/media-card.tsx +0 -44
  113. package/src/components/media/media-grid.tsx +0 -32
  114. package/src/components/media/media-library-dialog.tsx +0 -465
  115. package/src/components/ui/aspect-ratio.tsx +0 -7
  116. package/src/components/ui/badge.tsx +0 -36
  117. package/src/components/ui/button.tsx +0 -56
  118. package/src/components/ui/calendar.tsx +0 -214
  119. package/src/components/ui/card.tsx +0 -79
  120. package/src/components/ui/checkbox.tsx +0 -28
  121. package/src/components/ui/command.tsx +0 -151
  122. package/src/components/ui/data-table.tsx +0 -219
  123. package/src/components/ui/dialog.tsx +0 -122
  124. package/src/components/ui/dropdown-menu.tsx +0 -200
  125. package/src/components/ui/form.tsx +0 -178
  126. package/src/components/ui/input.tsx +0 -24
  127. package/src/components/ui/label.tsx +0 -24
  128. package/src/components/ui/page-header.tsx +0 -30
  129. package/src/components/ui/pagination.tsx +0 -57
  130. package/src/components/ui/popover.tsx +0 -29
  131. package/src/components/ui/progress.tsx +0 -26
  132. package/src/components/ui/radio-group.tsx +0 -42
  133. package/src/components/ui/render-cell.tsx +0 -110
  134. package/src/components/ui/scroll-area.tsx +0 -46
  135. package/src/components/ui/select.tsx +0 -160
  136. package/src/components/ui/separator.tsx +0 -29
  137. package/src/components/ui/sheet.tsx +0 -140
  138. package/src/components/ui/sidebar.tsx +0 -771
  139. package/src/components/ui/skeleton.tsx +0 -15
  140. package/src/components/ui/sonner.tsx +0 -27
  141. package/src/components/ui/switch.tsx +0 -27
  142. package/src/components/ui/table.tsx +0 -117
  143. package/src/components/ui/tabs.tsx +0 -53
  144. package/src/components/ui/textarea.tsx +0 -22
  145. package/src/components/ui/toggle.tsx +0 -43
  146. package/src/components/ui/tooltip.tsx +0 -28
  147. package/src/hooks/use-mobile.tsx +0 -19
  148. package/src/hooks/use-preferences.ts +0 -56
  149. package/src/index.css +0 -111
  150. package/src/index.tsx +0 -198
  151. package/src/lib/utils.ts +0 -36
  152. package/src/main.tsx +0 -10
  153. package/src/pages/auth/first-user-page.tsx +0 -115
  154. package/src/pages/auth/login-page.tsx +0 -91
  155. package/src/pages/collections/edit-page.tsx +0 -280
  156. package/src/pages/collections/list-page.tsx +0 -343
  157. package/src/pages/dashboard/dashboard.tsx +0 -150
  158. package/src/pages/globals/editor-page.tsx +0 -122
  159. package/src/pages/media/media-page.tsx +0 -564
  160. package/src/pages/setup/setup-prompt.tsx +0 -181
  161. package/src/providers/dyrected-provider.tsx +0 -122
  162. package/src/providers/query-provider.tsx +0 -19
  163. package/src/types/jexl.d.ts +0 -11
  164. package/tailwind.config.ts +0 -103
  165. package/tsconfig.app.json +0 -28
  166. package/tsconfig.json +0 -12
  167. package/tsconfig.node.json +0 -25
  168. package/vite.config.ts +0 -39
  169. /package/{public → dist}/favicon.svg +0 -0
  170. /package/{public → dist}/icons.svg +0 -0
package/src/index.tsx DELETED
@@ -1,198 +0,0 @@
1
- /** @jsxImportSource react */
2
- import "./index.css";
3
- import React, { useEffect, useState, StrictMode } from "react";
4
- import { createRoot } from "react-dom/client";
5
- import {
6
- HashRouter,
7
- MemoryRouter,
8
- Routes,
9
- Route,
10
- useParams,
11
- useLocation,
12
- } from "react-router-dom";
13
- import { useQuery } from "@tanstack/react-query";
14
- import { DyrectedProvider, useDyrected } from "./providers/dyrected-provider";
15
- import { QueryProvider } from "./providers/query-provider";
16
- import { AdminShell } from "./components/layout/admin-shell";
17
- import { Dashboard } from "./pages/dashboard/dashboard";
18
- import { CollectionListPage } from "./pages/collections/list-page";
19
- import { EditEntryPage } from "./pages/collections/edit-page";
20
- import { MediaPage } from "./pages/media/media-page";
21
- import { GlobalEditorPage } from "./pages/globals/editor-page";
22
- import { SetupPromptUI } from "./pages/setup/setup-prompt";
23
- import { ErrorBoundary } from "./components/error-boundary";
24
- import { AuthGate } from "./components/auth/auth-gate";
25
- import { Toaster } from "./components/ui/sonner";
26
-
27
- // ─── Route that resolves collection → list or media page ─────────────────────
28
-
29
- function CollectionRoute() {
30
- const { slug } = useParams();
31
- const { client } = useDyrected();
32
-
33
- const { data: schemas } = useQuery({
34
- queryKey: ["schemas"],
35
- queryFn: () => client?.getSchemas() || Promise.resolve({ collections: [], globals: [] }),
36
- enabled: !!client,
37
- });
38
-
39
- const schema = schemas?.collections.find((c: any) => c.slug === slug);
40
-
41
- if (schema?.admin?.hidden) {
42
- return <div>404: Not Found</div>;
43
- }
44
-
45
- if (schema?.upload) {
46
- return <MediaPage collectionSlug={slug!} schema={schema} />;
47
- }
48
-
49
- return <CollectionListPage slug={slug!} />;
50
- }
51
-
52
- // ─── Setup page — reads config from context ───────────────────────────────────
53
-
54
- function SetupPage() {
55
- const { config } = useDyrected();
56
- return <SetupPromptUI config={config} />;
57
- }
58
-
59
- // ─── Navigation sync — notifies host on every internal route change ───────────
60
-
61
- interface NavigationSyncProps {
62
- onNavigate?: (path: string) => void;
63
- }
64
-
65
- function NavigationSync({ onNavigate }: NavigationSyncProps) {
66
- const location = useLocation();
67
-
68
- useEffect(() => {
69
- onNavigate?.(location.pathname + location.search);
70
- }, [location, onNavigate]);
71
-
72
- return null;
73
- }
74
-
75
- // ─── Route tree (shared between embedded and standalone) ──────────────────────
76
-
77
- function AdminRoutes({ onNavigate, isEmbedded = false }: { onNavigate?: (path: string) => void, isEmbedded?: boolean }) {
78
- return (
79
- <AuthGate>
80
- <AdminShell isEmbedded={isEmbedded}>
81
- <NavigationSync onNavigate={onNavigate} />
82
- <Routes>
83
- <Route path="/" element={<Dashboard />} />
84
- <Route path="/collections/:slug" element={<CollectionRoute />} />
85
- <Route path="/collections/:slug/new" element={<EditEntryPage />} />
86
- <Route path="/collections/:slug/edit/:id" element={<EditEntryPage />} />
87
- <Route path="/globals/:slug" element={<GlobalEditorPage />} />
88
- <Route path="/setup" element={<SetupPage />} />
89
- </Routes>
90
- </AdminShell>
91
- </AuthGate>
92
- );
93
- }
94
-
95
- // ─── Public types ─────────────────────────────────────────────────────────────
96
-
97
- export interface AdminUIProps {
98
- apiKey?: string;
99
- baseUrl?: string;
100
- siteId?: string;
101
- /**
102
- * The base path where the admin is mounted in the host app.
103
- * Defaults to "/admin". Used by BrowserRouter so internal links
104
- * are relative to this prefix.
105
- *
106
- * Example — Next.js catch-all page at `app/admin/[[...path]]/page.tsx`:
107
- * <AdminUI basename="/admin" ... />
108
- */
109
- basename?: string;
110
- /**
111
- * Called whenever the internal admin route changes.
112
- * Use this to sync the host router (e.g. Next.js router.push / Nuxt navigateTo)
113
- * so browser history works correctly when embedded.
114
- *
115
- * Example (Nuxt):
116
- * <AdminUI onNavigate={(path) => navigateTo('/admin' + path)} ... />
117
- */
118
- onNavigate?: (path: string) => void;
119
- isEmbedded?: boolean
120
- }
121
-
122
- // ─── Embedded component (BrowserRouter — real URL + history) ─────────────────
123
-
124
- export function AdminUI({
125
- apiKey,
126
- baseUrl = "/dyrected",
127
- siteId,
128
- onNavigate,
129
- isEmbedded
130
- }: AdminUIProps) {
131
- const [mounted, setMounted] = useState(false);
132
- useEffect(() => setMounted(true), []);
133
-
134
- if (!mounted) {
135
- return (
136
- <div className="dy-flex-1 dy-flex dy-items-center dy-justify-center dy-p-12 dy-bg-muted/5 dy-animate-pulse">
137
- <div className="dy-text-muted-foreground/40 dy-text-sm dy-font-medium">Loading Dashboard...</div>
138
- </div>
139
- );
140
- }
141
-
142
- return (
143
- <div className="dy-admin-ui dy-h-full">
144
- <ErrorBoundary>
145
- <DyrectedProvider apiKey={apiKey} baseUrl={baseUrl} siteId={siteId}>
146
- <QueryProvider>
147
- <HashRouter>
148
- <AdminRoutes onNavigate={onNavigate} isEmbedded={isEmbedded} />
149
- </HashRouter>
150
- </QueryProvider>
151
- <Toaster position="top-right" expand={true} richColors />
152
- </DyrectedProvider>
153
- </ErrorBoundary>
154
- </div>
155
- );
156
- }
157
-
158
- /**
159
- * Renders the Admin UI into a DOM element.
160
- * Useful for non-React frameworks like Nuxt, Svelte, or Vanilla JS.
161
- */
162
- export function renderAdminUI(container: HTMLElement, props: AdminUIProps) {
163
- const root = createRoot(container);
164
- root.render(
165
- React.createElement(StrictMode, null,
166
- React.createElement(AdminUI, props)
167
- )
168
- );
169
- return () => root.unmount();
170
- }
171
-
172
- // ─── Standalone component (MemoryRouter — for iframe / self-hosted mode) ──────
173
-
174
- export interface AdminStandaloneProps {
175
- apiKey: string;
176
- baseUrl: string;
177
- siteId?: string;
178
- }
179
-
180
- export function AdminStandalone({ apiKey, baseUrl, siteId }: AdminStandaloneProps) {
181
- return (
182
- <div className="dy-admin-ui dy-h-full">
183
- <DyrectedProvider apiKey={apiKey} baseUrl={baseUrl} siteId={siteId}>
184
- <QueryProvider>
185
- <MemoryRouter>
186
- <AdminRoutes />
187
- </MemoryRouter>
188
- </QueryProvider>
189
- <Toaster position="top-right" expand={true} richColors />
190
- </DyrectedProvider>
191
- </div>
192
- );
193
- }
194
-
195
- // ─── Re-exports for external use ──────────────────────────────────────────────
196
-
197
- export { SetupPromptUI } from "./pages/setup/setup-prompt";
198
- export type { SetupPromptProps } from "./pages/setup/setup-prompt";
package/src/lib/utils.ts DELETED
@@ -1,36 +0,0 @@
1
- import { clsx, type ClassValue } from "clsx"
2
- import { extendTailwindMerge } from "tailwind-merge"
3
-
4
- const customTwMerge = extendTailwindMerge({
5
- prefix: "dy-",
6
- })
7
-
8
- export function cn(...inputs: ClassValue[]) {
9
- return customTwMerge(clsx(inputs))
10
- }
11
-
12
- export function getMediaUrl(val: string | any, baseUrl: string) {
13
- if (!val) return "";
14
-
15
- // Handle object with direct URL
16
- if (typeof val === 'object' && (val.url || val.filename)) {
17
- const url = val.url || val.filename;
18
- if (url.startsWith('http')) return url;
19
- if (url.startsWith('/')) return `${baseUrl}${url}`;
20
- // If it's just a filename in the object, use proxy
21
- return `${baseUrl}/media/${val.filename || val.url}`;
22
- }
23
-
24
- const valueStr = typeof val === 'string' ? val : val.id || val.filename || val.url;
25
- if (!valueStr) return "";
26
-
27
- if (valueStr.startsWith('http')) return valueStr;
28
-
29
- // Check if it's a relative path starting with /
30
- if (valueStr.startsWith('/')) {
31
- return `${baseUrl}${valueStr}`;
32
- }
33
-
34
- // Default fallback to proxy endpoint
35
- return `${baseUrl}/media/${valueStr}`;
36
- }
package/src/main.tsx DELETED
@@ -1,10 +0,0 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import './index.css'
4
- import App from './App.tsx'
5
-
6
- createRoot(document.getElementById('root')!).render(
7
- <StrictMode>
8
- <App />
9
- </StrictMode>,
10
- )
@@ -1,115 +0,0 @@
1
- import React, { useState } from "react";
2
- import { useDyrected } from "../../providers/dyrected-provider";
3
- import { Button } from "../../components/ui/button";
4
- import { Input } from "../../components/ui/input";
5
- import { Label } from "../../components/ui/label";
6
- import { toast } from "sonner";
7
-
8
- export function FirstUserPage({
9
- collectionSlug,
10
- onComplete
11
- }: {
12
- collectionSlug: string;
13
- onComplete: (data: any) => void
14
- }) {
15
- const { client } = useDyrected();
16
- const [email, setEmail] = useState("");
17
- const [password, setPassword] = useState("");
18
- const [confirmPassword, setConfirmPassword] = useState("");
19
- const [error, setError] = useState("");
20
- const [loading, setLoading] = useState(false);
21
-
22
- const handleSubmit = async (e: React.FormEvent) => {
23
- e.preventDefault();
24
- setError("");
25
-
26
- if (password !== confirmPassword) {
27
- setError("Passwords do not match");
28
- return;
29
- }
30
-
31
- setLoading(true);
32
-
33
- try {
34
- const data = await client!.collection(collectionSlug).registerFirstUser({
35
- email,
36
- password,
37
- });
38
- toast.success("Admin account created successfully");
39
- onComplete(data);
40
- } catch (err: any) {
41
- const message = err.message || "Failed to create initial user";
42
- setError(message);
43
- toast.error(message);
44
- } finally {
45
- setLoading(false);
46
- }
47
- };
48
-
49
- return (
50
- <div className="dy-flex dy-min-h-screen dy-items-center dy-justify-center dy-bg-background dy-px-4">
51
- <div className="dy-w-full dy-max-w-sm dy-space-y-8">
52
- <div className="dy-space-y-2 dy-text-center">
53
- <div className="dy-mx-auto dy-h-12 dy-w-12 dy-rounded-full dy-bg-primary/5 dy-flex dy-items-center dy-justify-center dy-mb-4">
54
- <div className="dy-h-6 dy-w-6 dy-rounded-full dy-border-2 dy-border-primary dy-border-t-transparent dy-animate-pulse" />
55
- </div>
56
- <h1 className="dy-text-2xl dy-font-semibold dy-tracking-tight">Setup Admin Account</h1>
57
- <p className="dy-text-sm dy-text-muted-foreground">Create the first administrative user to get started</p>
58
- </div>
59
-
60
- <form onSubmit={handleSubmit} className="dy-space-y-4">
61
- <div className="dy-space-y-2">
62
- <Label htmlFor="email">Admin Email</Label>
63
- <Input
64
- id="email"
65
- type="email"
66
- placeholder="admin@example.com"
67
- value={email}
68
- onChange={(e) => setEmail(e.target.value)}
69
- required
70
- className="dy-bg-transparent"
71
- />
72
- </div>
73
- <div className="dy-space-y-2">
74
- <Label htmlFor="password">Password</Label>
75
- <Input
76
- id="password"
77
- type="password"
78
- value={password}
79
- onChange={(e) => setPassword(e.target.value)}
80
- required
81
- className="dy-bg-transparent"
82
- />
83
- </div>
84
- <div className="dy-space-y-2">
85
- <Label htmlFor="confirm-password">Confirm Password</Label>
86
- <Input
87
- id="confirm-password"
88
- type="password"
89
- value={confirmPassword}
90
- onChange={(e) => setConfirmPassword(e.target.value)}
91
- required
92
- className="dy-bg-transparent"
93
- />
94
- </div>
95
-
96
- {error && (
97
- <div className="dy-text-xs dy-text-destructive dy-font-medium dy-bg-destructive/10 dy-p-3 dy-rounded-md">
98
- {error}
99
- </div>
100
- )}
101
-
102
- <Button type="submit" className="dy-w-full" disabled={loading}>
103
- {loading ? "Creating account..." : "Create Admin Account"}
104
- </Button>
105
- </form>
106
-
107
- <div className="dy-pt-4 dy-border-t dy-text-center dy-space-y-2">
108
- <p className="dy-text-[10px] dy-text-muted-foreground dy-uppercase dy-tracking-widest">
109
- Dyrected CMS · Initial Setup
110
- </p>
111
- </div>
112
- </div>
113
- </div>
114
- );
115
- }
@@ -1,91 +0,0 @@
1
- import React, { useState } from "react";
2
- import { useDyrected } from "../../providers/dyrected-provider";
3
- import { Button } from "../../components/ui/button";
4
- import { Input } from "../../components/ui/input";
5
- import { Label } from "../../components/ui/label";
6
- import { toast } from "sonner";
7
-
8
- export function LoginPage({
9
- collectionSlug,
10
- onLogin
11
- }: {
12
- collectionSlug: string;
13
- onLogin: (data: any) => void
14
- }) {
15
- const { client } = useDyrected();
16
- const [email, setEmail] = useState("");
17
- const [password, setPassword] = useState("");
18
- const [error, setError] = useState("");
19
- const [loading, setLoading] = useState(false);
20
-
21
- const handleSubmit = async (e: React.FormEvent) => {
22
- e.preventDefault();
23
- setError("");
24
- setLoading(true);
25
-
26
- try {
27
- const data = await client!.collection(collectionSlug).login(email, password);
28
- toast.success("Welcome back!");
29
- onLogin(data);
30
- } catch (err: any) {
31
- const message = err.message || "Invalid email or password";
32
- setError(message);
33
- toast.error(message);
34
- } finally {
35
- setLoading(false);
36
- }
37
- };
38
-
39
- return (
40
- <div className="dy-flex dy-min-h-screen dy-items-center dy-justify-center dy-bg-background dy-px-4">
41
- <div className="dy-w-full dy-max-w-sm dy-space-y-8">
42
- <div className="dy-space-y-2 dy-text-center">
43
- <h1 className="dy-text-2xl dy-font-semibold dy-tracking-tight">Welcome back</h1>
44
- <p className="dy-text-sm dy-text-muted-foreground">Enter your credentials to access the dashboard</p>
45
- </div>
46
-
47
- <form onSubmit={handleSubmit} className="dy-space-y-4">
48
- <div className="dy-space-y-2">
49
- <Label htmlFor="email">Email</Label>
50
- <Input
51
- id="email"
52
- type="email"
53
- placeholder="name@example.com"
54
- value={email}
55
- onChange={(e) => setEmail(e.target.value)}
56
- required
57
- className="dy-bg-transparent"
58
- />
59
- </div>
60
- <div className="dy-space-y-2">
61
- <div className="dy-flex dy-items-center dy-justify-between">
62
- <Label htmlFor="password">Password</Label>
63
- </div>
64
- <Input
65
- id="password"
66
- type="password"
67
- value={password}
68
- onChange={(e) => setPassword(e.target.value)}
69
- required
70
- className="dy-bg-transparent"
71
- />
72
- </div>
73
-
74
- {error && (
75
- <div className="dy-text-xs dy-text-destructive dy-font-medium dy-bg-destructive/10 dy-p-3 dy-rounded-md">
76
- {error}
77
- </div>
78
- )}
79
-
80
- <Button type="submit" className="dy-w-full" disabled={loading}>
81
- {loading ? "Signing in..." : "Sign In"}
82
- </Button>
83
- </form>
84
-
85
- <p className="dy-text-center dy-text-xs dy-text-muted-foreground dy-uppercase dy-tracking-widest">
86
- Dyrected CMS
87
- </p>
88
- </div>
89
- </div>
90
- );
91
- }