@openenvx/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/README.md ADDED
@@ -0,0 +1,449 @@
1
+ # @openenvx/admin
2
+
3
+ Zero-config admin panel for Drizzle ORM powered by Refine and shadcn/ui.
4
+
5
+ ## Features
6
+
7
+ - **Zero Configuration**: Works out of the box with your Drizzle schema
8
+ - **Runtime Generated**: Admin UI updates automatically when schema changes
9
+ - **Full CRUD**: List, create, edit, and delete operations for all tables
10
+ - **Type Safe**: Full TypeScript support with Drizzle type inference
11
+ - **Customizable**: Override any part of the UI or behavior
12
+ - **Modern Stack**: Built on Refine, shadcn/ui, and Tailwind CSS
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # Install @openenvx/admin and required peer dependencies
18
+ npm install @openenvx/admin @refinedev/core @refinedev/react-table @tanstack/react-table drizzle-orm
19
+
20
+ # Install Refine's shadcn/ui components
21
+ npx shadcn@latest add https://ui.refine.dev/r/views.json
22
+ npx shadcn@latest add https://ui.refine.dev/r/data-table.json
23
+ npx shadcn@latest add https://ui.refine.dev/r/layout/layout-01.json
24
+ npx shadcn@latest add https://ui.refine.dev/r/buttons.json
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### 1. Create Data Provider (Server)
30
+
31
+ ```typescript
32
+ // app/api/admin/[...resource]/route.ts
33
+ import { createDrizzleDataProvider } from '@openenvx/admin/server';
34
+ import { drizzle } from 'drizzle-orm/node-postgres';
35
+ import { Pool } from 'pg';
36
+ import * as schema from '@/db/schema';
37
+
38
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
39
+ const db = drizzle(pool, { schema });
40
+
41
+ // Create data provider with resource mapping
42
+ const dataProvider = createDrizzleDataProvider({
43
+ db,
44
+ resources: {
45
+ users: 'users',
46
+ posts: 'posts',
47
+ // Map resource names to table names
48
+ }
49
+ });
50
+
51
+ // Export route handlers
52
+ export const { GET, POST, PUT, DELETE } = dataProvider;
53
+ ```
54
+
55
+ ### 2. Set up Refine with Admin Resources (Client)
56
+
57
+ ```typescript
58
+ // app/admin/layout.tsx
59
+ 'use client';
60
+
61
+ import { Refine } from '@refinedev/core';
62
+ import routerProvider from '@refinedev/nextjs-router';
63
+ import dataProvider from '@refinedev/simple-rest';
64
+ import { Layout } from '@/components/refine-ui/layout/layout-01';
65
+ import { createAdminResources } from '@openenvx/admin';
66
+ import * as schema from '@/db/schema';
67
+
68
+ const resources = createAdminResources({
69
+ schema,
70
+ exclude: ['_drizzle_migrations'],
71
+ resources: {
72
+ users: {
73
+ label: 'Team Members',
74
+ meta: { icon: 'Users' }
75
+ },
76
+ posts: {
77
+ label: 'Blog Posts',
78
+ meta: { icon: 'FileText' }
79
+ }
80
+ }
81
+ });
82
+
83
+ export default function AdminLayout({ children }: { children: React.ReactNode }) {
84
+ return (
85
+ <Refine
86
+ routerProvider={routerProvider}
87
+ dataProvider={dataProvider('/api/admin')}
88
+ resources={resources}
89
+ options={{
90
+ syncWithLocation: true,
91
+ warnWhenUnsavedChanges: true,
92
+ useNewQueryKeys: true,
93
+ projectId: 'my-admin',
94
+ }}
95
+ >
96
+ <Layout>{children}</Layout>
97
+ </Refine>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ### 3. Create List Page using Refine's shadcn/ui Components
103
+
104
+ ```typescript
105
+ // app/admin/[resource]/page.tsx
106
+ 'use client';
107
+
108
+ import { useMemo } from 'react';
109
+ import { useTable } from '@refinedev/react-table';
110
+ import type { ColumnDef } from '@tanstack/react-table';
111
+ import { useResourceParams } from '@refinedev/core';
112
+ import { ListView, ListViewHeader } from '@/components/refine-ui/views/list-view';
113
+ import { DataTable } from '@/components/refine-ui/data-table/data-table';
114
+ import { DataTableSorter } from '@/components/refine-ui/data-table/data-table-sorter';
115
+ import { DataTableFilterDropdownText } from '@/components/refine-ui/data-table/data-table-filter';
116
+ import { CreateButton } from '@/components/refine-ui/buttons/create-button';
117
+
118
+ interface Post {
119
+ id: number;
120
+ title: string;
121
+ status: string;
122
+ createdAt: string;
123
+ }
124
+
125
+ export default function PostListPage() {
126
+ const { resource } = useResourceParams();
127
+
128
+ const columns = useMemo<ColumnDef<Post>[]>(
129
+ () => [
130
+ {
131
+ id: 'id',
132
+ accessorKey: 'id',
133
+ header: ({ column }) => (
134
+ <div className="flex items-center gap-1">
135
+ <span>ID</span>
136
+ <DataTableSorter column={column} />
137
+ </div>
138
+ ),
139
+ },
140
+ {
141
+ id: 'title',
142
+ accessorKey: 'title',
143
+ header: ({ column, table }) => (
144
+ <div className="flex items-center gap-1">
145
+ <span>Title</span>
146
+ <DataTableFilterDropdownText
147
+ defaultOperator="contains"
148
+ column={column}
149
+ table={table}
150
+ placeholder="Filter by title"
151
+ />
152
+ </div>
153
+ ),
154
+ },
155
+ {
156
+ id: 'status',
157
+ accessorKey: 'status',
158
+ header: 'Status',
159
+ },
160
+ {
161
+ id: 'createdAt',
162
+ accessorKey: 'createdAt',
163
+ header: ({ column }) => (
164
+ <div className="flex items-center gap-1">
165
+ <span>Created</span>
166
+ <DataTableSorter column={column} />
167
+ </div>
168
+ ),
169
+ cell: ({ row }) => new Date(row.original.createdAt).toLocaleDateString(),
170
+ },
171
+ ],
172
+ []
173
+ );
174
+
175
+ const table = useTable<Post>({
176
+ columns,
177
+ refineCoreProps: {
178
+ resource: resource?.name || 'posts',
179
+ },
180
+ });
181
+
182
+ return (
183
+ <ListView>
184
+ <ListViewHeader
185
+ title={resource?.meta?.label || resource?.name || 'Posts'}
186
+ headerButtons={<CreateButton resource={resource?.name} />}
187
+ />
188
+ <DataTable table={table} />
189
+ </ListView>
190
+ );
191
+ }
192
+ ```
193
+
194
+ ### 4. Create Edit Page
195
+
196
+ ```typescript
197
+ // app/admin/[resource]/edit/[id]/page.tsx
198
+ 'use client';
199
+
200
+ import { useForm } from '@refinedev/react-hook-form';
201
+ import { useResourceParams } from '@refinedev/core';
202
+ import { EditView, EditViewHeader } from '@/components/refine-ui/views/edit-view';
203
+ import { Input } from '@/components/ui/input';
204
+ import { Label } from '@/components/ui/label';
205
+ import {
206
+ Select,
207
+ SelectContent,
208
+ SelectItem,
209
+ SelectTrigger,
210
+ SelectValue,
211
+ } from '@/components/ui/select';
212
+
213
+ export default function PostEditPage() {
214
+ const { resource } = useResourceParams();
215
+ const {
216
+ refineCore: { onFinish, formLoading },
217
+ register,
218
+ handleSubmit,
219
+ formState: { errors },
220
+ } = useForm({
221
+ refineCoreProps: {
222
+ resource: resource?.name,
223
+ },
224
+ });
225
+
226
+ return (
227
+ <EditView>
228
+ <EditViewHeader title={`Edit ${resource?.meta?.label || resource?.name}`} />
229
+ <form onSubmit={handleSubmit(onFinish)}>
230
+ <div className="space-y-4">
231
+ <div>
232
+ <Label htmlFor="title">Title</Label>
233
+ <Input
234
+ id="title"
235
+ {...register('title', { required: 'Title is required' })}
236
+ />
237
+ {errors.title && (
238
+ <span className="text-red-500 text-sm">{errors.title.message}</span>
239
+ )}
240
+ </div>
241
+
242
+ <div>
243
+ <Label htmlFor="status">Status</Label>
244
+ <Select {...register('status')}>
245
+ <SelectTrigger>
246
+ <SelectValue placeholder="Select status" />
247
+ </SelectTrigger>
248
+ <SelectContent>
249
+ <SelectItem value="draft">Draft</SelectItem>
250
+ <SelectItem value="published">Published</SelectItem>
251
+ </SelectContent>
252
+ </Select>
253
+ </div>
254
+
255
+ <div>
256
+ <Label htmlFor="content">Content</Label>
257
+ <textarea
258
+ id="content"
259
+ {...register('content')}
260
+ className="w-full min-h-[200px] p-2 border rounded"
261
+ />
262
+ </div>
263
+ </div>
264
+ </form>
265
+ </EditView>
266
+ );
267
+ }
268
+ ```
269
+
270
+ ### 5. Create Show Page
271
+
272
+ ```typescript
273
+ // app/admin/[resource]/show/[id]/page.tsx
274
+ 'use client';
275
+
276
+ import { useShow } from '@refinedev/core';
277
+ import { useResourceParams } from '@refinedev/core';
278
+ import { ShowView, ShowViewHeader } from '@/components/refine-ui/views/show-view';
279
+ import { TextField } from '@/components/refine-ui/fields/text-field';
280
+ import { DateField } from '@/components/refine-ui/fields/date-field';
281
+
282
+ interface Post {
283
+ id: number;
284
+ title: string;
285
+ content: string;
286
+ status: string;
287
+ createdAt: string;
288
+ updatedAt: string;
289
+ }
290
+
291
+ export default function PostShowPage() {
292
+ const { resource } = useResourceParams();
293
+ const { queryResult } = useShow<Post>({
294
+ resource: resource?.name,
295
+ });
296
+
297
+ const { data, isLoading } = queryResult;
298
+ const record = data?.data;
299
+
300
+ if (isLoading) {
301
+ return <div>Loading...</div>;
302
+ }
303
+
304
+ return (
305
+ <ShowView>
306
+ <ShowViewHeader title={record?.title} />
307
+ <div className="space-y-4">
308
+ <div>
309
+ <label className="font-medium">Title</label>
310
+ <TextField value={record?.title} />
311
+ </div>
312
+ <div>
313
+ <label className="font-medium">Status</label>
314
+ <div className="capitalize">{record?.status}</div>
315
+ </div>
316
+ <div>
317
+ <label className="font-medium">Content</label>
318
+ <div className="whitespace-pre-wrap">{record?.content}</div>
319
+ </div>
320
+ <div className="grid grid-cols-2 gap-4">
321
+ <div>
322
+ <label className="font-medium">Created</label>
323
+ <DateField value={record?.createdAt} />
324
+ </div>
325
+ <div>
326
+ <label className="font-medium">Updated</label>
327
+ <DateField value={record?.updatedAt} />
328
+ </div>
329
+ </div>
330
+ </div>
331
+ </ShowView>
332
+ );
333
+ }
334
+ ```
335
+
336
+ ## Configuration
337
+
338
+ ### `createAdminResources(options)`
339
+
340
+ Generate Refine resources from your Drizzle schema.
341
+
342
+ ```typescript
343
+ const resources = createAdminResources({
344
+ // Your Drizzle schema exports
345
+ schema,
346
+
347
+ // Tables to exclude
348
+ exclude: ['migrations', 'sessions'],
349
+
350
+ // Custom configurations per resource
351
+ resources: {
352
+ users: {
353
+ label: 'Team Members', // Display name
354
+ icon: 'Users', // Lucide icon name
355
+ hidden: false, // Hide from navigation
356
+ meta: { // Custom metadata
357
+ icon: 'Users',
358
+ label: 'Team Members',
359
+ canDelete: false, // Disable delete button
360
+ }
361
+ }
362
+ },
363
+
364
+ // Default icon for resources
365
+ defaultIcon: 'FileText'
366
+ });
367
+ ```
368
+
369
+ ### `createDrizzleDataProvider(options)`
370
+
371
+ Create a Refine data provider for Drizzle ORM.
372
+
373
+ ```typescript
374
+ const dataProvider = createDrizzleDataProvider({
375
+ // Drizzle database instance with query support
376
+ db,
377
+
378
+ // Resource name to table name mapping
379
+ resources: {
380
+ users: 'users',
381
+ posts: 'posts',
382
+ // 'resource-name': 'table_name'
383
+ }
384
+ });
385
+ ```
386
+
387
+ ## Available Refine shadcn/ui Components
388
+
389
+ ### Views
390
+ - `ListView` / `ListViewHeader` - List page layout
391
+ - `CreateView` / `CreateViewHeader` - Create page layout
392
+ - `EditView` / `EditViewHeader` - Edit page layout
393
+ - `ShowView` / `ShowViewHeader` - Show page layout
394
+
395
+ ### Data Table
396
+ - `DataTable` - Advanced table with sorting, filtering, pagination
397
+ - `DataTableSorter` - Column sort buttons
398
+ - `DataTableFilterDropdownText` - Text filter dropdown
399
+ - `DataTableColumnHeader` - Column header with sort/filter
400
+
401
+ ### Buttons
402
+ - `CreateButton` - Navigate to create page
403
+ - `EditButton` - Navigate to edit page
404
+ - `DeleteButton` - Delete with confirmation
405
+ - `ShowButton` - Navigate to show page
406
+ - `ListButton` - Navigate to list page
407
+ - `SaveButton` - Save form
408
+ - `RefreshButton` - Refresh data
409
+
410
+ ### Fields
411
+ - `TextField` - Display text
412
+ - `DateField` - Display dates
413
+ - `BooleanField` - Display booleans
414
+ - `NumberField` - Display numbers
415
+ - `EmailField` - Display emails
416
+ - `UrlField` - Display URLs
417
+ - `TagField` - Display tags
418
+ - `MarkdownField` - Display markdown
419
+
420
+ ### Layout
421
+ - `Layout` - Complete app layout with sidebar, header, theme support
422
+ - `ThemedLayout` - Layout with theme provider
423
+ - `Breadcrumb` - Navigation breadcrumb
424
+
425
+ ### Forms
426
+ - `Form` - Form wrapper with validation
427
+ - `AutoSaveIndicator` - Shows auto-save status
428
+
429
+ ### Auth
430
+ - `SignInForm` - Login form
431
+ - `SignUpForm` - Registration form
432
+ - `ForgotPasswordForm` - Password reset
433
+
434
+ ### Utilities
435
+ - `ErrorComponent` - Error boundary
436
+ - `NotificationProvider` - Toast notifications
437
+ - `CanAccess` - Access control wrapper
438
+ - `Authenticated` - Auth guard wrapper
439
+
440
+ ## Resources
441
+
442
+ - [Refine Documentation](https://refine.dev/)
443
+ - [Refine shadcn/ui Integration](https://refine.dev/core/docs/ui-integrations/shadcn/introduction/)
444
+ - [shadcn/ui Documentation](https://ui.shadcn.com/)
445
+ - [Drizzle ORM Documentation](https://orm.drizzle.team/)
446
+
447
+ ## License
448
+
449
+ MIT
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Client-side exports for @openenvx/admin
3
+ *
4
+ * This package provides utilities to set up Refine with Drizzle ORM.
5
+ *
6
+ * For UI components, use Refine's shadcn/ui registry:
7
+ * npx shadcn@latest add https://ui.refine.dev/r/views.json
8
+ * npx shadcn@latest add https://ui.refine.dev/r/data-table.json
9
+ * npx shadcn@latest add https://ui.refine.dev/r/layout/layout-01.json
10
+ *
11
+ * @see https://refine.dev/core/docs/ui-integrations/shadcn/introduction/
12
+ */
13
+ export type { AdminResource, AdminResourcesConfig } from './resources';
14
+ export { createAdminResources } from './resources';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,77 @@
1
+ //#region src/client/resources.ts
2
+ /**
3
+ * Generate Refine resources from Drizzle schema
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import * as schema from './db/schema';
8
+ *
9
+ * const resources = createAdminResources({
10
+ * schema,
11
+ * exclude: ['migrations'],
12
+ * resources: {
13
+ * users: {
14
+ * label: 'Team Members',
15
+ * meta: { icon: 'Users' }
16
+ * }
17
+ * }
18
+ * });
19
+ * ```
20
+ */
21
+ function createAdminResources(config) {
22
+ const { schema, exclude = [], resources = {}, defaultIcon = "FileText" } = config;
23
+ const adminResources = [];
24
+ for (const [name, table] of Object.entries(schema)) {
25
+ if (exclude.includes(name) || isRelation(table)) continue;
26
+ if (!isTable(table)) continue;
27
+ const tableName = getTableName(table) || name;
28
+ const customConfig = resources[name] || {};
29
+ if (customConfig.hidden) continue;
30
+ const resource = {
31
+ name: tableName,
32
+ list: `/${tableName}`,
33
+ create: `/${tableName}/create`,
34
+ edit: `/${tableName}/edit/:id`,
35
+ show: `/${tableName}/show/:id`,
36
+ label: customConfig.label || capitalize(tableName),
37
+ icon: customConfig.icon || defaultIcon,
38
+ meta: {
39
+ label: customConfig.label || capitalize(tableName),
40
+ icon: customConfig.icon || defaultIcon,
41
+ ...customConfig.meta
42
+ },
43
+ ...customConfig
44
+ };
45
+ adminResources.push(resource);
46
+ }
47
+ return adminResources;
48
+ }
49
+ function isTable(value) {
50
+ if (typeof value !== "object" || value === null) return false;
51
+ const table = value;
52
+ return Symbol.for("drizzle:PgTable") in table || Symbol.for("drizzle:MySqlTable") in table || Symbol.for("drizzle:SQLiteTable") in table || typeof table[""] === "object" && table[""] !== null;
53
+ }
54
+ function isRelation(value) {
55
+ if (typeof value !== "object" || value === null) return false;
56
+ const rel = value;
57
+ return Symbol.for("drizzle:Relations") in rel || rel.config !== void 0 && typeof rel.config === "object";
58
+ }
59
+ function getTableName(table) {
60
+ if (typeof table !== "object" || table === null) return null;
61
+ const t = table;
62
+ const nameSymbol = Symbol.for("drizzle:Name");
63
+ if (nameSymbol in t) return t[nameSymbol];
64
+ if (typeof t[""] === "object" && t[""] !== null) {
65
+ const config = t[""];
66
+ const configNameSymbol = Symbol.for("drizzle:Name");
67
+ if (configNameSymbol in config) return config[configNameSymbol];
68
+ }
69
+ return null;
70
+ }
71
+ function capitalize(str) {
72
+ return str.charAt(0).toUpperCase() + str.slice(1);
73
+ }
74
+ //#endregion
75
+ export { createAdminResources };
76
+
77
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/client/resources.ts"],"sourcesContent":["/**\n * Generate Refine resources from Drizzle schema\n */\n\nimport type { ResourceProps } from '@refinedev/core';\n\nexport interface AdminResource extends ResourceProps {\n hidden?: boolean;\n icon?: string;\n label?: string;\n meta?: {\n label?: string;\n icon?: string;\n [key: string]: unknown;\n };\n}\n\nexport interface AdminResourcesConfig {\n /** Default icon for resources */\n defaultIcon?: string;\n /** Tables to exclude from the admin panel */\n exclude?: string[];\n /** Custom resource configurations */\n resources?: Record<string, Partial<AdminResource>>;\n /** Schema module exported from your Drizzle schema file */\n schema: Record<string, unknown>;\n}\n\n/**\n * Generate Refine resources from Drizzle schema\n *\n * @example\n * ```typescript\n * import * as schema from './db/schema';\n *\n * const resources = createAdminResources({\n * schema,\n * exclude: ['migrations'],\n * resources: {\n * users: {\n * label: 'Team Members',\n * meta: { icon: 'Users' }\n * }\n * }\n * });\n * ```\n */\nexport function createAdminResources(\n config: AdminResourcesConfig\n): AdminResource[] {\n const {\n schema,\n exclude = [],\n resources = {},\n defaultIcon = 'FileText',\n } = config;\n\n const adminResources: AdminResource[] = [];\n\n for (const [name, table] of Object.entries(schema)) {\n // Skip if it's a relation definition or excluded\n if (exclude.includes(name) || isRelation(table)) {\n continue;\n }\n\n // Check if it's a table (has the Drizzle table symbol)\n if (!isTable(table)) {\n continue;\n }\n\n // Get table name from the table object\n const tableName = getTableName(table) || name;\n\n // Get custom config for this resource\n const customConfig = resources[name] || {};\n\n // Skip if explicitly hidden\n if (customConfig.hidden) {\n continue;\n }\n\n const resource: AdminResource = {\n name: tableName,\n list: `/${tableName}`,\n create: `/${tableName}/create`,\n edit: `/${tableName}/edit/:id`,\n show: `/${tableName}/show/:id`,\n label: customConfig.label || capitalize(tableName),\n icon: customConfig.icon || defaultIcon,\n meta: {\n label: customConfig.label || capitalize(tableName),\n icon: customConfig.icon || defaultIcon,\n ...customConfig.meta,\n },\n ...customConfig,\n };\n\n adminResources.push(resource);\n }\n\n return adminResources;\n}\n\nfunction isTable(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const table = value as Record<string | symbol, unknown>;\n\n // Check for Drizzle table symbols\n return (\n Symbol.for('drizzle:PgTable') in table ||\n Symbol.for('drizzle:MySqlTable') in table ||\n Symbol.for('drizzle:SQLiteTable') in table ||\n // Fallback: check if it has table-specific properties\n (typeof table[''] === 'object' && table[''] !== null)\n );\n}\n\nfunction isRelation(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const rel = value as Record<string | symbol, unknown>;\n\n return (\n Symbol.for('drizzle:Relations') in rel ||\n (rel.config !== undefined && typeof rel.config === 'object')\n );\n}\n\nfunction getTableName(table: unknown): string | null {\n if (typeof table !== 'object' || table === null) {\n return null;\n }\n\n const t = table as Record<string | symbol, unknown>;\n\n // Try to get name from symbol\n const nameSymbol = Symbol.for('drizzle:Name');\n if (nameSymbol in t) {\n return t[nameSymbol] as string;\n }\n\n // Try to get from internal config\n if (typeof t[''] === 'object' && t[''] !== null) {\n const config = t[''] as Record<symbol, unknown>;\n const configNameSymbol = Symbol.for('drizzle:Name');\n if (configNameSymbol in config) {\n return config[configNameSymbol] as string;\n }\n }\n\n return null;\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,qBACd,QACiB;CACjB,MAAM,EACJ,QACA,UAAU,EAAE,EACZ,YAAY,EAAE,EACd,cAAc,eACZ;CAEJ,MAAM,iBAAkC,EAAE;AAE1C,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,EAAE;AAElD,MAAI,QAAQ,SAAS,KAAK,IAAI,WAAW,MAAM,CAC7C;AAIF,MAAI,CAAC,QAAQ,MAAM,CACjB;EAIF,MAAM,YAAY,aAAa,MAAM,IAAI;EAGzC,MAAM,eAAe,UAAU,SAAS,EAAE;AAG1C,MAAI,aAAa,OACf;EAGF,MAAM,WAA0B;GAC9B,MAAM;GACN,MAAM,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,UAAU;GACpB,MAAM,IAAI,UAAU;GACpB,OAAO,aAAa,SAAS,WAAW,UAAU;GAClD,MAAM,aAAa,QAAQ;GAC3B,MAAM;IACJ,OAAO,aAAa,SAAS,WAAW,UAAU;IAClD,MAAM,aAAa,QAAQ;IAC3B,GAAG,aAAa;IACjB;GACD,GAAG;GACJ;AAED,iBAAe,KAAK,SAAS;;AAG/B,QAAO;;AAGT,SAAS,QAAQ,OAAyB;AACxC,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;CAGT,MAAM,QAAQ;AAGd,QACE,OAAO,IAAI,kBAAkB,IAAI,SACjC,OAAO,IAAI,qBAAqB,IAAI,SACpC,OAAO,IAAI,sBAAsB,IAAI,SAEpC,OAAO,MAAM,QAAQ,YAAY,MAAM,QAAQ;;AAIpD,SAAS,WAAW,OAAyB;AAC3C,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;CAGT,MAAM,MAAM;AAEZ,QACE,OAAO,IAAI,oBAAoB,IAAI,OAClC,IAAI,WAAW,KAAA,KAAa,OAAO,IAAI,WAAW;;AAIvD,SAAS,aAAa,OAA+B;AACnD,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;CAGT,MAAM,IAAI;CAGV,MAAM,aAAa,OAAO,IAAI,eAAe;AAC7C,KAAI,cAAc,EAChB,QAAO,EAAE;AAIX,KAAI,OAAO,EAAE,QAAQ,YAAY,EAAE,QAAQ,MAAM;EAC/C,MAAM,SAAS,EAAE;EACjB,MAAM,mBAAmB,OAAO,IAAI,eAAe;AACnD,MAAI,oBAAoB,OACtB,QAAO,OAAO;;AAIlB,QAAO;;AAGT,SAAS,WAAW,KAAqB;AACvC,QAAO,IAAI,OAAO,EAAE,CAAC,aAAa,GAAG,IAAI,MAAM,EAAE"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Generate Refine resources from Drizzle schema
3
+ */
4
+ import type { ResourceProps } from '@refinedev/core';
5
+ export interface AdminResource extends ResourceProps {
6
+ hidden?: boolean;
7
+ icon?: string;
8
+ label?: string;
9
+ meta?: {
10
+ label?: string;
11
+ icon?: string;
12
+ [key: string]: unknown;
13
+ };
14
+ }
15
+ export interface AdminResourcesConfig {
16
+ /** Default icon for resources */
17
+ defaultIcon?: string;
18
+ /** Tables to exclude from the admin panel */
19
+ exclude?: string[];
20
+ /** Custom resource configurations */
21
+ resources?: Record<string, Partial<AdminResource>>;
22
+ /** Schema module exported from your Drizzle schema file */
23
+ schema: Record<string, unknown>;
24
+ }
25
+ /**
26
+ * Generate Refine resources from Drizzle schema
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * import * as schema from './db/schema';
31
+ *
32
+ * const resources = createAdminResources({
33
+ * schema,
34
+ * exclude: ['migrations'],
35
+ * resources: {
36
+ * users: {
37
+ * label: 'Team Members',
38
+ * meta: { icon: 'Users' }
39
+ * }
40
+ * }
41
+ * });
42
+ * ```
43
+ */
44
+ export declare function createAdminResources(config: AdminResourcesConfig): AdminResource[];
45
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/client/resources.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;IACnD,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,GAC3B,aAAa,EAAE,CAoDjB"}
@@ -0,0 +1,3 @@
1
+ import type { AdminConfig } from '../core/types';
2
+ export declare function defineAdminConfig(config: AdminConfig): AdminConfig;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAElE"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Configuration exports for @openenvx/admin
3
+ */
4
+ export type { AdminConfig, FormLayout, TableConfig } from '../core/types';
5
+ export { defineAdminConfig } from './config';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Schema introspector using Drizzle's native APIs
3
+ * Imports the schema module and extracts metadata using getTableConfig
4
+ */
5
+ import type { AdminSchema, TableSchema } from '../core/types';
6
+ /**
7
+ * Introspect a Drizzle schema module and extract all table metadata
8
+ */
9
+ export declare function introspectSchema(schemaPath: string): Promise<AdminSchema>;
10
+ /**
11
+ * Extract relations from the schema module
12
+ * Relations are defined using the `relations()` function from drizzle-orm
13
+ */
14
+ export declare function introspectRelations(schemaPath: string): Promise<Record<string, unknown>>;
15
+ /**
16
+ * Get list of all tables in the schema
17
+ */
18
+ export declare function getTableNames(schemaPath: string): Promise<string[]>;
19
+ /**
20
+ * Get a specific table's schema
21
+ */
22
+ export declare function getTableSchema(schemaPath: string, tableName: string): Promise<TableSchema | undefined>;
23
+ //# sourceMappingURL=introspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspector.d.ts","sourceRoot":"","sources":["../../src/core/introspector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAgB,WAAW,EAAE,MAAM,eAAe,CAAC;AAM5E;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CA4BtB;AAyHD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAYlC;AAeD;;GAEG;AACH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGzE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAGlC"}