@structcms/admin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +180 -0
- package/dist/index.cjs +2761 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +736 -0
- package/dist/index.d.ts +736 -0
- package/dist/index.js +2686 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { AuthUser, AuthSession } from '@structcms/api';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import React__default, { ReactNode } from 'react';
|
|
5
|
+
import { Registry, FieldType, SectionData, NavigationItem } from '@structcms/core';
|
|
6
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
7
|
+
import { VariantProps } from 'class-variance-authority';
|
|
8
|
+
import { DefaultValues } from 'react-hook-form';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { ClassValue } from 'clsx';
|
|
11
|
+
|
|
12
|
+
interface AuthContextValue {
|
|
13
|
+
user: AuthUser | null;
|
|
14
|
+
session: AuthSession | null;
|
|
15
|
+
isLoading: boolean;
|
|
16
|
+
isAuthenticated: boolean;
|
|
17
|
+
signIn: (email: string, password: string) => Promise<void>;
|
|
18
|
+
signOut: () => Promise<void>;
|
|
19
|
+
refreshSession: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
interface AuthProviderProps {
|
|
22
|
+
children: React__default.ReactNode;
|
|
23
|
+
apiBaseUrl: string;
|
|
24
|
+
onAuthStateChange?: (user: AuthUser | null) => void;
|
|
25
|
+
}
|
|
26
|
+
declare function AuthProvider({ children, apiBaseUrl, onAuthStateChange }: AuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
27
|
+
declare function useAuth(): AuthContextValue;
|
|
28
|
+
|
|
29
|
+
interface SidebarNavItem {
|
|
30
|
+
label: string;
|
|
31
|
+
path: string;
|
|
32
|
+
}
|
|
33
|
+
interface AdminLayoutProps {
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
title?: string;
|
|
36
|
+
navItems?: SidebarNavItem[];
|
|
37
|
+
activePath?: string;
|
|
38
|
+
onNavigate: (path: string) => void;
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Admin layout with sidebar navigation, header, and content area.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <AdminLayout
|
|
47
|
+
* title="My CMS"
|
|
48
|
+
* onNavigate={(path) => router.push(path)}
|
|
49
|
+
* activePath="/pages"
|
|
50
|
+
* >
|
|
51
|
+
* <PageList ... />
|
|
52
|
+
* </AdminLayout>
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function AdminLayout({ children, title, navItems, activePath, onNavigate, className, }: AdminLayoutProps): react_jsx_runtime.JSX.Element;
|
|
56
|
+
declare namespace AdminLayout {
|
|
57
|
+
var displayName: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface StructCMSAdminAppProps {
|
|
61
|
+
registry: Registry;
|
|
62
|
+
apiBaseUrl?: string;
|
|
63
|
+
className?: string;
|
|
64
|
+
customNavItems?: SidebarNavItem[];
|
|
65
|
+
renderView?: (view: View) => React__default.ReactNode | null;
|
|
66
|
+
/**
|
|
67
|
+
* If true, wraps the app in AuthProvider. When enabled, you should provide login/auth handling.
|
|
68
|
+
* @default false
|
|
69
|
+
*/
|
|
70
|
+
enableAuth?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Callback when user authentication state changes
|
|
73
|
+
*/
|
|
74
|
+
onAuthStateChange?: (user: AuthContextValue['user']) => void;
|
|
75
|
+
}
|
|
76
|
+
type View = {
|
|
77
|
+
type: 'dashboard';
|
|
78
|
+
} | {
|
|
79
|
+
type: 'pages';
|
|
80
|
+
} | {
|
|
81
|
+
type: 'page-editor';
|
|
82
|
+
pageId?: string;
|
|
83
|
+
} | {
|
|
84
|
+
type: 'media';
|
|
85
|
+
} | {
|
|
86
|
+
type: 'navigation';
|
|
87
|
+
} | {
|
|
88
|
+
type: 'custom';
|
|
89
|
+
path: string;
|
|
90
|
+
};
|
|
91
|
+
declare function StructCMSAdminApp({ registry, apiBaseUrl, className, customNavItems, renderView: customRenderView, enableAuth, onAuthStateChange, }: StructCMSAdminAppProps): react_jsx_runtime.JSX.Element;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Configuration for the Admin context
|
|
95
|
+
*/
|
|
96
|
+
interface AdminContextValue {
|
|
97
|
+
registry: Registry;
|
|
98
|
+
apiBaseUrl: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Props for the AdminProvider component
|
|
102
|
+
*/
|
|
103
|
+
interface AdminProviderProps {
|
|
104
|
+
children: ReactNode;
|
|
105
|
+
registry: Registry;
|
|
106
|
+
apiBaseUrl?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Provider component that makes registry and API configuration available to all admin components.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```tsx
|
|
113
|
+
* import { AdminProvider } from '@structcms/admin';
|
|
114
|
+
* import { registry } from './registry';
|
|
115
|
+
*
|
|
116
|
+
* export default function AdminLayout({ children }) {
|
|
117
|
+
* return (
|
|
118
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
119
|
+
* {children}
|
|
120
|
+
* </AdminProvider>
|
|
121
|
+
* );
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function AdminProvider({ children, registry, apiBaseUrl, }: AdminProviderProps): react_jsx_runtime.JSX.Element;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Hook to access the Admin context.
|
|
129
|
+
* Must be used within an AdminProvider.
|
|
130
|
+
*
|
|
131
|
+
* @returns The admin context containing registry and apiBaseUrl
|
|
132
|
+
* @throws Error if used outside of AdminProvider
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```tsx
|
|
136
|
+
* function MyComponent() {
|
|
137
|
+
* const { registry, apiBaseUrl } = useAdmin();
|
|
138
|
+
* const sections = registry.getAllSections();
|
|
139
|
+
* // ...
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function useAdmin(): AdminContextValue;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* API response wrapper
|
|
147
|
+
*/
|
|
148
|
+
interface ApiResponse<T> {
|
|
149
|
+
data: T | null;
|
|
150
|
+
error: ApiError | null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* API error structure
|
|
154
|
+
*/
|
|
155
|
+
interface ApiError {
|
|
156
|
+
message: string;
|
|
157
|
+
code?: string;
|
|
158
|
+
status: number;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* API client interface for making requests to the CMS API
|
|
162
|
+
*/
|
|
163
|
+
interface ApiClient {
|
|
164
|
+
get<T>(path: string): Promise<ApiResponse<T>>;
|
|
165
|
+
post<T>(path: string, body: unknown): Promise<ApiResponse<T>>;
|
|
166
|
+
put<T>(path: string, body: unknown): Promise<ApiResponse<T>>;
|
|
167
|
+
delete<T>(path: string): Promise<ApiResponse<T>>;
|
|
168
|
+
upload<T>(path: string, body: FormData): Promise<ApiResponse<T>>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Hook to get an API client configured with the base URL from AdminProvider.
|
|
172
|
+
* Must be used within an AdminProvider.
|
|
173
|
+
*
|
|
174
|
+
* @returns An API client with get, post, put, delete methods
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```tsx
|
|
178
|
+
* function PageList() {
|
|
179
|
+
* const api = useApiClient();
|
|
180
|
+
*
|
|
181
|
+
* async function loadPages() {
|
|
182
|
+
* const { data, error } = await api.get<Page[]>('/pages');
|
|
183
|
+
* if (error) {
|
|
184
|
+
* console.error(error.message);
|
|
185
|
+
* return;
|
|
186
|
+
* }
|
|
187
|
+
* setPages(data);
|
|
188
|
+
* }
|
|
189
|
+
* }
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
declare function useApiClient(): ApiClient;
|
|
193
|
+
|
|
194
|
+
interface StringInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
195
|
+
label: string;
|
|
196
|
+
error?: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Text input component for string fields with label, placeholder, and validation error display.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```tsx
|
|
203
|
+
* <StringInput
|
|
204
|
+
* label="Title"
|
|
205
|
+
* placeholder="Enter title..."
|
|
206
|
+
* required
|
|
207
|
+
* {...register('title')}
|
|
208
|
+
* error={errors.title?.message}
|
|
209
|
+
* />
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
declare const StringInput: React.ForwardRefExoticComponent<StringInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
213
|
+
|
|
214
|
+
interface TextInputProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'rows'> {
|
|
215
|
+
label: string;
|
|
216
|
+
error?: string;
|
|
217
|
+
rows?: number;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Textarea component for long text fields with label, placeholder, and validation error display.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```tsx
|
|
224
|
+
* <TextInput
|
|
225
|
+
* label="Description"
|
|
226
|
+
* placeholder="Enter description..."
|
|
227
|
+
* rows={5}
|
|
228
|
+
* required
|
|
229
|
+
* {...register('description')}
|
|
230
|
+
* error={errors.description?.message}
|
|
231
|
+
* />
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
declare const TextInput: React.ForwardRefExoticComponent<TextInputProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
235
|
+
|
|
236
|
+
interface RichTextEditorProps {
|
|
237
|
+
label: string;
|
|
238
|
+
value?: string;
|
|
239
|
+
onChange?: (value: string) => void;
|
|
240
|
+
error?: string;
|
|
241
|
+
required?: boolean;
|
|
242
|
+
placeholder?: string;
|
|
243
|
+
className?: string;
|
|
244
|
+
id?: string;
|
|
245
|
+
name?: string;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* WYSIWYG editor for richtext fields using TipTap.
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* ```tsx
|
|
252
|
+
* <RichTextEditor
|
|
253
|
+
* label="Content"
|
|
254
|
+
* value={content}
|
|
255
|
+
* onChange={setContent}
|
|
256
|
+
* required
|
|
257
|
+
* error={errors.content?.message}
|
|
258
|
+
* />
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
declare function RichTextEditor({ label, value, onChange, error, required, placeholder, className, id, name, }: RichTextEditorProps): react_jsx_runtime.JSX.Element | null;
|
|
262
|
+
declare namespace RichTextEditor {
|
|
263
|
+
var displayName: string;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
interface LoginFormProps {
|
|
267
|
+
onSuccess?: () => void;
|
|
268
|
+
onError?: (error: Error) => void;
|
|
269
|
+
}
|
|
270
|
+
declare function LoginForm({ onSuccess, onError }: LoginFormProps): react_jsx_runtime.JSX.Element;
|
|
271
|
+
|
|
272
|
+
interface ProtectedRouteProps {
|
|
273
|
+
children: React__default.ReactNode;
|
|
274
|
+
fallback?: React__default.ReactNode;
|
|
275
|
+
loadingFallback?: React__default.ReactNode;
|
|
276
|
+
}
|
|
277
|
+
declare function ProtectedRoute({ children, fallback, loadingFallback }: ProtectedRouteProps): react_jsx_runtime.JSX.Element;
|
|
278
|
+
|
|
279
|
+
interface OAuthButtonProps {
|
|
280
|
+
provider: 'google' | 'github' | 'gitlab' | 'azure' | 'bitbucket';
|
|
281
|
+
apiBaseUrl: string;
|
|
282
|
+
redirectTo?: string;
|
|
283
|
+
children?: React__default.ReactNode;
|
|
284
|
+
className?: string;
|
|
285
|
+
}
|
|
286
|
+
declare function OAuthButton({ provider, apiBaseUrl, redirectTo, children, className, }: OAuthButtonProps): react_jsx_runtime.JSX.Element;
|
|
287
|
+
|
|
288
|
+
interface ImagePickerProps {
|
|
289
|
+
label: string;
|
|
290
|
+
value?: string;
|
|
291
|
+
onChange?: (value: string) => void;
|
|
292
|
+
onBrowse?: () => void;
|
|
293
|
+
error?: string;
|
|
294
|
+
required?: boolean;
|
|
295
|
+
className?: string;
|
|
296
|
+
id?: string;
|
|
297
|
+
name?: string;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Component for image fields that opens MediaBrowser for selection.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```tsx
|
|
304
|
+
* <ImagePicker
|
|
305
|
+
* label="Hero Image"
|
|
306
|
+
* value={imageUrl}
|
|
307
|
+
* onChange={setImageUrl}
|
|
308
|
+
* onBrowse={() => setMediaBrowserOpen(true)}
|
|
309
|
+
* required
|
|
310
|
+
* error={errors.image?.message}
|
|
311
|
+
* />
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
declare function ImagePicker({ label, value, onChange, onBrowse, error, required, className, id, name, }: ImagePickerProps): react_jsx_runtime.JSX.Element;
|
|
315
|
+
declare namespace ImagePicker {
|
|
316
|
+
var displayName: string;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
interface ArrayFieldProps<T> {
|
|
320
|
+
label: string;
|
|
321
|
+
value: T[];
|
|
322
|
+
onChange: (value: T[]) => void;
|
|
323
|
+
renderItem: (item: T, index: number, onChange: (item: T) => void) => React.ReactNode;
|
|
324
|
+
createDefaultItem: () => T;
|
|
325
|
+
error?: string;
|
|
326
|
+
required?: boolean;
|
|
327
|
+
className?: string;
|
|
328
|
+
id?: string;
|
|
329
|
+
name?: string;
|
|
330
|
+
}
|
|
331
|
+
declare const ArrayField: <T>(props: ArrayFieldProps<T> & {
|
|
332
|
+
ref?: React.ForwardedRef<HTMLDivElement>;
|
|
333
|
+
}) => React.ReactElement;
|
|
334
|
+
|
|
335
|
+
interface ObjectFieldProps {
|
|
336
|
+
label: string;
|
|
337
|
+
children: React.ReactNode;
|
|
338
|
+
error?: string;
|
|
339
|
+
required?: boolean;
|
|
340
|
+
className?: string;
|
|
341
|
+
id?: string;
|
|
342
|
+
name?: string;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Component for nested object fields, rendering sub-form with visual grouping.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```tsx
|
|
349
|
+
* <ObjectField label="Address" required>
|
|
350
|
+
* <StringInput label="Street" {...register('address.street')} />
|
|
351
|
+
* <StringInput label="City" {...register('address.city')} />
|
|
352
|
+
* <StringInput label="Zip" {...register('address.zip')} />
|
|
353
|
+
* </ObjectField>
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
declare function ObjectField({ label, children, error, required, className, id, name }: ObjectFieldProps): react_jsx_runtime.JSX.Element;
|
|
357
|
+
declare namespace ObjectField {
|
|
358
|
+
var displayName: string;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
362
|
+
}
|
|
363
|
+
declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
|
|
364
|
+
|
|
365
|
+
interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
366
|
+
}
|
|
367
|
+
declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
368
|
+
|
|
369
|
+
declare const labelVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
|
|
370
|
+
interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement>, VariantProps<typeof labelVariants> {
|
|
371
|
+
}
|
|
372
|
+
declare const Label: React.ForwardRefExoticComponent<LabelProps & React.RefAttributes<HTMLLabelElement>>;
|
|
373
|
+
|
|
374
|
+
declare const buttonVariants: (props?: ({
|
|
375
|
+
variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
376
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
377
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
378
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
379
|
+
}
|
|
380
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Resolves the FieldType from a Zod schema by unwrapping wrappers first
|
|
384
|
+
*/
|
|
385
|
+
declare function resolveFieldType(schema: z.ZodTypeAny): FieldType | null;
|
|
386
|
+
/**
|
|
387
|
+
* Converts a camelCase or snake_case field name to a human-readable label
|
|
388
|
+
*/
|
|
389
|
+
declare function fieldNameToLabel(name: string): string;
|
|
390
|
+
interface FormGeneratorProps<T extends z.ZodObject<z.ZodRawShape>> {
|
|
391
|
+
schema: T;
|
|
392
|
+
onSubmit: (data: z.infer<T>) => void;
|
|
393
|
+
onChange?: (data: z.infer<T>) => void;
|
|
394
|
+
defaultValues?: DefaultValues<z.infer<T>>;
|
|
395
|
+
submitLabel?: string;
|
|
396
|
+
className?: string;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Generates a React Hook Form from a Zod schema, mapping field types to input components.
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ```tsx
|
|
403
|
+
* const schema = z.object({
|
|
404
|
+
* title: fields.string().min(1),
|
|
405
|
+
* description: fields.text(),
|
|
406
|
+
* content: fields.richtext(),
|
|
407
|
+
* });
|
|
408
|
+
*
|
|
409
|
+
* <FormGenerator
|
|
410
|
+
* schema={schema}
|
|
411
|
+
* onSubmit={(data) => console.log(data)}
|
|
412
|
+
* submitLabel="Save"
|
|
413
|
+
* />
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
declare function FormGenerator<T extends z.ZodObject<z.ZodRawShape>>({ schema, onSubmit, onChange, defaultValues, submitLabel, className, }: FormGeneratorProps<T>): react_jsx_runtime.JSX.Element;
|
|
417
|
+
declare namespace FormGenerator {
|
|
418
|
+
var displayName: string;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
interface SectionEditorProps {
|
|
422
|
+
sectionType: string;
|
|
423
|
+
data?: Record<string, unknown>;
|
|
424
|
+
onChange: (data: Record<string, unknown>) => void;
|
|
425
|
+
submitLabel?: string;
|
|
426
|
+
className?: string;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Component that renders a form for a section based on its schema from the registry.
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```tsx
|
|
433
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
434
|
+
* <SectionEditor
|
|
435
|
+
* sectionType="hero"
|
|
436
|
+
* data={{ title: 'Hello', subtitle: 'World' }}
|
|
437
|
+
* onChange={(data) => console.log(data)}
|
|
438
|
+
* />
|
|
439
|
+
* </AdminProvider>
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
declare function SectionEditor({ sectionType, data, onChange, submitLabel, className, }: SectionEditorProps): react_jsx_runtime.JSX.Element;
|
|
443
|
+
declare namespace SectionEditor {
|
|
444
|
+
var displayName: string;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
interface PageEditorProps {
|
|
448
|
+
sections: SectionData[];
|
|
449
|
+
allowedSections: string[];
|
|
450
|
+
onSave: (sections: SectionData[]) => void;
|
|
451
|
+
className?: string;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Full page editor with multiple sections, add/remove/reorder sections.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```tsx
|
|
458
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
459
|
+
* <PageEditor
|
|
460
|
+
* sections={page.sections}
|
|
461
|
+
* allowedSections={['hero', 'content', 'cta']}
|
|
462
|
+
* onSave={(sections) => savePage({ ...page, sections })}
|
|
463
|
+
* />
|
|
464
|
+
* </AdminProvider>
|
|
465
|
+
* ```
|
|
466
|
+
*/
|
|
467
|
+
declare function PageEditor({ sections: initialSections, allowedSections, onSave, className, }: PageEditorProps): react_jsx_runtime.JSX.Element;
|
|
468
|
+
declare namespace PageEditor {
|
|
469
|
+
var displayName: string;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
interface PageSummary {
|
|
473
|
+
id: string;
|
|
474
|
+
title: string;
|
|
475
|
+
slug: string;
|
|
476
|
+
pageType: string;
|
|
477
|
+
updatedAt?: string;
|
|
478
|
+
}
|
|
479
|
+
interface PageListProps {
|
|
480
|
+
onSelectPage: (page: PageSummary) => void;
|
|
481
|
+
onCreatePage: () => void;
|
|
482
|
+
className?: string;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* List all pages with filter/search, link to edit each page.
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* ```tsx
|
|
489
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
490
|
+
* <PageList
|
|
491
|
+
* onSelectPage={(page) => router.push(`/admin/pages/${page.id}`)}
|
|
492
|
+
* onCreatePage={() => router.push('/admin/pages/new')}
|
|
493
|
+
* />
|
|
494
|
+
* </AdminProvider>
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
declare function PageList({ onSelectPage, onCreatePage, className }: PageListProps): react_jsx_runtime.JSX.Element;
|
|
498
|
+
declare namespace PageList {
|
|
499
|
+
var displayName: string;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
interface NavigationEditorProps {
|
|
503
|
+
items: NavigationItem[];
|
|
504
|
+
onSave: (items: NavigationItem[]) => void;
|
|
505
|
+
className?: string;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Editor for navigation items with nested structure support (one level).
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* ```tsx
|
|
512
|
+
* <NavigationEditor
|
|
513
|
+
* items={navigation.items}
|
|
514
|
+
* onSave={(items) => saveNavigation({ ...navigation, items })}
|
|
515
|
+
* />
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
declare function NavigationEditor({ items: initialItems, onSave, className }: NavigationEditorProps): react_jsx_runtime.JSX.Element;
|
|
519
|
+
declare namespace NavigationEditor {
|
|
520
|
+
var displayName: string;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
interface MediaItem {
|
|
524
|
+
id: string;
|
|
525
|
+
url: string;
|
|
526
|
+
filename: string;
|
|
527
|
+
mimeType?: string;
|
|
528
|
+
createdAt?: string;
|
|
529
|
+
}
|
|
530
|
+
interface MediaBrowserProps {
|
|
531
|
+
onSelect?: (item: MediaItem) => void;
|
|
532
|
+
className?: string;
|
|
533
|
+
pageSize?: number;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Browse, upload, and select media files.
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* ```tsx
|
|
540
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
541
|
+
* <MediaBrowser onSelect={(item) => setImageUrl(item.url)} />
|
|
542
|
+
* </AdminProvider>
|
|
543
|
+
* ```
|
|
544
|
+
*/
|
|
545
|
+
declare function MediaBrowser({ onSelect, className, pageSize }: MediaBrowserProps): react_jsx_runtime.JSX.Element;
|
|
546
|
+
declare namespace MediaBrowser {
|
|
547
|
+
var displayName: string;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
551
|
+
width?: string | number;
|
|
552
|
+
height?: string | number;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Skeleton loading placeholder with pulse animation.
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* ```tsx
|
|
559
|
+
* <Skeleton className="h-4 w-48" />
|
|
560
|
+
* <Skeleton width={200} height={20} />
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
declare function Skeleton({ className, width, height, style, ...props }: SkeletonProps): react_jsx_runtime.JSX.Element;
|
|
564
|
+
declare namespace Skeleton {
|
|
565
|
+
var displayName: string;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
type ToastVariant = 'default' | 'success' | 'error';
|
|
569
|
+
interface Toast {
|
|
570
|
+
id: string;
|
|
571
|
+
message: string;
|
|
572
|
+
variant?: ToastVariant;
|
|
573
|
+
}
|
|
574
|
+
interface ToastProviderProps {
|
|
575
|
+
children: React.ReactNode;
|
|
576
|
+
autoDismissMs?: number;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Provider for toast notifications. Wrap your app with this to enable useToast().
|
|
580
|
+
*
|
|
581
|
+
* @example
|
|
582
|
+
* ```tsx
|
|
583
|
+
* <ToastProvider>
|
|
584
|
+
* <App />
|
|
585
|
+
* </ToastProvider>
|
|
586
|
+
* ```
|
|
587
|
+
*/
|
|
588
|
+
declare function ToastProvider({ children, autoDismissMs }: ToastProviderProps): react_jsx_runtime.JSX.Element;
|
|
589
|
+
declare namespace ToastProvider {
|
|
590
|
+
var displayName: string;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Hook to trigger toast notifications. Must be used within a ToastProvider.
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```tsx
|
|
597
|
+
* const { toast } = useToast();
|
|
598
|
+
* toast('Page saved!', 'success');
|
|
599
|
+
* toast('Something went wrong', 'error');
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
declare function useToast(): {
|
|
603
|
+
toast: (message: string, variant?: ToastVariant) => void;
|
|
604
|
+
dismiss: (id: string) => void;
|
|
605
|
+
toasts: Toast[];
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
interface ErrorBoundaryProps {
|
|
609
|
+
children: React.ReactNode;
|
|
610
|
+
fallback?: React.ReactNode;
|
|
611
|
+
onReset?: () => void;
|
|
612
|
+
className?: string;
|
|
613
|
+
}
|
|
614
|
+
interface ErrorBoundaryState {
|
|
615
|
+
hasError: boolean;
|
|
616
|
+
error: Error | null;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Error boundary for catching component-level render errors.
|
|
620
|
+
*
|
|
621
|
+
* @example
|
|
622
|
+
* ```tsx
|
|
623
|
+
* <ErrorBoundary fallback={<p>Something went wrong</p>}>
|
|
624
|
+
* <MyComponent />
|
|
625
|
+
* </ErrorBoundary>
|
|
626
|
+
* ```
|
|
627
|
+
*/
|
|
628
|
+
declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
629
|
+
constructor(props: ErrorBoundaryProps);
|
|
630
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
631
|
+
handleReset: () => void;
|
|
632
|
+
render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
interface DialogProps {
|
|
636
|
+
open: boolean;
|
|
637
|
+
onClose: () => void;
|
|
638
|
+
children: React.ReactNode;
|
|
639
|
+
className?: string;
|
|
640
|
+
title?: string;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Minimal dialog component using React Portal.
|
|
644
|
+
* Renders a modal overlay with backdrop that closes on outside click or Escape key.
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* ```tsx
|
|
648
|
+
* <Dialog open={isOpen} onClose={() => setIsOpen(false)} title="Select Media">
|
|
649
|
+
* <MediaBrowser onSelect={handleSelect} />
|
|
650
|
+
* </Dialog>
|
|
651
|
+
* ```
|
|
652
|
+
*/
|
|
653
|
+
declare function Dialog({ open, onClose, children, className, title }: DialogProps): React.ReactPortal | null;
|
|
654
|
+
declare namespace Dialog {
|
|
655
|
+
var displayName: string;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
interface DashboardPageProps {
|
|
659
|
+
onSelectPage: (page: PageSummary) => void;
|
|
660
|
+
onCreatePage: () => void;
|
|
661
|
+
onUploadMedia: () => void;
|
|
662
|
+
className?: string;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Main dashboard page composing KPI cards, recent pages, and quick actions.
|
|
666
|
+
* This is the default admin entry point.
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* ```tsx
|
|
670
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
671
|
+
* <DashboardPage
|
|
672
|
+
* onSelectPage={(page) => router.push(`/admin/pages/${page.id}`)}
|
|
673
|
+
* onCreatePage={() => router.push('/admin/pages/new')}
|
|
674
|
+
* onUploadMedia={() => router.push('/admin/media')}
|
|
675
|
+
* />
|
|
676
|
+
* </AdminProvider>
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
679
|
+
declare function DashboardPage({ onSelectPage, onCreatePage, onUploadMedia, className, }: DashboardPageProps): react_jsx_runtime.JSX.Element;
|
|
680
|
+
declare namespace DashboardPage {
|
|
681
|
+
var displayName: string;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
interface QuickActionsProps {
|
|
685
|
+
onCreatePage: () => void;
|
|
686
|
+
onUploadMedia: () => void;
|
|
687
|
+
className?: string;
|
|
688
|
+
}
|
|
689
|
+
declare function QuickActions({ onCreatePage, onUploadMedia, className }: QuickActionsProps): react_jsx_runtime.JSX.Element;
|
|
690
|
+
|
|
691
|
+
interface KpiCardsProps {
|
|
692
|
+
className?: string;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Dashboard KPI cards displaying content metrics.
|
|
696
|
+
*
|
|
697
|
+
* Fetches pages, media, and navigation counts from the API in parallel.
|
|
698
|
+
* Sections count is derived synchronously from the registry.
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```tsx
|
|
702
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
703
|
+
* <KpiCards />
|
|
704
|
+
* </AdminProvider>
|
|
705
|
+
* ```
|
|
706
|
+
*/
|
|
707
|
+
declare function KpiCards({ className }: KpiCardsProps): react_jsx_runtime.JSX.Element;
|
|
708
|
+
declare namespace KpiCards {
|
|
709
|
+
var displayName: string;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
interface RecentPagesProps {
|
|
713
|
+
onSelectPage: (page: PageSummary) => void;
|
|
714
|
+
className?: string;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Displays a list of recently updated pages (max 10, sorted by updatedAt DESC).
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```tsx
|
|
721
|
+
* <AdminProvider registry={registry} apiBaseUrl="/api/cms">
|
|
722
|
+
* <RecentPages onSelectPage={(page) => router.push(`/admin/pages/${page.id}`)} />
|
|
723
|
+
* </AdminProvider>
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
declare function RecentPages({ onSelectPage, className }: RecentPagesProps): react_jsx_runtime.JSX.Element;
|
|
727
|
+
declare namespace RecentPages {
|
|
728
|
+
var displayName: string;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Utility function to merge Tailwind CSS classes with clsx
|
|
733
|
+
*/
|
|
734
|
+
declare function cn(...inputs: ClassValue[]): string;
|
|
735
|
+
|
|
736
|
+
export { type AdminContextValue, AdminLayout, type AdminLayoutProps, AdminProvider, type AdminProviderProps, type ApiClient, type ApiError, type ApiResponse, ArrayField, type ArrayFieldProps, type AuthContextValue, AuthProvider, type AuthProviderProps, Button, type ButtonProps, DashboardPage, type DashboardPageProps, Dialog, type DialogProps, ErrorBoundary, type ErrorBoundaryProps, FormGenerator, type FormGeneratorProps, ImagePicker, type ImagePickerProps, Input, type InputProps, KpiCards, type KpiCardsProps, Label, type LabelProps, LoginForm, type LoginFormProps, MediaBrowser, type MediaBrowserProps, type MediaItem, NavigationEditor, type NavigationEditorProps, OAuthButton, type OAuthButtonProps, ObjectField, type ObjectFieldProps, PageEditor, type PageEditorProps, PageList, type PageListProps, type PageSummary, ProtectedRoute, type ProtectedRouteProps, QuickActions, type QuickActionsProps, RecentPages, type RecentPagesProps, RichTextEditor, type RichTextEditorProps, SectionEditor, type SectionEditorProps, type SidebarNavItem, Skeleton, type SkeletonProps, StringInput, type StringInputProps, StructCMSAdminApp, type StructCMSAdminAppProps, TextInput, type TextInputProps, Textarea, type TextareaProps, type Toast, ToastProvider, type ToastProviderProps, type ToastVariant, type View, buttonVariants, cn, fieldNameToLabel, resolveFieldType, useAdmin, useApiClient, useAuth, useToast };
|