shadcn-data-views 1.0.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/dist/index.css +3243 -0
- package/dist/index.d.mts +47 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +1897 -0
- package/dist/index.mjs +1868 -0
- package/package.json +89 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1868 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __objRest = (source, exclude) => {
|
|
21
|
+
var target = {};
|
|
22
|
+
for (var prop in source)
|
|
23
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
24
|
+
target[prop] = source[prop];
|
|
25
|
+
if (source != null && __getOwnPropSymbols)
|
|
26
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
27
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
28
|
+
target[prop] = source[prop];
|
|
29
|
+
}
|
|
30
|
+
return target;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/components/DataViews.tsx
|
|
34
|
+
import { useState as useState5, useEffect as useEffect2 } from "react";
|
|
35
|
+
|
|
36
|
+
// src/lib/schema.ts
|
|
37
|
+
var tableSchema = {
|
|
38
|
+
id: "tasks",
|
|
39
|
+
name: "Tasks",
|
|
40
|
+
icon: "\u{1F4CB}",
|
|
41
|
+
fields: [
|
|
42
|
+
{ id: "title", name: "Title", type: "text", isPrimary: true },
|
|
43
|
+
{ id: "description", name: "Description", type: "text" },
|
|
44
|
+
{
|
|
45
|
+
id: "status",
|
|
46
|
+
name: "Status",
|
|
47
|
+
type: "select",
|
|
48
|
+
options: [
|
|
49
|
+
{ id: "todo", name: "To Do", color: "gray" },
|
|
50
|
+
{ id: "in_progress", name: "In Progress", color: "blue" },
|
|
51
|
+
{ id: "review", name: "Review", color: "yellow" },
|
|
52
|
+
{ id: "done", name: "Done", color: "green" }
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "priority",
|
|
57
|
+
name: "Priority",
|
|
58
|
+
type: "select",
|
|
59
|
+
options: [
|
|
60
|
+
{ id: "low", name: "Low", color: "gray" },
|
|
61
|
+
{ id: "medium", name: "Medium", color: "yellow" },
|
|
62
|
+
{ id: "high", name: "High", color: "red" }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{ id: "dueDate", name: "Due Date", type: "date" },
|
|
66
|
+
{ id: "completed", name: "Completed", type: "checkbox" }
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
function getSelectField(schema) {
|
|
70
|
+
return schema.fields.find((f) => f.type === "select");
|
|
71
|
+
}
|
|
72
|
+
function getDateField(schema) {
|
|
73
|
+
return schema.fields.find((f) => f.type === "date");
|
|
74
|
+
}
|
|
75
|
+
function getPrimaryField(schema) {
|
|
76
|
+
return schema.fields.find((f) => f.isPrimary) || schema.fields[0];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/components/ui/tabs.tsx
|
|
80
|
+
import * as React from "react";
|
|
81
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
82
|
+
|
|
83
|
+
// src/lib/utils.ts
|
|
84
|
+
import { clsx } from "clsx";
|
|
85
|
+
import { twMerge } from "tailwind-merge";
|
|
86
|
+
function cn(...inputs) {
|
|
87
|
+
return twMerge(clsx(inputs));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/components/ui/tabs.tsx
|
|
91
|
+
import { jsx } from "react/jsx-runtime";
|
|
92
|
+
var Tabs = TabsPrimitive.Root;
|
|
93
|
+
var TabsList = React.forwardRef((_a, ref) => {
|
|
94
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
95
|
+
return /* @__PURE__ */ jsx(
|
|
96
|
+
TabsPrimitive.List,
|
|
97
|
+
__spreadValues({
|
|
98
|
+
ref,
|
|
99
|
+
className: cn(
|
|
100
|
+
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
|
101
|
+
className
|
|
102
|
+
)
|
|
103
|
+
}, props)
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
107
|
+
var TabsTrigger = React.forwardRef((_a, ref) => {
|
|
108
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
109
|
+
return /* @__PURE__ */ jsx(
|
|
110
|
+
TabsPrimitive.Trigger,
|
|
111
|
+
__spreadValues({
|
|
112
|
+
ref,
|
|
113
|
+
className: cn(
|
|
114
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
|
115
|
+
className
|
|
116
|
+
)
|
|
117
|
+
}, props)
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
121
|
+
var TabsContent = React.forwardRef((_a, ref) => {
|
|
122
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
123
|
+
return /* @__PURE__ */ jsx(
|
|
124
|
+
TabsPrimitive.Content,
|
|
125
|
+
__spreadValues({
|
|
126
|
+
ref,
|
|
127
|
+
className: cn(
|
|
128
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
129
|
+
className
|
|
130
|
+
)
|
|
131
|
+
}, props)
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
135
|
+
|
|
136
|
+
// src/components/DataViews.tsx
|
|
137
|
+
import { LayoutGrid, Columns, Calendar as CalendarIcon, Image, FileText } from "lucide-react";
|
|
138
|
+
|
|
139
|
+
// src/components/views/GridView.tsx
|
|
140
|
+
import { Plus, Trash2, Eye, Pencil } from "lucide-react";
|
|
141
|
+
|
|
142
|
+
// src/components/ui/button.tsx
|
|
143
|
+
import * as React2 from "react";
|
|
144
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
145
|
+
import { cva } from "class-variance-authority";
|
|
146
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
147
|
+
var buttonVariants = cva(
|
|
148
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
149
|
+
{
|
|
150
|
+
variants: {
|
|
151
|
+
variant: {
|
|
152
|
+
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
153
|
+
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
154
|
+
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
155
|
+
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
156
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
157
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
158
|
+
},
|
|
159
|
+
size: {
|
|
160
|
+
default: "h-9 px-4 py-2",
|
|
161
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
162
|
+
lg: "h-10 rounded-md px-8",
|
|
163
|
+
icon: "h-9 w-9"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
defaultVariants: {
|
|
167
|
+
variant: "default",
|
|
168
|
+
size: "default"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
var Button = React2.forwardRef(
|
|
173
|
+
(_a, ref) => {
|
|
174
|
+
var _b = _a, { className, variant, size, asChild = false } = _b, props = __objRest(_b, ["className", "variant", "size", "asChild"]);
|
|
175
|
+
const Comp = asChild ? Slot : "button";
|
|
176
|
+
return /* @__PURE__ */ jsx2(
|
|
177
|
+
Comp,
|
|
178
|
+
__spreadValues({
|
|
179
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
180
|
+
ref
|
|
181
|
+
}, props)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
Button.displayName = "Button";
|
|
186
|
+
|
|
187
|
+
// src/components/ui/table.tsx
|
|
188
|
+
import * as React3 from "react";
|
|
189
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
190
|
+
var Table = React3.forwardRef((_a, ref) => {
|
|
191
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
192
|
+
return /* @__PURE__ */ jsx3("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx3(
|
|
193
|
+
"table",
|
|
194
|
+
__spreadValues({
|
|
195
|
+
ref,
|
|
196
|
+
className: cn("w-full caption-bottom text-sm", className)
|
|
197
|
+
}, props)
|
|
198
|
+
) });
|
|
199
|
+
});
|
|
200
|
+
Table.displayName = "Table";
|
|
201
|
+
var TableHeader = React3.forwardRef((_a, ref) => {
|
|
202
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
203
|
+
return /* @__PURE__ */ jsx3("thead", __spreadValues({ ref, className: cn("[&_tr]:border-b", className) }, props));
|
|
204
|
+
});
|
|
205
|
+
TableHeader.displayName = "TableHeader";
|
|
206
|
+
var TableBody = React3.forwardRef((_a, ref) => {
|
|
207
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
208
|
+
return /* @__PURE__ */ jsx3(
|
|
209
|
+
"tbody",
|
|
210
|
+
__spreadValues({
|
|
211
|
+
ref,
|
|
212
|
+
className: cn("[&_tr:last-child]:border-0", className)
|
|
213
|
+
}, props)
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
TableBody.displayName = "TableBody";
|
|
217
|
+
var TableFooter = React3.forwardRef((_a, ref) => {
|
|
218
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
219
|
+
return /* @__PURE__ */ jsx3(
|
|
220
|
+
"tfoot",
|
|
221
|
+
__spreadValues({
|
|
222
|
+
ref,
|
|
223
|
+
className: cn(
|
|
224
|
+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
|
225
|
+
className
|
|
226
|
+
)
|
|
227
|
+
}, props)
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
TableFooter.displayName = "TableFooter";
|
|
231
|
+
var TableRow = React3.forwardRef((_a, ref) => {
|
|
232
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
233
|
+
return /* @__PURE__ */ jsx3(
|
|
234
|
+
"tr",
|
|
235
|
+
__spreadValues({
|
|
236
|
+
ref,
|
|
237
|
+
className: cn(
|
|
238
|
+
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
|
239
|
+
className
|
|
240
|
+
)
|
|
241
|
+
}, props)
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
TableRow.displayName = "TableRow";
|
|
245
|
+
var TableHead = React3.forwardRef((_a, ref) => {
|
|
246
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
247
|
+
return /* @__PURE__ */ jsx3(
|
|
248
|
+
"th",
|
|
249
|
+
__spreadValues({
|
|
250
|
+
ref,
|
|
251
|
+
className: cn(
|
|
252
|
+
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
253
|
+
className
|
|
254
|
+
)
|
|
255
|
+
}, props)
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
TableHead.displayName = "TableHead";
|
|
259
|
+
var TableCell = React3.forwardRef((_a, ref) => {
|
|
260
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
261
|
+
return /* @__PURE__ */ jsx3(
|
|
262
|
+
"td",
|
|
263
|
+
__spreadValues({
|
|
264
|
+
ref,
|
|
265
|
+
className: cn(
|
|
266
|
+
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
267
|
+
className
|
|
268
|
+
)
|
|
269
|
+
}, props)
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
TableCell.displayName = "TableCell";
|
|
273
|
+
var TableCaption = React3.forwardRef((_a, ref) => {
|
|
274
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
275
|
+
return /* @__PURE__ */ jsx3(
|
|
276
|
+
"caption",
|
|
277
|
+
__spreadValues({
|
|
278
|
+
ref,
|
|
279
|
+
className: cn("mt-4 text-sm text-muted-foreground", className)
|
|
280
|
+
}, props)
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
TableCaption.displayName = "TableCaption";
|
|
284
|
+
|
|
285
|
+
// src/components/ui/checkbox.tsx
|
|
286
|
+
import * as React4 from "react";
|
|
287
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
288
|
+
import { Check } from "lucide-react";
|
|
289
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
290
|
+
var Checkbox = React4.forwardRef((_a, ref) => {
|
|
291
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
292
|
+
return /* @__PURE__ */ jsx4(
|
|
293
|
+
CheckboxPrimitive.Root,
|
|
294
|
+
__spreadProps(__spreadValues({
|
|
295
|
+
ref,
|
|
296
|
+
className: cn(
|
|
297
|
+
"grid place-content-center peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
298
|
+
className
|
|
299
|
+
)
|
|
300
|
+
}, props), {
|
|
301
|
+
children: /* @__PURE__ */ jsx4(
|
|
302
|
+
CheckboxPrimitive.Indicator,
|
|
303
|
+
{
|
|
304
|
+
className: cn("grid place-content-center text-current"),
|
|
305
|
+
children: /* @__PURE__ */ jsx4(Check, { className: "h-4 w-4" })
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
});
|
|
311
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
312
|
+
|
|
313
|
+
// src/components/ui/badge.tsx
|
|
314
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
315
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
316
|
+
var badgeVariants = cva2(
|
|
317
|
+
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
318
|
+
{
|
|
319
|
+
variants: {
|
|
320
|
+
variant: {
|
|
321
|
+
default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
322
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
323
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
|
324
|
+
outline: "text-foreground"
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
defaultVariants: {
|
|
328
|
+
variant: "default"
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
function Badge(_a) {
|
|
333
|
+
var _b = _a, { className, variant } = _b, props = __objRest(_b, ["className", "variant"]);
|
|
334
|
+
return /* @__PURE__ */ jsx5("div", __spreadValues({ className: cn(badgeVariants({ variant }), className) }, props));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/components/views/GridView.tsx
|
|
338
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
339
|
+
var BADGE_COLOR_MAP = {
|
|
340
|
+
gray: "bg-muted text-muted-foreground",
|
|
341
|
+
blue: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300",
|
|
342
|
+
green: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300",
|
|
343
|
+
yellow: "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300",
|
|
344
|
+
red: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300",
|
|
345
|
+
purple: "bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300"
|
|
346
|
+
};
|
|
347
|
+
function GridView({ schema, records, onUpdateRecord, onDeleteRecord, onOpenModal }) {
|
|
348
|
+
const renderCellValue = (record, field) => {
|
|
349
|
+
var _a;
|
|
350
|
+
const value = record.fields[field.id];
|
|
351
|
+
switch (field.type) {
|
|
352
|
+
case "checkbox":
|
|
353
|
+
return /* @__PURE__ */ jsx6(
|
|
354
|
+
Checkbox,
|
|
355
|
+
{
|
|
356
|
+
checked: !!value,
|
|
357
|
+
onCheckedChange: (checked) => onUpdateRecord(record.id, { [field.id]: checked }),
|
|
358
|
+
onClick: (e) => e.stopPropagation()
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
case "select":
|
|
362
|
+
const option = (_a = field.options) == null ? void 0 : _a.find((o) => o.name === value);
|
|
363
|
+
return value ? /* @__PURE__ */ jsx6(Badge, { variant: "secondary", className: cn("font-normal", BADGE_COLOR_MAP[(option == null ? void 0 : option.color) || "gray"]), children: value }) : null;
|
|
364
|
+
case "multiSelect":
|
|
365
|
+
const values = Array.isArray(value) ? value : [];
|
|
366
|
+
return /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap gap-1", children: values.map((v) => {
|
|
367
|
+
var _a2;
|
|
368
|
+
const opt = (_a2 = field.options) == null ? void 0 : _a2.find((o) => o.name === v);
|
|
369
|
+
return /* @__PURE__ */ jsx6(Badge, { variant: "secondary", className: cn("font-normal", BADGE_COLOR_MAP[(opt == null ? void 0 : opt.color) || "gray"]), children: v }, v);
|
|
370
|
+
}) });
|
|
371
|
+
case "date":
|
|
372
|
+
return value ? new Date(value).toLocaleDateString() : null;
|
|
373
|
+
case "number":
|
|
374
|
+
return value != null ? Number(value).toLocaleString() : null;
|
|
375
|
+
default:
|
|
376
|
+
return value;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full grow overflow-hidden sm:pl-2 relative group flex flex-col h-full", children: [
|
|
380
|
+
/* @__PURE__ */ jsx6("div", { className: "relative w-full h-full", children: /* @__PURE__ */ jsx6("div", { className: "w-full h-full", style: { pointerEvents: "auto" }, children: /* @__PURE__ */ jsx6("div", { "data-t-grid-container": "true", tabIndex: 0, className: "relative outline-none w-full h-full", children: /* @__PURE__ */ jsx6("div", { className: "w-full h-full overflow-auto pb-10", children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
381
|
+
/* @__PURE__ */ jsx6(TableHeader, { className: "bg-muted/50 sticky top-0 z-10", children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
382
|
+
/* @__PURE__ */ jsx6(TableHead, { className: "w-12 text-muted-foreground", children: "#" }),
|
|
383
|
+
schema.fields.map((field) => /* @__PURE__ */ jsxs(TableHead, { className: "min-w-[150px]", children: [
|
|
384
|
+
/* @__PURE__ */ jsx6("span", { className: "font-medium", children: field.name }),
|
|
385
|
+
field.isPrimary && /* @__PURE__ */ jsx6(Badge, { variant: "outline", className: "ml-2 text-xs", children: "Primary" })
|
|
386
|
+
] }, field.id)),
|
|
387
|
+
/* @__PURE__ */ jsx6(TableHead, { className: "w-[100px] text-right", children: "Actions" })
|
|
388
|
+
] }) }),
|
|
389
|
+
/* @__PURE__ */ jsxs(TableBody, { children: [
|
|
390
|
+
records.map((record, index) => /* @__PURE__ */ jsxs(
|
|
391
|
+
TableRow,
|
|
392
|
+
{
|
|
393
|
+
className: "group cursor-pointer hover:bg-muted/50",
|
|
394
|
+
onClick: () => onOpenModal("view", record),
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ jsx6(TableCell, { className: "text-muted-foreground tabular-nums", children: index + 1 }),
|
|
397
|
+
schema.fields.map((field) => /* @__PURE__ */ jsx6(TableCell, { children: /* @__PURE__ */ jsx6("div", { className: "min-h-[24px] flex items-center", children: renderCellValue(record, field) }) }, field.id)),
|
|
398
|
+
/* @__PURE__ */ jsx6(TableCell, { className: "text-right", onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
|
|
399
|
+
/* @__PURE__ */ jsx6(
|
|
400
|
+
Button,
|
|
401
|
+
{
|
|
402
|
+
variant: "ghost",
|
|
403
|
+
size: "icon",
|
|
404
|
+
className: "h-8 w-8",
|
|
405
|
+
onClick: () => onOpenModal("view", record),
|
|
406
|
+
children: /* @__PURE__ */ jsx6(Eye, { className: "h-4 w-4" })
|
|
407
|
+
}
|
|
408
|
+
),
|
|
409
|
+
/* @__PURE__ */ jsx6(
|
|
410
|
+
Button,
|
|
411
|
+
{
|
|
412
|
+
variant: "ghost",
|
|
413
|
+
size: "icon",
|
|
414
|
+
className: "h-8 w-8",
|
|
415
|
+
onClick: () => onOpenModal("edit", record),
|
|
416
|
+
children: /* @__PURE__ */ jsx6(Pencil, { className: "h-4 w-4" })
|
|
417
|
+
}
|
|
418
|
+
),
|
|
419
|
+
/* @__PURE__ */ jsx6(
|
|
420
|
+
Button,
|
|
421
|
+
{
|
|
422
|
+
variant: "ghost",
|
|
423
|
+
size: "icon",
|
|
424
|
+
className: "h-8 w-8 text-muted-foreground hover:text-destructive",
|
|
425
|
+
onClick: () => onDeleteRecord(record.id),
|
|
426
|
+
children: /* @__PURE__ */ jsx6(Trash2, { className: "h-4 w-4" })
|
|
427
|
+
}
|
|
428
|
+
)
|
|
429
|
+
] }) })
|
|
430
|
+
]
|
|
431
|
+
},
|
|
432
|
+
record.id
|
|
433
|
+
)),
|
|
434
|
+
/* @__PURE__ */ jsx6(TableRow, { children: /* @__PURE__ */ jsx6(TableCell, { colSpan: schema.fields.length + 2, className: "p-0", children: /* @__PURE__ */ jsxs(
|
|
435
|
+
Button,
|
|
436
|
+
{
|
|
437
|
+
variant: "ghost",
|
|
438
|
+
size: "sm",
|
|
439
|
+
onClick: () => onOpenModal("add"),
|
|
440
|
+
className: "w-full justify-start px-4 h-10 text-muted-foreground hover:text-foreground rounded-none",
|
|
441
|
+
children: [
|
|
442
|
+
/* @__PURE__ */ jsx6(Plus, { className: "h-4 w-4 mr-2" }),
|
|
443
|
+
"Add new record"
|
|
444
|
+
]
|
|
445
|
+
}
|
|
446
|
+
) }) })
|
|
447
|
+
] })
|
|
448
|
+
] }) }) }) }) }),
|
|
449
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center h-6 pl-2 ml-1 text-xs bg-violet-200 dark:bg-zinc-600 rounded absolute bottom-3 left-0 shadow-sm z-20", children: [
|
|
450
|
+
records.length,
|
|
451
|
+
" records",
|
|
452
|
+
/* @__PURE__ */ jsx6("button", { className: "inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 gap-2 hover:text-accent-foreground rounded-md text-xs ml-[2px] h-full rounded-l-none p-[2px] hover:bg-violet-300 dark:hover:bg-zinc-500", children: /* @__PURE__ */ jsx6("svg", { xmlns: "http://www.w3.org/2000/svg", width: "1em", height: "1em", fill: "none", viewBox: "0 0 24 24", className: "w-3 h-3", children: /* @__PURE__ */ jsx6("path", { stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "m15 18-6-6 6-6" }) }) })
|
|
453
|
+
] })
|
|
454
|
+
] });
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/components/views/FormView.tsx
|
|
458
|
+
import { useState as useState2 } from "react";
|
|
459
|
+
|
|
460
|
+
// src/components/ui/card.tsx
|
|
461
|
+
import * as React5 from "react";
|
|
462
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
463
|
+
var Card = React5.forwardRef((_a, ref) => {
|
|
464
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
465
|
+
return /* @__PURE__ */ jsx7(
|
|
466
|
+
"div",
|
|
467
|
+
__spreadValues({
|
|
468
|
+
ref,
|
|
469
|
+
className: cn(
|
|
470
|
+
"rounded-xl border bg-card text-card-foreground shadow",
|
|
471
|
+
className
|
|
472
|
+
)
|
|
473
|
+
}, props)
|
|
474
|
+
);
|
|
475
|
+
});
|
|
476
|
+
Card.displayName = "Card";
|
|
477
|
+
var CardHeader = React5.forwardRef((_a, ref) => {
|
|
478
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
479
|
+
return /* @__PURE__ */ jsx7(
|
|
480
|
+
"div",
|
|
481
|
+
__spreadValues({
|
|
482
|
+
ref,
|
|
483
|
+
className: cn("flex flex-col space-y-1.5 p-6", className)
|
|
484
|
+
}, props)
|
|
485
|
+
);
|
|
486
|
+
});
|
|
487
|
+
CardHeader.displayName = "CardHeader";
|
|
488
|
+
var CardTitle = React5.forwardRef((_a, ref) => {
|
|
489
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
490
|
+
return /* @__PURE__ */ jsx7(
|
|
491
|
+
"div",
|
|
492
|
+
__spreadValues({
|
|
493
|
+
ref,
|
|
494
|
+
className: cn("font-semibold leading-none tracking-tight", className)
|
|
495
|
+
}, props)
|
|
496
|
+
);
|
|
497
|
+
});
|
|
498
|
+
CardTitle.displayName = "CardTitle";
|
|
499
|
+
var CardDescription = React5.forwardRef((_a, ref) => {
|
|
500
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
501
|
+
return /* @__PURE__ */ jsx7(
|
|
502
|
+
"div",
|
|
503
|
+
__spreadValues({
|
|
504
|
+
ref,
|
|
505
|
+
className: cn("text-sm text-muted-foreground", className)
|
|
506
|
+
}, props)
|
|
507
|
+
);
|
|
508
|
+
});
|
|
509
|
+
CardDescription.displayName = "CardDescription";
|
|
510
|
+
var CardContent = React5.forwardRef((_a, ref) => {
|
|
511
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
512
|
+
return /* @__PURE__ */ jsx7("div", __spreadValues({ ref, className: cn("p-6 pt-0", className) }, props));
|
|
513
|
+
});
|
|
514
|
+
CardContent.displayName = "CardContent";
|
|
515
|
+
var CardFooter = React5.forwardRef((_a, ref) => {
|
|
516
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
517
|
+
return /* @__PURE__ */ jsx7(
|
|
518
|
+
"div",
|
|
519
|
+
__spreadValues({
|
|
520
|
+
ref,
|
|
521
|
+
className: cn("flex items-center p-6 pt-0", className)
|
|
522
|
+
}, props)
|
|
523
|
+
);
|
|
524
|
+
});
|
|
525
|
+
CardFooter.displayName = "CardFooter";
|
|
526
|
+
|
|
527
|
+
// src/components/ui/alert.tsx
|
|
528
|
+
import * as React6 from "react";
|
|
529
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
530
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
531
|
+
var alertVariants = cva3(
|
|
532
|
+
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
|
533
|
+
{
|
|
534
|
+
variants: {
|
|
535
|
+
variant: {
|
|
536
|
+
default: "bg-background text-foreground",
|
|
537
|
+
destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive"
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
defaultVariants: {
|
|
541
|
+
variant: "default"
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
);
|
|
545
|
+
var Alert = React6.forwardRef((_a, ref) => {
|
|
546
|
+
var _b = _a, { className, variant } = _b, props = __objRest(_b, ["className", "variant"]);
|
|
547
|
+
return /* @__PURE__ */ jsx8(
|
|
548
|
+
"div",
|
|
549
|
+
__spreadValues({
|
|
550
|
+
ref,
|
|
551
|
+
role: "alert",
|
|
552
|
+
className: cn(alertVariants({ variant }), className)
|
|
553
|
+
}, props)
|
|
554
|
+
);
|
|
555
|
+
});
|
|
556
|
+
Alert.displayName = "Alert";
|
|
557
|
+
var AlertTitle = React6.forwardRef((_a, ref) => {
|
|
558
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
559
|
+
return /* @__PURE__ */ jsx8(
|
|
560
|
+
"h5",
|
|
561
|
+
__spreadValues({
|
|
562
|
+
ref,
|
|
563
|
+
className: cn("mb-1 font-medium leading-none tracking-tight", className)
|
|
564
|
+
}, props)
|
|
565
|
+
);
|
|
566
|
+
});
|
|
567
|
+
AlertTitle.displayName = "AlertTitle";
|
|
568
|
+
var AlertDescription = React6.forwardRef((_a, ref) => {
|
|
569
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
570
|
+
return /* @__PURE__ */ jsx8(
|
|
571
|
+
"div",
|
|
572
|
+
__spreadValues({
|
|
573
|
+
ref,
|
|
574
|
+
className: cn("text-sm [&_p]:leading-relaxed", className)
|
|
575
|
+
}, props)
|
|
576
|
+
);
|
|
577
|
+
});
|
|
578
|
+
AlertDescription.displayName = "AlertDescription";
|
|
579
|
+
|
|
580
|
+
// src/components/views/FormView.tsx
|
|
581
|
+
import { CheckCircle2 } from "lucide-react";
|
|
582
|
+
|
|
583
|
+
// src/components/RecordForm.tsx
|
|
584
|
+
import { useState, useEffect } from "react";
|
|
585
|
+
|
|
586
|
+
// src/components/ui/input.tsx
|
|
587
|
+
import * as React7 from "react";
|
|
588
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
589
|
+
var Input = React7.forwardRef(
|
|
590
|
+
(_a, ref) => {
|
|
591
|
+
var _b = _a, { className, type } = _b, props = __objRest(_b, ["className", "type"]);
|
|
592
|
+
return /* @__PURE__ */ jsx9(
|
|
593
|
+
"input",
|
|
594
|
+
__spreadValues({
|
|
595
|
+
type,
|
|
596
|
+
className: cn(
|
|
597
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
598
|
+
className
|
|
599
|
+
),
|
|
600
|
+
ref
|
|
601
|
+
}, props)
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
);
|
|
605
|
+
Input.displayName = "Input";
|
|
606
|
+
|
|
607
|
+
// src/components/ui/label.tsx
|
|
608
|
+
import * as React8 from "react";
|
|
609
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
610
|
+
import { cva as cva4 } from "class-variance-authority";
|
|
611
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
612
|
+
var labelVariants = cva4(
|
|
613
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
614
|
+
);
|
|
615
|
+
var Label = React8.forwardRef((_a, ref) => {
|
|
616
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
617
|
+
return /* @__PURE__ */ jsx10(
|
|
618
|
+
LabelPrimitive.Root,
|
|
619
|
+
__spreadValues({
|
|
620
|
+
ref,
|
|
621
|
+
className: cn(labelVariants(), className)
|
|
622
|
+
}, props)
|
|
623
|
+
);
|
|
624
|
+
});
|
|
625
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
626
|
+
|
|
627
|
+
// src/components/ui/select.tsx
|
|
628
|
+
import * as React9 from "react";
|
|
629
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
630
|
+
import { Check as Check2, ChevronDown, ChevronUp } from "lucide-react";
|
|
631
|
+
import { jsx as jsx11, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
632
|
+
var Select = SelectPrimitive.Root;
|
|
633
|
+
var SelectValue = SelectPrimitive.Value;
|
|
634
|
+
var SelectTrigger = React9.forwardRef((_a, ref) => {
|
|
635
|
+
var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
|
|
636
|
+
return /* @__PURE__ */ jsxs2(
|
|
637
|
+
SelectPrimitive.Trigger,
|
|
638
|
+
__spreadProps(__spreadValues({
|
|
639
|
+
ref,
|
|
640
|
+
className: cn(
|
|
641
|
+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
642
|
+
className
|
|
643
|
+
)
|
|
644
|
+
}, props), {
|
|
645
|
+
children: [
|
|
646
|
+
children,
|
|
647
|
+
/* @__PURE__ */ jsx11(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx11(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
648
|
+
]
|
|
649
|
+
})
|
|
650
|
+
);
|
|
651
|
+
});
|
|
652
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
653
|
+
var SelectScrollUpButton = React9.forwardRef((_a, ref) => {
|
|
654
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
655
|
+
return /* @__PURE__ */ jsx11(
|
|
656
|
+
SelectPrimitive.ScrollUpButton,
|
|
657
|
+
__spreadProps(__spreadValues({
|
|
658
|
+
ref,
|
|
659
|
+
className: cn(
|
|
660
|
+
"flex cursor-default items-center justify-center py-1",
|
|
661
|
+
className
|
|
662
|
+
)
|
|
663
|
+
}, props), {
|
|
664
|
+
children: /* @__PURE__ */ jsx11(ChevronUp, { className: "h-4 w-4" })
|
|
665
|
+
})
|
|
666
|
+
);
|
|
667
|
+
});
|
|
668
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
669
|
+
var SelectScrollDownButton = React9.forwardRef((_a, ref) => {
|
|
670
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
671
|
+
return /* @__PURE__ */ jsx11(
|
|
672
|
+
SelectPrimitive.ScrollDownButton,
|
|
673
|
+
__spreadProps(__spreadValues({
|
|
674
|
+
ref,
|
|
675
|
+
className: cn(
|
|
676
|
+
"flex cursor-default items-center justify-center py-1",
|
|
677
|
+
className
|
|
678
|
+
)
|
|
679
|
+
}, props), {
|
|
680
|
+
children: /* @__PURE__ */ jsx11(ChevronDown, { className: "h-4 w-4" })
|
|
681
|
+
})
|
|
682
|
+
);
|
|
683
|
+
});
|
|
684
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
685
|
+
var SelectContent = React9.forwardRef((_a, ref) => {
|
|
686
|
+
var _b = _a, { className, children, position = "popper" } = _b, props = __objRest(_b, ["className", "children", "position"]);
|
|
687
|
+
return /* @__PURE__ */ jsx11(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs2(
|
|
688
|
+
SelectPrimitive.Content,
|
|
689
|
+
__spreadProps(__spreadValues({
|
|
690
|
+
ref,
|
|
691
|
+
className: cn(
|
|
692
|
+
"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
|
|
693
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
694
|
+
className
|
|
695
|
+
),
|
|
696
|
+
position
|
|
697
|
+
}, props), {
|
|
698
|
+
children: [
|
|
699
|
+
/* @__PURE__ */ jsx11(SelectScrollUpButton, {}),
|
|
700
|
+
/* @__PURE__ */ jsx11(
|
|
701
|
+
SelectPrimitive.Viewport,
|
|
702
|
+
{
|
|
703
|
+
className: cn(
|
|
704
|
+
"p-1",
|
|
705
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
706
|
+
),
|
|
707
|
+
children
|
|
708
|
+
}
|
|
709
|
+
),
|
|
710
|
+
/* @__PURE__ */ jsx11(SelectScrollDownButton, {})
|
|
711
|
+
]
|
|
712
|
+
})
|
|
713
|
+
) });
|
|
714
|
+
});
|
|
715
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
716
|
+
var SelectLabel = React9.forwardRef((_a, ref) => {
|
|
717
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
718
|
+
return /* @__PURE__ */ jsx11(
|
|
719
|
+
SelectPrimitive.Label,
|
|
720
|
+
__spreadValues({
|
|
721
|
+
ref,
|
|
722
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", className)
|
|
723
|
+
}, props)
|
|
724
|
+
);
|
|
725
|
+
});
|
|
726
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
727
|
+
var SelectItem = React9.forwardRef((_a, ref) => {
|
|
728
|
+
var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
|
|
729
|
+
return /* @__PURE__ */ jsxs2(
|
|
730
|
+
SelectPrimitive.Item,
|
|
731
|
+
__spreadProps(__spreadValues({
|
|
732
|
+
ref,
|
|
733
|
+
className: cn(
|
|
734
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
735
|
+
className
|
|
736
|
+
)
|
|
737
|
+
}, props), {
|
|
738
|
+
children: [
|
|
739
|
+
/* @__PURE__ */ jsx11("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx11(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11(Check2, { className: "h-4 w-4" }) }) }),
|
|
740
|
+
/* @__PURE__ */ jsx11(SelectPrimitive.ItemText, { children })
|
|
741
|
+
]
|
|
742
|
+
})
|
|
743
|
+
);
|
|
744
|
+
});
|
|
745
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
746
|
+
var SelectSeparator = React9.forwardRef((_a, ref) => {
|
|
747
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
748
|
+
return /* @__PURE__ */ jsx11(
|
|
749
|
+
SelectPrimitive.Separator,
|
|
750
|
+
__spreadValues({
|
|
751
|
+
ref,
|
|
752
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className)
|
|
753
|
+
}, props)
|
|
754
|
+
);
|
|
755
|
+
});
|
|
756
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
757
|
+
|
|
758
|
+
// src/components/ui/textarea.tsx
|
|
759
|
+
import * as React10 from "react";
|
|
760
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
761
|
+
var Textarea = React10.forwardRef((_a, ref) => {
|
|
762
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
763
|
+
return /* @__PURE__ */ jsx12(
|
|
764
|
+
"textarea",
|
|
765
|
+
__spreadValues({
|
|
766
|
+
className: cn(
|
|
767
|
+
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
768
|
+
className
|
|
769
|
+
),
|
|
770
|
+
ref
|
|
771
|
+
}, props)
|
|
772
|
+
);
|
|
773
|
+
});
|
|
774
|
+
Textarea.displayName = "Textarea";
|
|
775
|
+
|
|
776
|
+
// src/components/RecordForm.tsx
|
|
777
|
+
import { jsx as jsx13, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
778
|
+
var COLOR_MAP = {
|
|
779
|
+
gray: "bg-muted",
|
|
780
|
+
blue: "bg-blue-500",
|
|
781
|
+
green: "bg-green-500",
|
|
782
|
+
yellow: "bg-amber-500",
|
|
783
|
+
red: "bg-red-500",
|
|
784
|
+
purple: "bg-purple-500"
|
|
785
|
+
};
|
|
786
|
+
var DEFAULT_INITIAL_VALUES = {};
|
|
787
|
+
function RecordForm({ schema, initialValues = DEFAULT_INITIAL_VALUES, onSubmit, mode }) {
|
|
788
|
+
const [formData, setFormData] = useState(initialValues);
|
|
789
|
+
useEffect(() => {
|
|
790
|
+
setFormData(initialValues);
|
|
791
|
+
}, [initialValues]);
|
|
792
|
+
const handleChange = (fieldId, value) => {
|
|
793
|
+
if (mode === "view") return;
|
|
794
|
+
setFormData((prev) => __spreadProps(__spreadValues({}, prev), { [fieldId]: value }));
|
|
795
|
+
};
|
|
796
|
+
const handleSubmit = (e) => {
|
|
797
|
+
e.preventDefault();
|
|
798
|
+
if (onSubmit && mode !== "view") onSubmit(formData);
|
|
799
|
+
};
|
|
800
|
+
const isReadOnly = mode === "view";
|
|
801
|
+
const renderField = (field) => {
|
|
802
|
+
var _a, _b, _c;
|
|
803
|
+
const value = (_a = formData[field.id]) != null ? _a : "";
|
|
804
|
+
if (field.id === "description" || field.type === "text" && field.name.toLowerCase().includes("description")) {
|
|
805
|
+
return /* @__PURE__ */ jsx13(
|
|
806
|
+
Textarea,
|
|
807
|
+
{
|
|
808
|
+
value,
|
|
809
|
+
disabled: isReadOnly,
|
|
810
|
+
onChange: (e) => handleChange(field.id, e.target.value),
|
|
811
|
+
placeholder: isReadOnly ? "" : `Enter ${field.name}...`,
|
|
812
|
+
className: "min-h-[100px] resize-none"
|
|
813
|
+
}
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
switch (field.type) {
|
|
817
|
+
case "text":
|
|
818
|
+
return /* @__PURE__ */ jsx13(
|
|
819
|
+
Input,
|
|
820
|
+
{
|
|
821
|
+
value,
|
|
822
|
+
disabled: isReadOnly,
|
|
823
|
+
onChange: (e) => handleChange(field.id, e.target.value),
|
|
824
|
+
placeholder: isReadOnly ? "" : `Enter ${field.name}...`
|
|
825
|
+
}
|
|
826
|
+
);
|
|
827
|
+
case "number":
|
|
828
|
+
return /* @__PURE__ */ jsx13(
|
|
829
|
+
Input,
|
|
830
|
+
{
|
|
831
|
+
type: "number",
|
|
832
|
+
value,
|
|
833
|
+
disabled: isReadOnly,
|
|
834
|
+
onChange: (e) => handleChange(field.id, e.target.value),
|
|
835
|
+
placeholder: isReadOnly ? "" : `Enter ${field.name}...`
|
|
836
|
+
}
|
|
837
|
+
);
|
|
838
|
+
case "date":
|
|
839
|
+
return /* @__PURE__ */ jsx13(
|
|
840
|
+
Input,
|
|
841
|
+
{
|
|
842
|
+
type: "date",
|
|
843
|
+
value,
|
|
844
|
+
disabled: isReadOnly,
|
|
845
|
+
onChange: (e) => handleChange(field.id, e.target.value)
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
case "checkbox":
|
|
849
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
850
|
+
/* @__PURE__ */ jsx13(
|
|
851
|
+
Checkbox,
|
|
852
|
+
{
|
|
853
|
+
id: field.id,
|
|
854
|
+
checked: !!value,
|
|
855
|
+
disabled: isReadOnly,
|
|
856
|
+
onCheckedChange: (checked) => handleChange(field.id, checked)
|
|
857
|
+
}
|
|
858
|
+
),
|
|
859
|
+
/* @__PURE__ */ jsx13(Label, { htmlFor: field.id, className: "cursor-pointer text-sm", children: value ? "Yes" : "No" })
|
|
860
|
+
] });
|
|
861
|
+
case "select":
|
|
862
|
+
if (isReadOnly) {
|
|
863
|
+
return value ? /* @__PURE__ */ jsx13(Badge, { variant: "secondary", children: value }) : /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground text-sm", children: "None" });
|
|
864
|
+
}
|
|
865
|
+
return /* @__PURE__ */ jsxs3(Select, { value, onValueChange: (v) => handleChange(field.id, v), children: [
|
|
866
|
+
/* @__PURE__ */ jsx13(SelectTrigger, { children: /* @__PURE__ */ jsx13(SelectValue, { placeholder: "Select..." }) }),
|
|
867
|
+
/* @__PURE__ */ jsx13(SelectContent, { children: (_b = field.options) == null ? void 0 : _b.map((opt) => /* @__PURE__ */ jsx13(SelectItem, { value: opt.name, children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
868
|
+
/* @__PURE__ */ jsx13("div", { className: cn("w-2 h-2 rounded-full", COLOR_MAP[opt.color || "gray"]) }),
|
|
869
|
+
opt.name
|
|
870
|
+
] }) }, opt.id)) })
|
|
871
|
+
] });
|
|
872
|
+
case "multiSelect":
|
|
873
|
+
const selectedValues = Array.isArray(value) ? value : [];
|
|
874
|
+
if (isReadOnly) {
|
|
875
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap gap-1", children: [
|
|
876
|
+
selectedValues.map((v) => /* @__PURE__ */ jsx13(Badge, { variant: "secondary", children: v }, v)),
|
|
877
|
+
selectedValues.length === 0 && /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground text-sm", children: "None" })
|
|
878
|
+
] });
|
|
879
|
+
}
|
|
880
|
+
return /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-2", children: (_c = field.options) == null ? void 0 : _c.map((opt) => /* @__PURE__ */ jsx13(
|
|
881
|
+
Badge,
|
|
882
|
+
{
|
|
883
|
+
variant: selectedValues.includes(opt.name) ? "default" : "outline",
|
|
884
|
+
className: "cursor-pointer",
|
|
885
|
+
onClick: () => {
|
|
886
|
+
const newValues = selectedValues.includes(opt.name) ? selectedValues.filter((v) => v !== opt.name) : [...selectedValues, opt.name];
|
|
887
|
+
handleChange(field.id, newValues);
|
|
888
|
+
},
|
|
889
|
+
children: opt.name
|
|
890
|
+
},
|
|
891
|
+
opt.id
|
|
892
|
+
)) });
|
|
893
|
+
default:
|
|
894
|
+
return /* @__PURE__ */ jsx13(
|
|
895
|
+
Input,
|
|
896
|
+
{
|
|
897
|
+
value,
|
|
898
|
+
disabled: isReadOnly,
|
|
899
|
+
onChange: (e) => handleChange(field.id, e.target.value),
|
|
900
|
+
placeholder: isReadOnly ? "" : `Enter ${field.name}...`
|
|
901
|
+
}
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
return /* @__PURE__ */ jsxs3("form", { id: "record-form", onSubmit: handleSubmit, className: "space-y-5", children: [
|
|
906
|
+
schema.fields.map((field) => /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
|
|
907
|
+
/* @__PURE__ */ jsxs3(Label, { className: "text-sm font-medium", children: [
|
|
908
|
+
field.name,
|
|
909
|
+
field.isPrimary && /* @__PURE__ */ jsx13(Badge, { variant: "outline", className: "ml-2 text-xs", children: "Primary" })
|
|
910
|
+
] }),
|
|
911
|
+
renderField(field)
|
|
912
|
+
] }, field.id)),
|
|
913
|
+
mode !== "view" && /* @__PURE__ */ jsx13("div", { className: "flex justify-end pt-4 border-t", children: /* @__PURE__ */ jsx13(Button, { type: "submit", children: mode === "add" ? "Create Record" : "Save Changes" }) })
|
|
914
|
+
] });
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/components/views/FormView.tsx
|
|
918
|
+
import { jsx as jsx14, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
919
|
+
function FormView({ schema, onCreateRecord }) {
|
|
920
|
+
const [success, setSuccess] = useState2(false);
|
|
921
|
+
const handleSubmit = (formData) => {
|
|
922
|
+
onCreateRecord(formData);
|
|
923
|
+
setSuccess(true);
|
|
924
|
+
setTimeout(() => setSuccess(false), 3e3);
|
|
925
|
+
};
|
|
926
|
+
return /* @__PURE__ */ jsx14("div", { className: "h-full overflow-auto p-8 bg-muted/30", children: /* @__PURE__ */ jsx14("div", { className: "max-w-xl mx-auto", children: /* @__PURE__ */ jsxs4(Card, { children: [
|
|
927
|
+
/* @__PURE__ */ jsxs4(CardHeader, { children: [
|
|
928
|
+
/* @__PURE__ */ jsx14(CardTitle, { children: "Add New Record" }),
|
|
929
|
+
/* @__PURE__ */ jsx14(CardDescription, { children: "Fill out the form below to add a new entry to the database." })
|
|
930
|
+
] }),
|
|
931
|
+
/* @__PURE__ */ jsxs4(CardContent, { children: [
|
|
932
|
+
success && /* @__PURE__ */ jsxs4(Alert, { className: "mb-6 border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950 dark:text-green-200", children: [
|
|
933
|
+
/* @__PURE__ */ jsx14(CheckCircle2, { className: "h-4 w-4" }),
|
|
934
|
+
/* @__PURE__ */ jsx14(AlertDescription, { children: "Record created successfully!" })
|
|
935
|
+
] }),
|
|
936
|
+
/* @__PURE__ */ jsx14(
|
|
937
|
+
RecordForm,
|
|
938
|
+
{
|
|
939
|
+
schema,
|
|
940
|
+
mode: "add",
|
|
941
|
+
onSubmit: handleSubmit
|
|
942
|
+
}
|
|
943
|
+
)
|
|
944
|
+
] })
|
|
945
|
+
] }) }) });
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/components/views/KanbanView.tsx
|
|
949
|
+
import { useState as useState3, useMemo } from "react";
|
|
950
|
+
import {
|
|
951
|
+
Plus as Plus2,
|
|
952
|
+
Calendar,
|
|
953
|
+
AlignLeft,
|
|
954
|
+
CheckSquare,
|
|
955
|
+
Hash,
|
|
956
|
+
Tag,
|
|
957
|
+
Tags,
|
|
958
|
+
User as UserIcon
|
|
959
|
+
} from "lucide-react";
|
|
960
|
+
import { jsx as jsx15, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
961
|
+
var BADGE_COLOR_MAP2 = {
|
|
962
|
+
gray: "bg-muted text-muted-foreground hover:bg-muted/80",
|
|
963
|
+
blue: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300 hover:bg-blue-100/80",
|
|
964
|
+
green: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300 hover:bg-green-100/80",
|
|
965
|
+
yellow: "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300 hover:bg-amber-100/80",
|
|
966
|
+
red: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300 hover:bg-red-100/80",
|
|
967
|
+
purple: "bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300 hover:bg-purple-100/80"
|
|
968
|
+
};
|
|
969
|
+
var COLUMN_DOT_COLOR_MAP = {
|
|
970
|
+
gray: "bg-gray-400",
|
|
971
|
+
blue: "bg-blue-500",
|
|
972
|
+
green: "bg-green-500",
|
|
973
|
+
yellow: "bg-amber-500",
|
|
974
|
+
red: "bg-red-500",
|
|
975
|
+
purple: "bg-purple-500"
|
|
976
|
+
};
|
|
977
|
+
var FIELD_ICON_MAP = {
|
|
978
|
+
text: AlignLeft,
|
|
979
|
+
number: Hash,
|
|
980
|
+
select: Tag,
|
|
981
|
+
multiSelect: Tags,
|
|
982
|
+
date: Calendar,
|
|
983
|
+
checkbox: CheckSquare,
|
|
984
|
+
user: UserIcon
|
|
985
|
+
};
|
|
986
|
+
function KanbanView({ schema, records, onUpdateRecord, onDeleteRecord, onOpenModal }) {
|
|
987
|
+
const [draggedRecord, setDraggedRecord] = useState3(null);
|
|
988
|
+
const selectField = getSelectField(schema);
|
|
989
|
+
const primaryField = getPrimaryField(schema);
|
|
990
|
+
const columns = useMemo(() => {
|
|
991
|
+
if (!(selectField == null ? void 0 : selectField.options)) return [];
|
|
992
|
+
return [
|
|
993
|
+
{ id: "__empty__", name: "Uncategorized", color: "gray" },
|
|
994
|
+
...selectField.options
|
|
995
|
+
];
|
|
996
|
+
}, [selectField]);
|
|
997
|
+
const recordsByColumn = useMemo(() => {
|
|
998
|
+
const grouped = {};
|
|
999
|
+
columns.forEach((col) => {
|
|
1000
|
+
grouped[col.id] = [];
|
|
1001
|
+
});
|
|
1002
|
+
records.forEach((record) => {
|
|
1003
|
+
var _a, _b;
|
|
1004
|
+
if (!selectField) return;
|
|
1005
|
+
const value = record.fields[selectField.id];
|
|
1006
|
+
const option = (_a = selectField.options) == null ? void 0 : _a.find((o) => o.name === value);
|
|
1007
|
+
const colId = (option == null ? void 0 : option.id) || "__empty__";
|
|
1008
|
+
(_b = grouped[colId]) == null ? void 0 : _b.push(record);
|
|
1009
|
+
});
|
|
1010
|
+
return grouped;
|
|
1011
|
+
}, [records, columns, selectField]);
|
|
1012
|
+
const handleDragOver = (e) => {
|
|
1013
|
+
e.preventDefault();
|
|
1014
|
+
};
|
|
1015
|
+
const handleDrop = (columnId) => {
|
|
1016
|
+
if (!draggedRecord || !selectField) return;
|
|
1017
|
+
const column = columns.find((c) => c.id === columnId);
|
|
1018
|
+
const value = columnId === "__empty__" ? "" : (column == null ? void 0 : column.name) || "";
|
|
1019
|
+
onUpdateRecord(draggedRecord, { [selectField.id]: value });
|
|
1020
|
+
setDraggedRecord(null);
|
|
1021
|
+
};
|
|
1022
|
+
if (!selectField) {
|
|
1023
|
+
return /* @__PURE__ */ jsx15("div", { className: "h-full flex items-center justify-center text-muted-foreground", children: "No select field found in schema. Add a select field to enable Kanban view." });
|
|
1024
|
+
}
|
|
1025
|
+
return /* @__PURE__ */ jsx15("div", { className: "w-full grow overflow-hidden bg-background", children: /* @__PURE__ */ jsx15("div", { className: "relative w-full h-full overflow-x-auto overflow-y-hidden p-2", children: /* @__PURE__ */ jsx15("div", { className: "flex h-full", children: columns.map((column) => {
|
|
1026
|
+
var _a, _b;
|
|
1027
|
+
return /* @__PURE__ */ jsx15("div", { className: "h-full pr-4", children: /* @__PURE__ */ jsxs5("div", { className: "w-[264px] h-full border bg-muted rounded-md shrink-0 flex flex-col overflow-hidden", children: [
|
|
1028
|
+
/* @__PURE__ */ jsx15("div", { className: "w-full", children: /* @__PURE__ */ jsx15("div", { className: "flex h-12 w-full shrink-0 items-center justify-between border-b bg-card px-4", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center space-x-2 overflow-hidden text-muted-foreground", children: [
|
|
1029
|
+
/* @__PURE__ */ jsx15("div", { className: cn("w-2 h-2 rounded-full shrink-0", COLUMN_DOT_COLOR_MAP[column.color || "gray"]) }),
|
|
1030
|
+
/* @__PURE__ */ jsx15("span", { className: "text-sm font-semibold truncate", children: column.name }),
|
|
1031
|
+
/* @__PURE__ */ jsx15("span", { className: "rounded-xl border px-2 text-xs", children: ((_a = recordsByColumn[column.id]) == null ? void 0 : _a.length) || 0 })
|
|
1032
|
+
] }) }) }),
|
|
1033
|
+
/* @__PURE__ */ jsx15(
|
|
1034
|
+
"div",
|
|
1035
|
+
{
|
|
1036
|
+
className: "flex-1 w-full overflow-y-auto min-h-0",
|
|
1037
|
+
onDragOver: handleDragOver,
|
|
1038
|
+
onDrop: () => handleDrop(column.id),
|
|
1039
|
+
children: /* @__PURE__ */ jsx15("div", { className: "flex flex-col pt-3 pb-3", children: (_b = recordsByColumn[column.id]) == null ? void 0 : _b.map((record) => /* @__PURE__ */ jsx15("div", { className: "w-full px-3 mb-2", children: /* @__PURE__ */ jsxs5(
|
|
1040
|
+
"div",
|
|
1041
|
+
{
|
|
1042
|
+
draggable: true,
|
|
1043
|
+
onDragStart: () => setDraggedRecord(record.id),
|
|
1044
|
+
onClick: () => onOpenModal("view", record),
|
|
1045
|
+
className: cn(
|
|
1046
|
+
"relative flex w-full grow flex-col space-y-2 gap-1 overflow-hidden rounded-md border border-border bg-card hover:border-primary/15 p-3 cursor-pointer transition-all",
|
|
1047
|
+
draggedRecord === record.id && "opacity-50 ring-2 ring-primary"
|
|
1048
|
+
),
|
|
1049
|
+
children: [
|
|
1050
|
+
/* @__PURE__ */ jsx15("div", { className: "text-base font-semibold", children: primaryField ? record.fields[primaryField.id] || "Untitled" : "Untitled" }),
|
|
1051
|
+
schema.fields.filter((f) => !f.isPrimary && f.id !== selectField.id).map((field) => {
|
|
1052
|
+
const value = record.fields[field.id];
|
|
1053
|
+
if (value === void 0 || value === null || value === "" || value === false) return null;
|
|
1054
|
+
const Icon2 = FIELD_ICON_MAP[field.type] || AlignLeft;
|
|
1055
|
+
return /* @__PURE__ */ jsxs5("div", { children: [
|
|
1056
|
+
/* @__PURE__ */ jsxs5("div", { className: "mb-1 flex items-center space-x-1 text-muted-foreground", children: [
|
|
1057
|
+
/* @__PURE__ */ jsx15(Icon2, { className: "w-4 h-4 text-sm" }),
|
|
1058
|
+
/* @__PURE__ */ jsx15("span", { className: "text-xs", children: field.name })
|
|
1059
|
+
] }),
|
|
1060
|
+
/* @__PURE__ */ jsx15("div", { className: "w-full text-[13px] leading-5 breaking-all line-clamp-6", children: field.type === "select" ? /* @__PURE__ */ jsx15("div", { className: "flex gap-1 flex-wrap", children: (() => {
|
|
1061
|
+
var _a2;
|
|
1062
|
+
const option = (_a2 = field.options) == null ? void 0 : _a2.find((o) => o.name === value);
|
|
1063
|
+
return /* @__PURE__ */ jsx15(
|
|
1064
|
+
"div",
|
|
1065
|
+
{
|
|
1066
|
+
className: cn(
|
|
1067
|
+
"text-xs px-2 h-5 rounded-md flex items-center gap-1 min-w-0 truncate",
|
|
1068
|
+
BADGE_COLOR_MAP2[(option == null ? void 0 : option.color) || "gray"]
|
|
1069
|
+
),
|
|
1070
|
+
title: String(value),
|
|
1071
|
+
children: /* @__PURE__ */ jsx15("span", { className: "min-w-0 truncate", children: String(value) })
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
})() }) : field.type === "checkbox" ? /* @__PURE__ */ jsx15("div", { className: "flex gap-1 flex-wrap", children: /* @__PURE__ */ jsx15("div", { className: cn(
|
|
1075
|
+
"flex items-center justify-center w-5 h-5 rounded-sm border border-primary shadow bg-primary text-primary-foreground"
|
|
1076
|
+
), children: /* @__PURE__ */ jsx15(CheckSquare, { className: "h-4 w-4" }) }) }) : /* @__PURE__ */ jsx15("span", { title: String(value), className: "whitespace-pre-wrap break-all", children: String(value) }) })
|
|
1077
|
+
] }, field.id);
|
|
1078
|
+
})
|
|
1079
|
+
]
|
|
1080
|
+
}
|
|
1081
|
+
) }, record.id)) })
|
|
1082
|
+
}
|
|
1083
|
+
),
|
|
1084
|
+
/* @__PURE__ */ jsx15("div", { className: "flex items-center justify-center rounded-b-md bg-slate-50 px-3 py-2 dark:bg-muted", children: /* @__PURE__ */ jsx15(
|
|
1085
|
+
Button,
|
|
1086
|
+
{
|
|
1087
|
+
variant: "outline",
|
|
1088
|
+
className: "w-full h-9 gap-2 bg-background dark:bg-white/5 dark:hover:bg-white/10 shadow-none hover:bg-zinc-100 dark:hover:bg-zinc-800",
|
|
1089
|
+
onClick: () => onOpenModal("add"),
|
|
1090
|
+
children: /* @__PURE__ */ jsx15(Plus2, { className: "w-5 h-5" })
|
|
1091
|
+
}
|
|
1092
|
+
) })
|
|
1093
|
+
] }) }, column.id);
|
|
1094
|
+
}) }) }) });
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// src/components/views/GalleryView.tsx
|
|
1098
|
+
import { Plus as Plus3, Trash2 as Trash23, MoreHorizontal as MoreHorizontal2, Eye as Eye3, Pencil as Pencil3 } from "lucide-react";
|
|
1099
|
+
|
|
1100
|
+
// src/components/ui/dropdown-menu.tsx
|
|
1101
|
+
import * as React11 from "react";
|
|
1102
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
1103
|
+
import { Check as Check3, ChevronRight, Circle } from "lucide-react";
|
|
1104
|
+
import { jsx as jsx16, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1105
|
+
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
1106
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
1107
|
+
var DropdownMenuSubTrigger = React11.forwardRef((_a, ref) => {
|
|
1108
|
+
var _b = _a, { className, inset, children } = _b, props = __objRest(_b, ["className", "inset", "children"]);
|
|
1109
|
+
return /* @__PURE__ */ jsxs6(
|
|
1110
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
1111
|
+
__spreadProps(__spreadValues({
|
|
1112
|
+
ref,
|
|
1113
|
+
className: cn(
|
|
1114
|
+
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
1115
|
+
inset && "pl-8",
|
|
1116
|
+
className
|
|
1117
|
+
)
|
|
1118
|
+
}, props), {
|
|
1119
|
+
children: [
|
|
1120
|
+
children,
|
|
1121
|
+
/* @__PURE__ */ jsx16(ChevronRight, { className: "ml-auto" })
|
|
1122
|
+
]
|
|
1123
|
+
})
|
|
1124
|
+
);
|
|
1125
|
+
});
|
|
1126
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
1127
|
+
var DropdownMenuSubContent = React11.forwardRef((_a, ref) => {
|
|
1128
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1129
|
+
return /* @__PURE__ */ jsx16(
|
|
1130
|
+
DropdownMenuPrimitive.SubContent,
|
|
1131
|
+
__spreadValues({
|
|
1132
|
+
ref,
|
|
1133
|
+
className: cn(
|
|
1134
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
1135
|
+
className
|
|
1136
|
+
)
|
|
1137
|
+
}, props)
|
|
1138
|
+
);
|
|
1139
|
+
});
|
|
1140
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
1141
|
+
var DropdownMenuContent = React11.forwardRef((_a, ref) => {
|
|
1142
|
+
var _b = _a, { className, sideOffset = 4 } = _b, props = __objRest(_b, ["className", "sideOffset"]);
|
|
1143
|
+
return /* @__PURE__ */ jsx16(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx16(
|
|
1144
|
+
DropdownMenuPrimitive.Content,
|
|
1145
|
+
__spreadValues({
|
|
1146
|
+
ref,
|
|
1147
|
+
sideOffset,
|
|
1148
|
+
className: cn(
|
|
1149
|
+
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
1150
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
1151
|
+
className
|
|
1152
|
+
)
|
|
1153
|
+
}, props)
|
|
1154
|
+
) });
|
|
1155
|
+
});
|
|
1156
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
1157
|
+
var DropdownMenuItem = React11.forwardRef((_a, ref) => {
|
|
1158
|
+
var _b = _a, { className, inset } = _b, props = __objRest(_b, ["className", "inset"]);
|
|
1159
|
+
return /* @__PURE__ */ jsx16(
|
|
1160
|
+
DropdownMenuPrimitive.Item,
|
|
1161
|
+
__spreadValues({
|
|
1162
|
+
ref,
|
|
1163
|
+
className: cn(
|
|
1164
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
1165
|
+
inset && "pl-8",
|
|
1166
|
+
className
|
|
1167
|
+
)
|
|
1168
|
+
}, props)
|
|
1169
|
+
);
|
|
1170
|
+
});
|
|
1171
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
1172
|
+
var DropdownMenuCheckboxItem = React11.forwardRef((_a, ref) => {
|
|
1173
|
+
var _b = _a, { className, children, checked } = _b, props = __objRest(_b, ["className", "children", "checked"]);
|
|
1174
|
+
return /* @__PURE__ */ jsxs6(
|
|
1175
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
1176
|
+
__spreadProps(__spreadValues({
|
|
1177
|
+
ref,
|
|
1178
|
+
className: cn(
|
|
1179
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
1180
|
+
className
|
|
1181
|
+
),
|
|
1182
|
+
checked
|
|
1183
|
+
}, props), {
|
|
1184
|
+
children: [
|
|
1185
|
+
/* @__PURE__ */ jsx16("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx16(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx16(Check3, { className: "h-4 w-4" }) }) }),
|
|
1186
|
+
children
|
|
1187
|
+
]
|
|
1188
|
+
})
|
|
1189
|
+
);
|
|
1190
|
+
});
|
|
1191
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
1192
|
+
var DropdownMenuRadioItem = React11.forwardRef((_a, ref) => {
|
|
1193
|
+
var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
|
|
1194
|
+
return /* @__PURE__ */ jsxs6(
|
|
1195
|
+
DropdownMenuPrimitive.RadioItem,
|
|
1196
|
+
__spreadProps(__spreadValues({
|
|
1197
|
+
ref,
|
|
1198
|
+
className: cn(
|
|
1199
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
1200
|
+
className
|
|
1201
|
+
)
|
|
1202
|
+
}, props), {
|
|
1203
|
+
children: [
|
|
1204
|
+
/* @__PURE__ */ jsx16("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx16(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx16(Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
1205
|
+
children
|
|
1206
|
+
]
|
|
1207
|
+
})
|
|
1208
|
+
);
|
|
1209
|
+
});
|
|
1210
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
1211
|
+
var DropdownMenuLabel = React11.forwardRef((_a, ref) => {
|
|
1212
|
+
var _b = _a, { className, inset } = _b, props = __objRest(_b, ["className", "inset"]);
|
|
1213
|
+
return /* @__PURE__ */ jsx16(
|
|
1214
|
+
DropdownMenuPrimitive.Label,
|
|
1215
|
+
__spreadValues({
|
|
1216
|
+
ref,
|
|
1217
|
+
className: cn(
|
|
1218
|
+
"px-2 py-1.5 text-sm font-semibold",
|
|
1219
|
+
inset && "pl-8",
|
|
1220
|
+
className
|
|
1221
|
+
)
|
|
1222
|
+
}, props)
|
|
1223
|
+
);
|
|
1224
|
+
});
|
|
1225
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
1226
|
+
var DropdownMenuSeparator = React11.forwardRef((_a, ref) => {
|
|
1227
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1228
|
+
return /* @__PURE__ */ jsx16(
|
|
1229
|
+
DropdownMenuPrimitive.Separator,
|
|
1230
|
+
__spreadValues({
|
|
1231
|
+
ref,
|
|
1232
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className)
|
|
1233
|
+
}, props)
|
|
1234
|
+
);
|
|
1235
|
+
});
|
|
1236
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
1237
|
+
var DropdownMenuShortcut = (_a) => {
|
|
1238
|
+
var _b = _a, {
|
|
1239
|
+
className
|
|
1240
|
+
} = _b, props = __objRest(_b, [
|
|
1241
|
+
"className"
|
|
1242
|
+
]);
|
|
1243
|
+
return /* @__PURE__ */ jsx16(
|
|
1244
|
+
"span",
|
|
1245
|
+
__spreadValues({
|
|
1246
|
+
className: cn("ml-auto text-xs tracking-widest opacity-60", className)
|
|
1247
|
+
}, props)
|
|
1248
|
+
);
|
|
1249
|
+
};
|
|
1250
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
1251
|
+
|
|
1252
|
+
// src/components/views/GalleryView.tsx
|
|
1253
|
+
import { jsx as jsx17, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1254
|
+
function GalleryView({ schema, records, onDeleteRecord, onOpenModal }) {
|
|
1255
|
+
const primaryField = getPrimaryField(schema);
|
|
1256
|
+
const displayFields = schema.fields.filter((f) => f.id !== (primaryField == null ? void 0 : primaryField.id)).slice(0, 3);
|
|
1257
|
+
return /* @__PURE__ */ jsx17("div", { className: "h-full overflow-auto p-6 bg-muted/30", children: /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4", children: [
|
|
1258
|
+
records.map((record) => {
|
|
1259
|
+
const title = primaryField ? record.fields[primaryField.id] : "Untitled";
|
|
1260
|
+
return /* @__PURE__ */ jsxs7(
|
|
1261
|
+
Card,
|
|
1262
|
+
{
|
|
1263
|
+
className: "group hover:shadow-md transition-all cursor-pointer overflow-hidden",
|
|
1264
|
+
onClick: () => onOpenModal("view", record),
|
|
1265
|
+
children: [
|
|
1266
|
+
/* @__PURE__ */ jsxs7("div", { className: "aspect-[16/10] bg-muted flex items-center justify-center relative", children: [
|
|
1267
|
+
/* @__PURE__ */ jsx17("span", { className: "text-4xl font-semibold text-muted-foreground/20 select-none", children: (title || "U").charAt(0).toUpperCase() }),
|
|
1268
|
+
/* @__PURE__ */ jsx17("div", { className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxs7(DropdownMenu, { children: [
|
|
1269
|
+
/* @__PURE__ */ jsx17(DropdownMenuTrigger, { asChild: true, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx17(Button, { variant: "secondary", size: "icon", className: "h-8 w-8", children: /* @__PURE__ */ jsx17(MoreHorizontal2, { className: "h-4 w-4" }) }) }),
|
|
1270
|
+
/* @__PURE__ */ jsxs7(DropdownMenuContent, { align: "end", children: [
|
|
1271
|
+
/* @__PURE__ */ jsxs7(DropdownMenuItem, { onClick: () => onOpenModal("view", record), children: [
|
|
1272
|
+
/* @__PURE__ */ jsx17(Eye3, { className: "h-4 w-4 mr-2" }),
|
|
1273
|
+
"View"
|
|
1274
|
+
] }),
|
|
1275
|
+
/* @__PURE__ */ jsxs7(DropdownMenuItem, { onClick: () => onOpenModal("edit", record), children: [
|
|
1276
|
+
/* @__PURE__ */ jsx17(Pencil3, { className: "h-4 w-4 mr-2" }),
|
|
1277
|
+
"Edit"
|
|
1278
|
+
] }),
|
|
1279
|
+
/* @__PURE__ */ jsxs7(
|
|
1280
|
+
DropdownMenuItem,
|
|
1281
|
+
{
|
|
1282
|
+
className: "text-destructive focus:text-destructive",
|
|
1283
|
+
onClick: () => onDeleteRecord(record.id),
|
|
1284
|
+
children: [
|
|
1285
|
+
/* @__PURE__ */ jsx17(Trash23, { className: "h-4 w-4 mr-2" }),
|
|
1286
|
+
"Delete"
|
|
1287
|
+
]
|
|
1288
|
+
}
|
|
1289
|
+
)
|
|
1290
|
+
] })
|
|
1291
|
+
] }) })
|
|
1292
|
+
] }),
|
|
1293
|
+
/* @__PURE__ */ jsxs7(CardContent, { className: "p-4", children: [
|
|
1294
|
+
/* @__PURE__ */ jsx17("h3", { className: "font-medium text-sm truncate mb-2", children: title || "Untitled" }),
|
|
1295
|
+
/* @__PURE__ */ jsx17("div", { className: "space-y-1.5", children: displayFields.map((field) => {
|
|
1296
|
+
const value = record.fields[field.id];
|
|
1297
|
+
if (value === void 0 || value === null || value === "" || value === false) return null;
|
|
1298
|
+
let displayValue = value;
|
|
1299
|
+
if (Array.isArray(value)) displayValue = value.join(", ");
|
|
1300
|
+
else if (typeof value === "boolean") displayValue = value ? "Yes" : "No";
|
|
1301
|
+
else if (field.type === "date") displayValue = new Date(value).toLocaleDateString();
|
|
1302
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex items-baseline gap-2 text-xs", children: [
|
|
1303
|
+
/* @__PURE__ */ jsxs7("span", { className: "text-muted-foreground flex-shrink-0", children: [
|
|
1304
|
+
field.name,
|
|
1305
|
+
":"
|
|
1306
|
+
] }),
|
|
1307
|
+
/* @__PURE__ */ jsx17("span", { className: "text-foreground truncate", children: displayValue })
|
|
1308
|
+
] }, field.id);
|
|
1309
|
+
}) })
|
|
1310
|
+
] })
|
|
1311
|
+
]
|
|
1312
|
+
},
|
|
1313
|
+
record.id
|
|
1314
|
+
);
|
|
1315
|
+
}),
|
|
1316
|
+
/* @__PURE__ */ jsxs7(
|
|
1317
|
+
"div",
|
|
1318
|
+
{
|
|
1319
|
+
className: "flex flex-col items-center justify-center cursor-pointer border-2 border-dashed border-muted-foreground/20 rounded-lg transition-all hover:border-primary hover:bg-muted/50 min-h-[200px]",
|
|
1320
|
+
onClick: () => onOpenModal("add"),
|
|
1321
|
+
children: [
|
|
1322
|
+
/* @__PURE__ */ jsx17("div", { className: "h-10 w-10 rounded-full bg-muted flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx17(Plus3, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
1323
|
+
/* @__PURE__ */ jsx17("span", { className: "text-sm font-medium text-muted-foreground", children: "Add Record" })
|
|
1324
|
+
]
|
|
1325
|
+
}
|
|
1326
|
+
)
|
|
1327
|
+
] }) });
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// src/components/views/CalendarView.tsx
|
|
1331
|
+
import { useMemo as useMemo2, useState as useState4 } from "react";
|
|
1332
|
+
import { ChevronLeft, ChevronRight as ChevronRight2, Plus as Plus4, Eye as Eye4, Pencil as Pencil4, Trash2 as Trash24, MoreHorizontal as MoreHorizontal3 } from "lucide-react";
|
|
1333
|
+
import { jsx as jsx18, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1334
|
+
function CalendarView({ schema, records, onUpdateRecord, onDeleteRecord, onOpenModal }) {
|
|
1335
|
+
const [currentDate, setCurrentDate] = useState4(/* @__PURE__ */ new Date());
|
|
1336
|
+
const dateField = getDateField(schema);
|
|
1337
|
+
const primaryField = getPrimaryField(schema);
|
|
1338
|
+
const monthStart = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
|
|
1339
|
+
const monthEnd = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
|
|
1340
|
+
const calendarDays = useMemo2(() => {
|
|
1341
|
+
const days = [];
|
|
1342
|
+
const firstDayOfWeek = monthStart.getDay();
|
|
1343
|
+
for (let i = firstDayOfWeek - 1; i >= 0; i--) {
|
|
1344
|
+
const date = new Date(monthStart);
|
|
1345
|
+
date.setDate(date.getDate() - i - 1);
|
|
1346
|
+
days.push(date);
|
|
1347
|
+
}
|
|
1348
|
+
for (let i = 1; i <= monthEnd.getDate(); i++) {
|
|
1349
|
+
days.push(new Date(currentDate.getFullYear(), currentDate.getMonth(), i));
|
|
1350
|
+
}
|
|
1351
|
+
const remaining = 42 - days.length;
|
|
1352
|
+
for (let i = 1; i <= remaining; i++) {
|
|
1353
|
+
const date = new Date(monthEnd);
|
|
1354
|
+
date.setDate(date.getDate() + i);
|
|
1355
|
+
days.push(date);
|
|
1356
|
+
}
|
|
1357
|
+
return days;
|
|
1358
|
+
}, [currentDate, monthStart, monthEnd]);
|
|
1359
|
+
const recordsByDate = useMemo2(() => {
|
|
1360
|
+
const map = {};
|
|
1361
|
+
if (!dateField) return map;
|
|
1362
|
+
records.forEach((record) => {
|
|
1363
|
+
const dateValue = record.fields[dateField.id];
|
|
1364
|
+
if (dateValue) {
|
|
1365
|
+
const date = new Date(dateValue);
|
|
1366
|
+
const key = date.toISOString().split("T")[0];
|
|
1367
|
+
if (!map[key]) map[key] = [];
|
|
1368
|
+
map[key].push(record);
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
return map;
|
|
1372
|
+
}, [records, dateField]);
|
|
1373
|
+
const isToday = (date) => date.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
|
|
1374
|
+
const isCurrentMonth = (date) => date.getMonth() === currentDate.getMonth();
|
|
1375
|
+
const handleDayClick = (date) => {
|
|
1376
|
+
if (!dateField) return;
|
|
1377
|
+
onOpenModal("add", void 0, { [dateField.id]: date.toISOString().split("T")[0] });
|
|
1378
|
+
};
|
|
1379
|
+
const handleDrop = (recordId, newDate) => {
|
|
1380
|
+
if (!dateField) return;
|
|
1381
|
+
onUpdateRecord(recordId, { [dateField.id]: newDate.toISOString().split("T")[0] });
|
|
1382
|
+
};
|
|
1383
|
+
const weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
1384
|
+
const monthNames = [
|
|
1385
|
+
"January",
|
|
1386
|
+
"February",
|
|
1387
|
+
"March",
|
|
1388
|
+
"April",
|
|
1389
|
+
"May",
|
|
1390
|
+
"June",
|
|
1391
|
+
"July",
|
|
1392
|
+
"August",
|
|
1393
|
+
"September",
|
|
1394
|
+
"October",
|
|
1395
|
+
"November",
|
|
1396
|
+
"December"
|
|
1397
|
+
];
|
|
1398
|
+
if (!dateField) {
|
|
1399
|
+
return /* @__PURE__ */ jsx18("div", { className: "h-full flex items-center justify-center text-muted-foreground", children: "No date field found in schema. Add a date field to enable Calendar view." });
|
|
1400
|
+
}
|
|
1401
|
+
return /* @__PURE__ */ jsxs8("div", { className: "h-full flex flex-col p-6 bg-muted/30", children: [
|
|
1402
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between mb-4", children: [
|
|
1403
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-4", children: [
|
|
1404
|
+
/* @__PURE__ */ jsxs8("h2", { className: "text-xl font-semibold", children: [
|
|
1405
|
+
monthNames[currentDate.getMonth()],
|
|
1406
|
+
" ",
|
|
1407
|
+
currentDate.getFullYear()
|
|
1408
|
+
] }),
|
|
1409
|
+
/* @__PURE__ */ jsx18(
|
|
1410
|
+
Button,
|
|
1411
|
+
{
|
|
1412
|
+
variant: "outline",
|
|
1413
|
+
size: "sm",
|
|
1414
|
+
onClick: () => setCurrentDate(/* @__PURE__ */ new Date()),
|
|
1415
|
+
children: "Today"
|
|
1416
|
+
}
|
|
1417
|
+
)
|
|
1418
|
+
] }),
|
|
1419
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1", children: [
|
|
1420
|
+
/* @__PURE__ */ jsx18(
|
|
1421
|
+
Button,
|
|
1422
|
+
{
|
|
1423
|
+
variant: "outline",
|
|
1424
|
+
size: "icon",
|
|
1425
|
+
className: "h-8 w-8",
|
|
1426
|
+
onClick: () => setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)),
|
|
1427
|
+
children: /* @__PURE__ */ jsx18(ChevronLeft, { className: "h-4 w-4" })
|
|
1428
|
+
}
|
|
1429
|
+
),
|
|
1430
|
+
/* @__PURE__ */ jsx18(
|
|
1431
|
+
Button,
|
|
1432
|
+
{
|
|
1433
|
+
variant: "outline",
|
|
1434
|
+
size: "icon",
|
|
1435
|
+
className: "h-8 w-8",
|
|
1436
|
+
onClick: () => setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)),
|
|
1437
|
+
children: /* @__PURE__ */ jsx18(ChevronRight2, { className: "h-4 w-4" })
|
|
1438
|
+
}
|
|
1439
|
+
)
|
|
1440
|
+
] })
|
|
1441
|
+
] }),
|
|
1442
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex-1 border rounded-lg overflow-hidden bg-background flex flex-col", children: [
|
|
1443
|
+
/* @__PURE__ */ jsx18("div", { className: "grid grid-cols-7 bg-muted/50 border-b", children: weekDays.map((day) => /* @__PURE__ */ jsx18("div", { className: "px-2 py-2 text-center text-xs font-medium text-muted-foreground", children: day }, day)) }),
|
|
1444
|
+
/* @__PURE__ */ jsx18("div", { className: "grid grid-cols-7 flex-1", children: calendarDays.map((date, index) => {
|
|
1445
|
+
const dateKey = date.toISOString().split("T")[0];
|
|
1446
|
+
const dayRecords = recordsByDate[dateKey] || [];
|
|
1447
|
+
return /* @__PURE__ */ jsxs8(
|
|
1448
|
+
"div",
|
|
1449
|
+
{
|
|
1450
|
+
className: cn(
|
|
1451
|
+
"min-h-[100px] p-1.5 border-b border-r transition-all group relative flex flex-col",
|
|
1452
|
+
!isCurrentMonth(date) && "bg-muted/30 text-muted-foreground/50",
|
|
1453
|
+
isCurrentMonth(date) && "hover:bg-muted/10",
|
|
1454
|
+
index % 7 === 6 && "border-r-0"
|
|
1455
|
+
),
|
|
1456
|
+
onDragOver: (e) => e.preventDefault(),
|
|
1457
|
+
onDrop: (e) => {
|
|
1458
|
+
const recordId = e.dataTransfer.getData("recordId");
|
|
1459
|
+
if (recordId) handleDrop(recordId, date);
|
|
1460
|
+
},
|
|
1461
|
+
children: [
|
|
1462
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between mb-1", children: [
|
|
1463
|
+
/* @__PURE__ */ jsx18("span", { className: cn(
|
|
1464
|
+
"text-xs w-6 h-6 flex items-center justify-center rounded-full",
|
|
1465
|
+
isToday(date) ? "bg-primary text-primary-foreground font-medium" : ""
|
|
1466
|
+
), children: date.getDate() }),
|
|
1467
|
+
/* @__PURE__ */ jsx18(
|
|
1468
|
+
Button,
|
|
1469
|
+
{
|
|
1470
|
+
variant: "ghost",
|
|
1471
|
+
size: "icon",
|
|
1472
|
+
className: "h-5 w-5 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1473
|
+
onClick: () => handleDayClick(date),
|
|
1474
|
+
children: /* @__PURE__ */ jsx18(Plus4, { className: "h-3 w-3" })
|
|
1475
|
+
}
|
|
1476
|
+
)
|
|
1477
|
+
] }),
|
|
1478
|
+
/* @__PURE__ */ jsx18("div", { className: "flex-1 space-y-0.5 overflow-y-auto", children: dayRecords.map((record) => /* @__PURE__ */ jsxs8(
|
|
1479
|
+
"div",
|
|
1480
|
+
{
|
|
1481
|
+
draggable: true,
|
|
1482
|
+
onDragStart: (e) => {
|
|
1483
|
+
e.stopPropagation();
|
|
1484
|
+
e.dataTransfer.setData("recordId", record.id);
|
|
1485
|
+
},
|
|
1486
|
+
onClick: (e) => {
|
|
1487
|
+
e.stopPropagation();
|
|
1488
|
+
onOpenModal("view", record);
|
|
1489
|
+
},
|
|
1490
|
+
className: "group/item relative",
|
|
1491
|
+
children: [
|
|
1492
|
+
/* @__PURE__ */ jsx18(
|
|
1493
|
+
Badge,
|
|
1494
|
+
{
|
|
1495
|
+
variant: "secondary",
|
|
1496
|
+
className: "w-full justify-start truncate text-xs py-0.5 cursor-grab active:cursor-grabbing hover:bg-primary/10",
|
|
1497
|
+
children: primaryField ? record.fields[primaryField.id] || "Untitled" : "Event"
|
|
1498
|
+
}
|
|
1499
|
+
),
|
|
1500
|
+
/* @__PURE__ */ jsx18("div", { className: "absolute right-0 top-0 h-full flex items-center opacity-0 group-hover/item:opacity-100 bg-background/80 pr-1 rounded-r", children: /* @__PURE__ */ jsxs8(DropdownMenu, { children: [
|
|
1501
|
+
/* @__PURE__ */ jsx18(DropdownMenuTrigger, { asChild: true, onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx18(MoreHorizontal3, { className: "h-3 w-3 text-muted-foreground cursor-pointer" }) }),
|
|
1502
|
+
/* @__PURE__ */ jsxs8(DropdownMenuContent, { align: "end", children: [
|
|
1503
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, { onClick: () => onOpenModal("view", record), children: [
|
|
1504
|
+
/* @__PURE__ */ jsx18(Eye4, { className: "h-4 w-4 mr-2" }),
|
|
1505
|
+
"View"
|
|
1506
|
+
] }),
|
|
1507
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, { onClick: () => onOpenModal("edit", record), children: [
|
|
1508
|
+
/* @__PURE__ */ jsx18(Pencil4, { className: "h-4 w-4 mr-2" }),
|
|
1509
|
+
"Edit"
|
|
1510
|
+
] }),
|
|
1511
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, { className: "text-destructive focus:text-destructive", onClick: () => onDeleteRecord(record.id), children: [
|
|
1512
|
+
/* @__PURE__ */ jsx18(Trash24, { className: "h-4 w-4 mr-2" }),
|
|
1513
|
+
"Delete"
|
|
1514
|
+
] })
|
|
1515
|
+
] })
|
|
1516
|
+
] }) })
|
|
1517
|
+
]
|
|
1518
|
+
},
|
|
1519
|
+
record.id
|
|
1520
|
+
)) })
|
|
1521
|
+
]
|
|
1522
|
+
},
|
|
1523
|
+
index
|
|
1524
|
+
);
|
|
1525
|
+
}) })
|
|
1526
|
+
] })
|
|
1527
|
+
] });
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// src/components/ui/dialog.tsx
|
|
1531
|
+
import * as React12 from "react";
|
|
1532
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
1533
|
+
import { X } from "lucide-react";
|
|
1534
|
+
import { jsx as jsx19, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1535
|
+
var Dialog = DialogPrimitive.Root;
|
|
1536
|
+
var DialogPortal = DialogPrimitive.Portal;
|
|
1537
|
+
var DialogOverlay = React12.forwardRef((_a, ref) => {
|
|
1538
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1539
|
+
return /* @__PURE__ */ jsx19(
|
|
1540
|
+
DialogPrimitive.Overlay,
|
|
1541
|
+
__spreadValues({
|
|
1542
|
+
ref,
|
|
1543
|
+
className: cn(
|
|
1544
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
1545
|
+
className
|
|
1546
|
+
)
|
|
1547
|
+
}, props)
|
|
1548
|
+
);
|
|
1549
|
+
});
|
|
1550
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
1551
|
+
var DialogContent = React12.forwardRef((_a, ref) => {
|
|
1552
|
+
var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
|
|
1553
|
+
return /* @__PURE__ */ jsxs9(DialogPortal, { children: [
|
|
1554
|
+
/* @__PURE__ */ jsx19(DialogOverlay, {}),
|
|
1555
|
+
/* @__PURE__ */ jsxs9(
|
|
1556
|
+
DialogPrimitive.Content,
|
|
1557
|
+
__spreadProps(__spreadValues({
|
|
1558
|
+
ref,
|
|
1559
|
+
className: cn(
|
|
1560
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
1561
|
+
className
|
|
1562
|
+
)
|
|
1563
|
+
}, props), {
|
|
1564
|
+
children: [
|
|
1565
|
+
children,
|
|
1566
|
+
/* @__PURE__ */ jsxs9(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
|
|
1567
|
+
/* @__PURE__ */ jsx19(X, { className: "h-4 w-4" }),
|
|
1568
|
+
/* @__PURE__ */ jsx19("span", { className: "sr-only", children: "Close" })
|
|
1569
|
+
] })
|
|
1570
|
+
]
|
|
1571
|
+
})
|
|
1572
|
+
)
|
|
1573
|
+
] });
|
|
1574
|
+
});
|
|
1575
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
1576
|
+
var DialogHeader = (_a) => {
|
|
1577
|
+
var _b = _a, {
|
|
1578
|
+
className
|
|
1579
|
+
} = _b, props = __objRest(_b, [
|
|
1580
|
+
"className"
|
|
1581
|
+
]);
|
|
1582
|
+
return /* @__PURE__ */ jsx19(
|
|
1583
|
+
"div",
|
|
1584
|
+
__spreadValues({
|
|
1585
|
+
className: cn(
|
|
1586
|
+
"flex flex-col space-y-1.5 text-center sm:text-left",
|
|
1587
|
+
className
|
|
1588
|
+
)
|
|
1589
|
+
}, props)
|
|
1590
|
+
);
|
|
1591
|
+
};
|
|
1592
|
+
DialogHeader.displayName = "DialogHeader";
|
|
1593
|
+
var DialogFooter = (_a) => {
|
|
1594
|
+
var _b = _a, {
|
|
1595
|
+
className
|
|
1596
|
+
} = _b, props = __objRest(_b, [
|
|
1597
|
+
"className"
|
|
1598
|
+
]);
|
|
1599
|
+
return /* @__PURE__ */ jsx19(
|
|
1600
|
+
"div",
|
|
1601
|
+
__spreadValues({
|
|
1602
|
+
className: cn(
|
|
1603
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
1604
|
+
className
|
|
1605
|
+
)
|
|
1606
|
+
}, props)
|
|
1607
|
+
);
|
|
1608
|
+
};
|
|
1609
|
+
DialogFooter.displayName = "DialogFooter";
|
|
1610
|
+
var DialogTitle = React12.forwardRef((_a, ref) => {
|
|
1611
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1612
|
+
return /* @__PURE__ */ jsx19(
|
|
1613
|
+
DialogPrimitive.Title,
|
|
1614
|
+
__spreadValues({
|
|
1615
|
+
ref,
|
|
1616
|
+
className: cn(
|
|
1617
|
+
"text-lg font-semibold leading-none tracking-tight",
|
|
1618
|
+
className
|
|
1619
|
+
)
|
|
1620
|
+
}, props)
|
|
1621
|
+
);
|
|
1622
|
+
});
|
|
1623
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
1624
|
+
var DialogDescription = React12.forwardRef((_a, ref) => {
|
|
1625
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1626
|
+
return /* @__PURE__ */ jsx19(
|
|
1627
|
+
DialogPrimitive.Description,
|
|
1628
|
+
__spreadValues({
|
|
1629
|
+
ref,
|
|
1630
|
+
className: cn("text-sm text-muted-foreground", className)
|
|
1631
|
+
}, props)
|
|
1632
|
+
);
|
|
1633
|
+
});
|
|
1634
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
1635
|
+
|
|
1636
|
+
// src/components/ui/scroll-area.tsx
|
|
1637
|
+
import * as React13 from "react";
|
|
1638
|
+
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
|
|
1639
|
+
import { jsx as jsx20, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1640
|
+
var ScrollArea = React13.forwardRef((_a, ref) => {
|
|
1641
|
+
var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
|
|
1642
|
+
return /* @__PURE__ */ jsxs10(
|
|
1643
|
+
ScrollAreaPrimitive.Root,
|
|
1644
|
+
__spreadProps(__spreadValues({
|
|
1645
|
+
ref,
|
|
1646
|
+
className: cn("relative overflow-hidden", className)
|
|
1647
|
+
}, props), {
|
|
1648
|
+
children: [
|
|
1649
|
+
/* @__PURE__ */ jsx20(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
|
|
1650
|
+
/* @__PURE__ */ jsx20(ScrollBar, {}),
|
|
1651
|
+
/* @__PURE__ */ jsx20(ScrollAreaPrimitive.Corner, {})
|
|
1652
|
+
]
|
|
1653
|
+
})
|
|
1654
|
+
);
|
|
1655
|
+
});
|
|
1656
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
1657
|
+
var ScrollBar = React13.forwardRef((_a, ref) => {
|
|
1658
|
+
var _b = _a, { className, orientation = "vertical" } = _b, props = __objRest(_b, ["className", "orientation"]);
|
|
1659
|
+
return /* @__PURE__ */ jsx20(
|
|
1660
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
1661
|
+
__spreadProps(__spreadValues({
|
|
1662
|
+
ref,
|
|
1663
|
+
orientation,
|
|
1664
|
+
className: cn(
|
|
1665
|
+
"flex touch-none select-none transition-colors",
|
|
1666
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
1667
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
1668
|
+
className
|
|
1669
|
+
)
|
|
1670
|
+
}, props), {
|
|
1671
|
+
children: /* @__PURE__ */ jsx20(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
|
|
1672
|
+
})
|
|
1673
|
+
);
|
|
1674
|
+
});
|
|
1675
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
1676
|
+
|
|
1677
|
+
// src/components/RecordModal.tsx
|
|
1678
|
+
import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1679
|
+
function RecordModal({
|
|
1680
|
+
isOpen,
|
|
1681
|
+
onClose,
|
|
1682
|
+
mode,
|
|
1683
|
+
schema,
|
|
1684
|
+
record,
|
|
1685
|
+
initialValues,
|
|
1686
|
+
onSubmit
|
|
1687
|
+
}) {
|
|
1688
|
+
const titles = {
|
|
1689
|
+
add: "Create New Record",
|
|
1690
|
+
edit: "Edit Record",
|
|
1691
|
+
view: "Record Details"
|
|
1692
|
+
};
|
|
1693
|
+
const descriptions = {
|
|
1694
|
+
add: "Enter the details for the new record.",
|
|
1695
|
+
edit: "Modify the values for this record.",
|
|
1696
|
+
view: "View the data for this record."
|
|
1697
|
+
};
|
|
1698
|
+
const handleSubmit = (values) => {
|
|
1699
|
+
onSubmit(values);
|
|
1700
|
+
onClose();
|
|
1701
|
+
};
|
|
1702
|
+
const formValues = mode === "add" ? initialValues : record == null ? void 0 : record.fields;
|
|
1703
|
+
return /* @__PURE__ */ jsx21(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs11(DialogContent, { className: "sm:max-w-[500px] max-h-[85vh] flex flex-col", children: [
|
|
1704
|
+
/* @__PURE__ */ jsxs11(DialogHeader, { children: [
|
|
1705
|
+
/* @__PURE__ */ jsx21(DialogTitle, { children: titles[mode] }),
|
|
1706
|
+
/* @__PURE__ */ jsx21(DialogDescription, { children: descriptions[mode] })
|
|
1707
|
+
] }),
|
|
1708
|
+
/* @__PURE__ */ jsx21(ScrollArea, { className: "flex-1 -mx-6 px-6", children: /* @__PURE__ */ jsx21(
|
|
1709
|
+
RecordForm,
|
|
1710
|
+
{
|
|
1711
|
+
schema,
|
|
1712
|
+
mode,
|
|
1713
|
+
initialValues: formValues || {},
|
|
1714
|
+
onSubmit: handleSubmit
|
|
1715
|
+
}
|
|
1716
|
+
) })
|
|
1717
|
+
] }) });
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// src/components/ThemeToggle.tsx
|
|
1721
|
+
import { useTheme } from "next-themes";
|
|
1722
|
+
import { Moon, Sun } from "lucide-react";
|
|
1723
|
+
import { jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1724
|
+
function ThemeToggle() {
|
|
1725
|
+
const { setTheme, theme } = useTheme();
|
|
1726
|
+
return /* @__PURE__ */ jsxs12(DropdownMenu, { children: [
|
|
1727
|
+
/* @__PURE__ */ jsx22(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(Button, { variant: "ghost", size: "icon", className: "h-9 w-9", children: [
|
|
1728
|
+
/* @__PURE__ */ jsx22(Sun, { className: "h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" }),
|
|
1729
|
+
/* @__PURE__ */ jsx22(Moon, { className: "absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" }),
|
|
1730
|
+
/* @__PURE__ */ jsx22("span", { className: "sr-only", children: "Toggle theme" })
|
|
1731
|
+
] }) }),
|
|
1732
|
+
/* @__PURE__ */ jsxs12(DropdownMenuContent, { align: "end", children: [
|
|
1733
|
+
/* @__PURE__ */ jsx22(DropdownMenuItem, { onClick: () => setTheme("light"), children: "Light" }),
|
|
1734
|
+
/* @__PURE__ */ jsx22(DropdownMenuItem, { onClick: () => setTheme("dark"), children: "Dark" }),
|
|
1735
|
+
/* @__PURE__ */ jsx22(DropdownMenuItem, { onClick: () => setTheme("system"), children: "System" })
|
|
1736
|
+
] })
|
|
1737
|
+
] });
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// src/components/DataViews.tsx
|
|
1741
|
+
import { jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1742
|
+
function DataViews({ schema, dbClient, config }) {
|
|
1743
|
+
const [records, setRecords] = useState5([]);
|
|
1744
|
+
const [activeView, setActiveView] = useState5((config == null ? void 0 : config.defaultView) || "grid");
|
|
1745
|
+
const [modalState, setModalState] = useState5({
|
|
1746
|
+
isOpen: false,
|
|
1747
|
+
mode: "add"
|
|
1748
|
+
});
|
|
1749
|
+
const [mounted, setMounted] = useState5(false);
|
|
1750
|
+
const hasSelectField = !!getSelectField(schema);
|
|
1751
|
+
const hasDateField = !!getDateField(schema);
|
|
1752
|
+
const fetchRecords = async () => {
|
|
1753
|
+
try {
|
|
1754
|
+
const data = await dbClient.getRecords();
|
|
1755
|
+
setRecords(data);
|
|
1756
|
+
} catch (error) {
|
|
1757
|
+
console.error("Failed to fetch records:", error);
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
useEffect2(() => {
|
|
1761
|
+
setMounted(true);
|
|
1762
|
+
fetchRecords();
|
|
1763
|
+
}, [dbClient]);
|
|
1764
|
+
const handleCreateRecord = async (fieldValues) => {
|
|
1765
|
+
try {
|
|
1766
|
+
const newRecord = await dbClient.createRecord({ fields: fieldValues });
|
|
1767
|
+
setRecords((prev) => [newRecord, ...prev]);
|
|
1768
|
+
return newRecord;
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
console.error("Failed to create record:", error);
|
|
1771
|
+
throw error;
|
|
1772
|
+
}
|
|
1773
|
+
};
|
|
1774
|
+
const handleUpdateRecord = async (recordId, fieldValues) => {
|
|
1775
|
+
try {
|
|
1776
|
+
const updatedRecord = await dbClient.updateRecord(recordId, { fields: fieldValues });
|
|
1777
|
+
setRecords((prev) => prev.map((r) => r.id === recordId ? updatedRecord : r));
|
|
1778
|
+
} catch (error) {
|
|
1779
|
+
console.error("Failed to update record:", error);
|
|
1780
|
+
throw error;
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
const handleDeleteRecord = async (recordId) => {
|
|
1784
|
+
try {
|
|
1785
|
+
await dbClient.deleteRecord(recordId);
|
|
1786
|
+
setRecords((prev) => prev.filter((r) => r.id !== recordId));
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
console.error("Failed to delete record:", error);
|
|
1789
|
+
throw error;
|
|
1790
|
+
}
|
|
1791
|
+
};
|
|
1792
|
+
const openModal = (mode, record, initialValues) => {
|
|
1793
|
+
setModalState({ isOpen: true, mode, record, initialValues });
|
|
1794
|
+
};
|
|
1795
|
+
const commonProps = {
|
|
1796
|
+
schema,
|
|
1797
|
+
records,
|
|
1798
|
+
onCreateRecord: handleCreateRecord,
|
|
1799
|
+
onUpdateRecord: handleUpdateRecord,
|
|
1800
|
+
onDeleteRecord: handleDeleteRecord,
|
|
1801
|
+
onOpenModal: openModal
|
|
1802
|
+
};
|
|
1803
|
+
return /* @__PURE__ */ jsxs13("div", { className: "h-full w-full flex flex-col bg-background", children: [
|
|
1804
|
+
/* @__PURE__ */ jsxs13("header", { className: "border-b px-6 py-3 flex items-center justify-between shrink-0", children: [
|
|
1805
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3", children: [
|
|
1806
|
+
/* @__PURE__ */ jsx23("span", { className: "text-2xl", children: schema.icon }),
|
|
1807
|
+
/* @__PURE__ */ jsx23("h1", { className: "text-lg font-semibold", children: schema.name })
|
|
1808
|
+
] }),
|
|
1809
|
+
/* @__PURE__ */ jsx23(ThemeToggle, {})
|
|
1810
|
+
] }),
|
|
1811
|
+
/* @__PURE__ */ jsxs13(Tabs, { value: activeView, onValueChange: (v) => setActiveView(v), className: "flex-1 flex flex-col overflow-hidden", children: [
|
|
1812
|
+
/* @__PURE__ */ jsx23("div", { className: "border-b px-4 shrink-0", children: /* @__PURE__ */ jsxs13(TabsList, { className: "h-10", children: [
|
|
1813
|
+
/* @__PURE__ */ jsxs13(TabsTrigger, { value: "grid", className: "gap-2", children: [
|
|
1814
|
+
mounted && /* @__PURE__ */ jsx23(LayoutGrid, { className: "h-4 w-4" }),
|
|
1815
|
+
"Grid"
|
|
1816
|
+
] }),
|
|
1817
|
+
/* @__PURE__ */ jsxs13(TabsTrigger, { value: "form", className: "gap-2", children: [
|
|
1818
|
+
mounted && /* @__PURE__ */ jsx23(FileText, { className: "h-4 w-4" }),
|
|
1819
|
+
"Form"
|
|
1820
|
+
] }),
|
|
1821
|
+
hasSelectField && /* @__PURE__ */ jsxs13(TabsTrigger, { value: "kanban", className: "gap-2", children: [
|
|
1822
|
+
mounted && /* @__PURE__ */ jsx23(Columns, { className: "h-4 w-4" }),
|
|
1823
|
+
"Kanban"
|
|
1824
|
+
] }),
|
|
1825
|
+
/* @__PURE__ */ jsxs13(TabsTrigger, { value: "gallery", className: "gap-2", children: [
|
|
1826
|
+
mounted && /* @__PURE__ */ jsx23(Image, { className: "h-4 w-4" }),
|
|
1827
|
+
"Gallery"
|
|
1828
|
+
] }),
|
|
1829
|
+
hasDateField && /* @__PURE__ */ jsxs13(TabsTrigger, { value: "calendar", className: "gap-2", children: [
|
|
1830
|
+
mounted && /* @__PURE__ */ jsx23(CalendarIcon, { className: "h-4 w-4" }),
|
|
1831
|
+
"Calendar"
|
|
1832
|
+
] })
|
|
1833
|
+
] }) }),
|
|
1834
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex-1 overflow-auto relative", children: [
|
|
1835
|
+
/* @__PURE__ */ jsx23(TabsContent, { value: "grid", className: "h-full m-0", children: /* @__PURE__ */ jsx23(GridView, __spreadValues({}, commonProps)) }),
|
|
1836
|
+
/* @__PURE__ */ jsx23(TabsContent, { value: "form", className: "h-full m-0", children: /* @__PURE__ */ jsx23(FormView, __spreadValues({}, commonProps)) }),
|
|
1837
|
+
hasSelectField && /* @__PURE__ */ jsx23(TabsContent, { value: "kanban", className: "h-full m-0", children: /* @__PURE__ */ jsx23(KanbanView, __spreadValues({}, commonProps)) }),
|
|
1838
|
+
/* @__PURE__ */ jsx23(TabsContent, { value: "gallery", className: "h-full m-0", children: /* @__PURE__ */ jsx23(GalleryView, __spreadValues({}, commonProps)) }),
|
|
1839
|
+
hasDateField && /* @__PURE__ */ jsx23(TabsContent, { value: "calendar", className: "h-full m-0", children: /* @__PURE__ */ jsx23(CalendarView, __spreadValues({}, commonProps)) })
|
|
1840
|
+
] })
|
|
1841
|
+
] }),
|
|
1842
|
+
/* @__PURE__ */ jsx23(
|
|
1843
|
+
RecordModal,
|
|
1844
|
+
{
|
|
1845
|
+
isOpen: modalState.isOpen,
|
|
1846
|
+
onClose: () => setModalState((s) => __spreadProps(__spreadValues({}, s), { isOpen: false })),
|
|
1847
|
+
mode: modalState.mode,
|
|
1848
|
+
schema,
|
|
1849
|
+
record: modalState.record,
|
|
1850
|
+
initialValues: modalState.initialValues,
|
|
1851
|
+
onSubmit: (values) => {
|
|
1852
|
+
if (modalState.mode === "add") {
|
|
1853
|
+
handleCreateRecord(values);
|
|
1854
|
+
} else if (modalState.mode === "edit" && modalState.record) {
|
|
1855
|
+
handleUpdateRecord(modalState.record.id, values);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
)
|
|
1860
|
+
] });
|
|
1861
|
+
}
|
|
1862
|
+
export {
|
|
1863
|
+
DataViews,
|
|
1864
|
+
getDateField,
|
|
1865
|
+
getPrimaryField,
|
|
1866
|
+
getSelectField,
|
|
1867
|
+
tableSchema
|
|
1868
|
+
};
|