@questpie/admin 0.0.1 → 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/README.md +439 -424
- package/dist/auth-layout-M8K8_q5R.mjs +181 -0
- package/dist/auth-layout-M8K8_q5R.mjs.map +1 -0
- package/dist/bulk-upload-dialog-h7zXD78Y.mjs +274 -0
- package/dist/bulk-upload-dialog-h7zXD78Y.mjs.map +1 -0
- package/dist/{components/ui/card.mjs → card-BKHjBQfw.mjs} +8 -8
- package/dist/card-BKHjBQfw.mjs.map +1 -0
- package/dist/client/styles/index.css +434 -0
- package/dist/client-BCGpkAz6.mjs +22635 -0
- package/dist/client-BCGpkAz6.mjs.map +1 -0
- package/dist/client-CcWZbkBP.d.mts +13585 -0
- package/dist/client-CcWZbkBP.d.mts.map +1 -0
- package/dist/client.d.mts +3 -0
- package/dist/client.mjs +14 -0
- package/dist/content-locales-provider-BXvuIgfg.mjs +1650 -0
- package/dist/content-locales-provider-BXvuIgfg.mjs.map +1 -0
- package/dist/dashboard-page-B4PGEdc2.mjs +2500 -0
- package/dist/dashboard-page-B4PGEdc2.mjs.map +1 -0
- package/dist/dashboard-page-CVlyR40m.mjs +6 -0
- package/dist/dropzone-Do3awXKd.mjs +634 -0
- package/dist/dropzone-Do3awXKd.mjs.map +1 -0
- package/dist/{views/auth/forgot-password-form.mjs → forgot-password-page-Bcp-An4Y.mjs} +87 -14
- package/dist/forgot-password-page-Bcp-An4Y.mjs.map +1 -0
- package/dist/forgot-password-page-CIILVhfo.mjs +7 -0
- package/dist/index-B9Xwk4hi.d.mts +2753 -0
- package/dist/index-B9Xwk4hi.d.mts.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +14 -0
- package/dist/login-page-8K7fo0qK.mjs +7 -0
- package/dist/login-page-CP4gA-dl.mjs +298 -0
- package/dist/login-page-CP4gA-dl.mjs.map +1 -0
- package/dist/preview-utils-BKQ9-TMa.mjs +65 -0
- package/dist/preview-utils-BKQ9-TMa.mjs.map +1 -0
- package/dist/{views/auth/reset-password-form.mjs → reset-password-page-BqfDmLxA.mjs} +111 -14
- package/dist/reset-password-page-BqfDmLxA.mjs.map +1 -0
- package/dist/reset-password-page-DLATv0xQ.mjs +7 -0
- package/dist/runtime-6VZM878K.mjs +69 -0
- package/dist/runtime-6VZM878K.mjs.map +1 -0
- package/dist/saved-views.types-BMsz5mCy.d.mts +42 -0
- package/dist/saved-views.types-BMsz5mCy.d.mts.map +1 -0
- package/dist/server.d.mts +250 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +832 -0
- package/dist/server.mjs.map +1 -0
- package/dist/setup-page-CMZ5P_OE.mjs +6 -0
- package/dist/setup-page-YAP_fzqh.mjs +264 -0
- package/dist/setup-page-YAP_fzqh.mjs.map +1 -0
- package/dist/shared.d.mts +57 -0
- package/dist/shared.d.mts.map +1 -0
- package/dist/shared.mjs +3 -0
- package/dist/{hooks/use-auth.mjs → use-auth-BoLmWtmU.mjs} +42 -30
- package/dist/use-auth-BoLmWtmU.mjs.map +1 -0
- package/package.json +48 -197
- package/.turbo/turbo-build.log +0 -108
- package/CHANGELOG.md +0 -10
- package/STATUS.md +0 -917
- package/VALIDATION.md +0 -602
- package/components.json +0 -24
- package/dist/__tests__/setup.mjs +0 -38
- package/dist/__tests__/test-utils.mjs +0 -45
- package/dist/__tests__/vitest.d.mjs +0 -3
- package/dist/components/admin-app.mjs +0 -69
- package/dist/components/fields/array-field.mjs +0 -190
- package/dist/components/fields/checkbox-field.mjs +0 -34
- package/dist/components/fields/custom-field.mjs +0 -32
- package/dist/components/fields/date-field.mjs +0 -41
- package/dist/components/fields/datetime-field.mjs +0 -42
- package/dist/components/fields/email-field.mjs +0 -37
- package/dist/components/fields/embedded-collection.mjs +0 -253
- package/dist/components/fields/field-types.mjs +0 -1
- package/dist/components/fields/field-utils.mjs +0 -10
- package/dist/components/fields/field-wrapper.mjs +0 -34
- package/dist/components/fields/index.mjs +0 -23
- package/dist/components/fields/json-field.mjs +0 -243
- package/dist/components/fields/locale-badge.mjs +0 -16
- package/dist/components/fields/number-field.mjs +0 -39
- package/dist/components/fields/password-field.mjs +0 -37
- package/dist/components/fields/relation-field.mjs +0 -104
- package/dist/components/fields/relation-picker.mjs +0 -229
- package/dist/components/fields/relation-select.mjs +0 -188
- package/dist/components/fields/rich-text-editor/index.mjs +0 -897
- package/dist/components/fields/select-field.mjs +0 -41
- package/dist/components/fields/switch-field.mjs +0 -34
- package/dist/components/fields/text-field.mjs +0 -38
- package/dist/components/fields/textarea-field.mjs +0 -38
- package/dist/components/index.mjs +0 -59
- package/dist/components/primitives/checkbox-input.mjs +0 -127
- package/dist/components/primitives/date-input.mjs +0 -303
- package/dist/components/primitives/index.mjs +0 -12
- package/dist/components/primitives/number-input.mjs +0 -104
- package/dist/components/primitives/select-input.mjs +0 -177
- package/dist/components/primitives/tag-input.mjs +0 -135
- package/dist/components/primitives/text-input.mjs +0 -39
- package/dist/components/primitives/textarea-input.mjs +0 -37
- package/dist/components/primitives/toggle-input.mjs +0 -31
- package/dist/components/primitives/types.mjs +0 -12
- package/dist/components/ui/accordion.mjs +0 -55
- package/dist/components/ui/avatar.mjs +0 -54
- package/dist/components/ui/badge.mjs +0 -34
- package/dist/components/ui/button.mjs +0 -48
- package/dist/components/ui/checkbox.mjs +0 -21
- package/dist/components/ui/combobox.mjs +0 -163
- package/dist/components/ui/dialog.mjs +0 -95
- package/dist/components/ui/dropdown-menu.mjs +0 -138
- package/dist/components/ui/field.mjs +0 -113
- package/dist/components/ui/input-group.mjs +0 -82
- package/dist/components/ui/input.mjs +0 -17
- package/dist/components/ui/label.mjs +0 -15
- package/dist/components/ui/popover.mjs +0 -56
- package/dist/components/ui/scroll-area.mjs +0 -38
- package/dist/components/ui/select.mjs +0 -100
- package/dist/components/ui/separator.mjs +0 -16
- package/dist/components/ui/sheet.mjs +0 -90
- package/dist/components/ui/sidebar.mjs +0 -387
- package/dist/components/ui/skeleton.mjs +0 -14
- package/dist/components/ui/spinner.mjs +0 -16
- package/dist/components/ui/switch.mjs +0 -22
- package/dist/components/ui/table.mjs +0 -68
- package/dist/components/ui/tabs.mjs +0 -48
- package/dist/components/ui/textarea.mjs +0 -15
- package/dist/components/ui/tooltip.mjs +0 -44
- package/dist/config/component-registry.mjs +0 -38
- package/dist/config/index.mjs +0 -129
- package/dist/hooks/admin-provider.mjs +0 -70
- package/dist/hooks/index.mjs +0 -7
- package/dist/hooks/store.mjs +0 -178
- package/dist/hooks/use-collection-db.mjs +0 -146
- package/dist/hooks/use-collection.mjs +0 -112
- package/dist/hooks/use-global.mjs +0 -46
- package/dist/hooks/use-mobile.mjs +0 -20
- package/dist/lib/utils.mjs +0 -10
- package/dist/styles/index.css +0 -336
- package/dist/styles/index.mjs +0 -1
- package/dist/utils/index.mjs +0 -9
- package/dist/views/auth/auth-layout.mjs +0 -52
- package/dist/views/auth/index.mjs +0 -6
- package/dist/views/auth/login-form.mjs +0 -156
- package/dist/views/collection/auto-form-fields.mjs +0 -525
- package/dist/views/collection/collection-form.mjs +0 -91
- package/dist/views/collection/collection-list.mjs +0 -76
- package/dist/views/collection/form-field.mjs +0 -42
- package/dist/views/collection/index.mjs +0 -6
- package/dist/views/common/index.mjs +0 -4
- package/dist/views/common/locale-switcher.mjs +0 -39
- package/dist/views/common/version-history.mjs +0 -272
- package/dist/views/index.mjs +0 -9
- package/dist/views/layout/admin-layout.mjs +0 -40
- package/dist/views/layout/admin-router.mjs +0 -95
- package/dist/views/layout/admin-sidebar.mjs +0 -63
- package/dist/views/layout/index.mjs +0 -5
- package/src/__tests__/setup.ts +0 -44
- package/src/__tests__/test-utils.tsx +0 -49
- package/src/__tests__/vitest.d.ts +0 -9
- package/src/components/admin-app.tsx +0 -221
- package/src/components/fields/array-field.tsx +0 -237
- package/src/components/fields/checkbox-field.tsx +0 -47
- package/src/components/fields/custom-field.tsx +0 -50
- package/src/components/fields/date-field.tsx +0 -65
- package/src/components/fields/datetime-field.tsx +0 -67
- package/src/components/fields/email-field.tsx +0 -51
- package/src/components/fields/embedded-collection.tsx +0 -315
- package/src/components/fields/field-types.ts +0 -162
- package/src/components/fields/field-utils.ts +0 -6
- package/src/components/fields/field-wrapper.tsx +0 -52
- package/src/components/fields/index.ts +0 -66
- package/src/components/fields/json-field.tsx +0 -440
- package/src/components/fields/locale-badge.tsx +0 -15
- package/src/components/fields/number-field.tsx +0 -57
- package/src/components/fields/password-field.tsx +0 -51
- package/src/components/fields/relation-field.tsx +0 -243
- package/src/components/fields/relation-picker.tsx +0 -402
- package/src/components/fields/relation-select.tsx +0 -327
- package/src/components/fields/rich-text-editor/index.tsx +0 -1337
- package/src/components/fields/select-field.tsx +0 -61
- package/src/components/fields/switch-field.tsx +0 -47
- package/src/components/fields/text-field.tsx +0 -55
- package/src/components/fields/textarea-field.tsx +0 -55
- package/src/components/index.ts +0 -40
- package/src/components/primitives/checkbox-input.tsx +0 -193
- package/src/components/primitives/date-input.tsx +0 -401
- package/src/components/primitives/index.ts +0 -24
- package/src/components/primitives/number-input.tsx +0 -132
- package/src/components/primitives/select-input.tsx +0 -296
- package/src/components/primitives/tag-input.tsx +0 -200
- package/src/components/primitives/text-input.tsx +0 -49
- package/src/components/primitives/textarea-input.tsx +0 -46
- package/src/components/primitives/toggle-input.tsx +0 -36
- package/src/components/primitives/types.ts +0 -235
- package/src/components/ui/accordion.tsx +0 -72
- package/src/components/ui/avatar.tsx +0 -106
- package/src/components/ui/badge.tsx +0 -48
- package/src/components/ui/button.tsx +0 -53
- package/src/components/ui/card.tsx +0 -94
- package/src/components/ui/checkbox.tsx +0 -27
- package/src/components/ui/combobox.tsx +0 -290
- package/src/components/ui/dialog.tsx +0 -151
- package/src/components/ui/dropdown-menu.tsx +0 -254
- package/src/components/ui/field.tsx +0 -227
- package/src/components/ui/input-group.tsx +0 -149
- package/src/components/ui/input.tsx +0 -20
- package/src/components/ui/label.tsx +0 -18
- package/src/components/ui/popover.tsx +0 -88
- package/src/components/ui/scroll-area.tsx +0 -53
- package/src/components/ui/select.tsx +0 -192
- package/src/components/ui/separator.tsx +0 -23
- package/src/components/ui/sheet.tsx +0 -127
- package/src/components/ui/sidebar.tsx +0 -723
- package/src/components/ui/skeleton.tsx +0 -13
- package/src/components/ui/spinner.tsx +0 -10
- package/src/components/ui/switch.tsx +0 -32
- package/src/components/ui/table.tsx +0 -99
- package/src/components/ui/tabs.tsx +0 -82
- package/src/components/ui/textarea.tsx +0 -18
- package/src/components/ui/tooltip.tsx +0 -70
- package/src/config/component-registry.ts +0 -190
- package/src/config/index.ts +0 -1099
- package/src/hooks/README.md +0 -269
- package/src/hooks/admin-provider.tsx +0 -110
- package/src/hooks/index.ts +0 -41
- package/src/hooks/store.ts +0 -248
- package/src/hooks/use-auth.ts +0 -168
- package/src/hooks/use-collection-db.ts +0 -209
- package/src/hooks/use-collection.ts +0 -156
- package/src/hooks/use-global.ts +0 -69
- package/src/hooks/use-mobile.ts +0 -21
- package/src/lib/utils.ts +0 -6
- package/src/styles/index.css +0 -340
- package/src/utils/index.ts +0 -6
- package/src/views/auth/auth-layout.tsx +0 -77
- package/src/views/auth/forgot-password-form.tsx +0 -192
- package/src/views/auth/index.ts +0 -21
- package/src/views/auth/login-form.tsx +0 -229
- package/src/views/auth/reset-password-form.tsx +0 -232
- package/src/views/collection/auto-form-fields.tsx +0 -982
- package/src/views/collection/collection-form.tsx +0 -186
- package/src/views/collection/collection-list.tsx +0 -223
- package/src/views/collection/form-field.tsx +0 -52
- package/src/views/collection/index.ts +0 -15
- package/src/views/common/index.ts +0 -8
- package/src/views/common/locale-switcher.tsx +0 -45
- package/src/views/common/version-history.tsx +0 -406
- package/src/views/index.ts +0 -25
- package/src/views/layout/admin-layout.tsx +0 -117
- package/src/views/layout/admin-router.tsx +0 -206
- package/src/views/layout/admin-sidebar.tsx +0 -185
- package/src/views/layout/index.ts +0 -12
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
- package/tsdown.config.ts +0 -13
- package/vitest.config.ts +0 -29
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { g as cn } from "./content-locales-provider-BXvuIgfg.mjs";
|
|
2
|
+
import { i as CardDescription, o as CardHeader, r as CardContent, s as CardTitle, t as Card } from "./card-BKHjBQfw.mjs";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { Input } from "@base-ui/react/input";
|
|
7
|
+
import { Separator } from "@base-ui/react/separator";
|
|
8
|
+
|
|
9
|
+
//#region src/client/components/ui/input.tsx
|
|
10
|
+
function Input$1({ className, type, ...props }) {
|
|
11
|
+
return /* @__PURE__ */ jsx(Input, {
|
|
12
|
+
type,
|
|
13
|
+
"data-slot": "input",
|
|
14
|
+
className: cn("bg-input/20 backdrop-blur-sm border-input/80 focus-visible:border-ring focus-visible:ring-ring/30 focus:border-ring focus:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 border px-3 py-1.5 text-sm transition-all file:h-7 file:text-sm file:font-medium focus-visible:ring-[2px] focus:ring-[2px] aria-invalid:ring-[2px] file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", className),
|
|
15
|
+
...props
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/client/components/ui/label.tsx
|
|
21
|
+
function Label({ className, ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsx("label", {
|
|
23
|
+
"data-slot": "label",
|
|
24
|
+
className: cn("gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed", className),
|
|
25
|
+
...props
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/client/components/ui/separator.tsx
|
|
31
|
+
function Separator$1({ className, orientation = "horizontal", ...props }) {
|
|
32
|
+
return /* @__PURE__ */ jsx(Separator, {
|
|
33
|
+
"data-slot": "separator",
|
|
34
|
+
orientation,
|
|
35
|
+
className: cn("bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch", className),
|
|
36
|
+
...props
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/client/components/ui/field.tsx
|
|
42
|
+
function FieldGroup({ className, ...props }) {
|
|
43
|
+
return /* @__PURE__ */ jsx("div", {
|
|
44
|
+
"data-slot": "field-group",
|
|
45
|
+
className: cn("gap-4 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 group/field-group @container/field-group flex w-full flex-col", className),
|
|
46
|
+
...props
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const fieldVariants = cva("data-[invalid=true]:text-destructive gap-2 group/field flex w-full", {
|
|
50
|
+
variants: { orientation: {
|
|
51
|
+
vertical: "flex-col [&>*]:w-full [&>.sr-only]:w-auto",
|
|
52
|
+
horizontal: "flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
53
|
+
responsive: "flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
|
|
54
|
+
} },
|
|
55
|
+
defaultVariants: { orientation: "vertical" }
|
|
56
|
+
});
|
|
57
|
+
function Field({ className, orientation = "vertical", ...props }) {
|
|
58
|
+
return /* @__PURE__ */ jsx("div", {
|
|
59
|
+
role: "group",
|
|
60
|
+
"data-slot": "field",
|
|
61
|
+
"data-orientation": orientation,
|
|
62
|
+
className: cn(fieldVariants({ orientation }), className),
|
|
63
|
+
...props
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function FieldContent({ className, ...props }) {
|
|
67
|
+
return /* @__PURE__ */ jsx("div", {
|
|
68
|
+
"data-slot": "field-content",
|
|
69
|
+
className: cn("gap-0.5 group/field-content flex flex-1 flex-col leading-snug", className),
|
|
70
|
+
...props
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function FieldLabel({ className, ...props }) {
|
|
74
|
+
return /* @__PURE__ */ jsx(Label, {
|
|
75
|
+
"data-slot": "field-label",
|
|
76
|
+
className: cn("has-data-checked:bg-primary/5 dark:has-data-checked:bg-primary/10 gap-2 group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-2 group/field-label peer/field-label flex w-fit leading-snug", "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col", className),
|
|
77
|
+
...props
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function FieldDescription({ className, ...props }) {
|
|
81
|
+
return /* @__PURE__ */ jsx("p", {
|
|
82
|
+
"data-slot": "field-description",
|
|
83
|
+
className: cn("text-muted-foreground text-left text-xs/relaxed [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance", "last:mt-0 nth-last-2:-mt-1", "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", className),
|
|
84
|
+
...props
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function FieldError({ className, children, errors, ...props }) {
|
|
88
|
+
const content = useMemo(() => {
|
|
89
|
+
if (children) return children;
|
|
90
|
+
if (!errors?.length) return null;
|
|
91
|
+
const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
|
|
92
|
+
if (uniqueErrors?.length == 1) return uniqueErrors[0]?.message;
|
|
93
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
94
|
+
className: "ml-4 flex list-disc flex-col gap-1",
|
|
95
|
+
children: uniqueErrors.map((error, index) => error?.message && /* @__PURE__ */ jsx("li", { children: error.message }, index))
|
|
96
|
+
});
|
|
97
|
+
}, [children, errors]);
|
|
98
|
+
if (!content) return null;
|
|
99
|
+
return /* @__PURE__ */ jsx("div", {
|
|
100
|
+
role: "alert",
|
|
101
|
+
"data-slot": "field-error",
|
|
102
|
+
className: cn("text-destructive text-xs/relaxed font-normal", className),
|
|
103
|
+
...props,
|
|
104
|
+
children: content
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/client/components/ui/alert.tsx
|
|
110
|
+
const alertVariants = cva("grid gap-0.5 border px-2 py-1.5 text-left text-xs/relaxed has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-1.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-3.5 w-full relative group/alert", {
|
|
111
|
+
variants: { variant: {
|
|
112
|
+
default: "bg-card text-card-foreground",
|
|
113
|
+
destructive: "text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current"
|
|
114
|
+
} },
|
|
115
|
+
defaultVariants: { variant: "default" }
|
|
116
|
+
});
|
|
117
|
+
function Alert({ className, variant, ...props }) {
|
|
118
|
+
return /* @__PURE__ */ jsx("div", {
|
|
119
|
+
"data-slot": "alert",
|
|
120
|
+
role: "alert",
|
|
121
|
+
className: cn(alertVariants({ variant }), className),
|
|
122
|
+
...props
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function AlertDescription({ className, ...props }) {
|
|
126
|
+
return /* @__PURE__ */ jsx("div", {
|
|
127
|
+
"data-slot": "alert-description",
|
|
128
|
+
className: cn("text-muted-foreground text-xs/relaxed text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3", className),
|
|
129
|
+
...props
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/client/views/auth/auth-layout.tsx
|
|
135
|
+
/**
|
|
136
|
+
* Centered layout for authentication pages (login, register, forgot password, etc.)
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```tsx
|
|
140
|
+
* <AuthLayout
|
|
141
|
+
* title="Sign in"
|
|
142
|
+
* description="Enter your credentials to access the admin panel"
|
|
143
|
+
* logo={<Logo />}
|
|
144
|
+
* footer={<Link to="/forgot-password">Forgot password?</Link>}
|
|
145
|
+
* >
|
|
146
|
+
* <LoginForm />
|
|
147
|
+
* </AuthLayout>
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
function AuthLayout({ title, description, logo, footer, children, className }) {
|
|
151
|
+
return /* @__PURE__ */ jsx("div", {
|
|
152
|
+
className: "bg-background flex min-h-screen flex-col items-center justify-center p-4",
|
|
153
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
154
|
+
className: "w-full max-w-sm space-y-6",
|
|
155
|
+
children: [
|
|
156
|
+
logo && /* @__PURE__ */ jsx("div", {
|
|
157
|
+
className: "flex justify-center",
|
|
158
|
+
children: logo
|
|
159
|
+
}),
|
|
160
|
+
/* @__PURE__ */ jsxs(Card, {
|
|
161
|
+
className: cn("w-full", className),
|
|
162
|
+
children: [/* @__PURE__ */ jsxs(CardHeader, {
|
|
163
|
+
className: "text-center",
|
|
164
|
+
children: [/* @__PURE__ */ jsx(CardTitle, {
|
|
165
|
+
className: "text-lg",
|
|
166
|
+
children: title
|
|
167
|
+
}), description && /* @__PURE__ */ jsx(CardDescription, { children: description })]
|
|
168
|
+
}), /* @__PURE__ */ jsx(CardContent, { children })]
|
|
169
|
+
}),
|
|
170
|
+
footer && /* @__PURE__ */ jsx("div", {
|
|
171
|
+
className: "text-muted-foreground text-center text-xs",
|
|
172
|
+
children: footer
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
})
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
export { FieldContent as a, FieldGroup as c, Label as d, Input$1 as f, Field as i, FieldLabel as l, Alert as n, FieldDescription as o, AlertDescription as r, FieldError as s, AuthLayout as t, Separator$1 as u };
|
|
181
|
+
//# sourceMappingURL=auth-layout-M8K8_q5R.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-layout-M8K8_q5R.mjs","names":["Input","InputPrimitive","Separator","SeparatorPrimitive"],"sources":["../src/client/components/ui/input.tsx","../src/client/components/ui/label.tsx","../src/client/components/ui/separator.tsx","../src/client/components/ui/field.tsx","../src/client/components/ui/alert.tsx","../src/client/views/auth/auth-layout.tsx"],"sourcesContent":["import { Input as InputPrimitive } from \"@base-ui/react/input\";\nimport type * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n\treturn (\n\t\t<InputPrimitive\n\t\t\ttype={type}\n\t\t\tdata-slot=\"input\"\n\t\t\tclassName={cn(\n\t\t\t\t\"bg-input/20 backdrop-blur-sm border-input/80 focus-visible:border-ring focus-visible:ring-ring/30 focus:border-ring focus:ring-ring/30 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 h-9 border px-3 py-1.5 text-sm transition-all file:h-7 file:text-sm file:font-medium focus-visible:ring-[2px] focus:ring-[2px] aria-invalid:ring-[2px] file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport { Input };\n","import type * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Label({ className, ...props }: React.ComponentProps<\"label\">) {\n return (\n <label\n data-slot=\"label\"\n className={cn(\n \"gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Label };\n","import { Separator as SeparatorPrimitive } from \"@base-ui/react/separator\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Separator({\n className,\n orientation = \"horizontal\",\n ...props\n}: SeparatorPrimitive.Props) {\n return (\n <SeparatorPrimitive\n data-slot=\"separator\"\n orientation={orientation}\n className={cn(\n \"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Separator };\n","\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { useMemo } from \"react\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Label } from \"./label\";\nimport { Separator } from \"./separator\";\n\nfunction FieldSet({ className, ...props }: React.ComponentProps<\"fieldset\">) {\n return (\n <fieldset\n data-slot=\"field-set\"\n className={cn(\n \"gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction FieldLegend({\n className,\n variant = \"legend\",\n ...props\n}: React.ComponentProps<\"legend\"> & { variant?: \"legend\" | \"label\" }) {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\n \"mb-2 font-medium data-[variant=label]:text-xs/relaxed data-[variant=legend]:text-sm\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction FieldGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n \"gap-4 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 group/field-group @container/field-group flex w-full flex-col\",\n className,\n )}\n {...props}\n />\n );\n}\n\nconst fieldVariants = cva(\n \"data-[invalid=true]:text-destructive gap-2 group/field flex w-full\",\n {\n variants: {\n orientation: {\n vertical: \"flex-col [&>*]:w-full [&>.sr-only]:w-auto\",\n horizontal:\n \"flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n responsive:\n \"flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\",\n },\n },\n defaultVariants: {\n orientation: \"vertical\",\n },\n },\n);\n\nfunction Field({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof fieldVariants>) {\n return (\n <div\n role=\"group\"\n data-slot=\"field\"\n data-orientation={orientation}\n className={cn(fieldVariants({ orientation }), className)}\n {...props}\n />\n );\n}\n\nfunction FieldContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(\n \"gap-0.5 group/field-content flex flex-1 flex-col leading-snug\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction FieldLabel({\n className,\n ...props\n}: React.ComponentProps<typeof Label>) {\n return (\n <Label\n data-slot=\"field-label\"\n className={cn(\n \"has-data-checked:bg-primary/5 dark:has-data-checked:bg-primary/10 gap-2 group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-2 group/field-label peer/field-label flex w-fit leading-snug\",\n \"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction FieldTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"field-label\"\n className={cn(\n \"gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction FieldDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <p\n data-slot=\"field-description\"\n className={cn(\n \"text-muted-foreground text-left text-xs/relaxed [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance\",\n \"last:mt-0 nth-last-2:-mt-1\",\n \"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction FieldSeparator({\n children,\n className,\n ...props\n}: React.ComponentProps<\"div\"> & {\n children?: React.ReactNode;\n}) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn(\n \"-my-2 h-5 text-xs/relaxed group-data-[variant=outline]/field-group:-mb-2 relative\",\n className,\n )}\n {...props}\n >\n <Separator className=\"absolute inset-0 top-1/2\" />\n {children && (\n <span\n className=\"text-muted-foreground px-2 bg-background relative mx-auto block w-fit\"\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n );\n}\n\nfunction FieldError({\n className,\n children,\n errors,\n ...props\n}: React.ComponentProps<\"div\"> & {\n errors?: Array<{ message?: string } | undefined>;\n}) {\n const content = useMemo(() => {\n if (children) {\n return children;\n }\n\n if (!errors?.length) {\n return null;\n }\n\n const uniqueErrors = [\n ...new Map(errors.map((error) => [error?.message, error])).values(),\n ];\n\n if (uniqueErrors?.length == 1) {\n return uniqueErrors[0]?.message;\n }\n\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {uniqueErrors.map(\n (error, index) =>\n error?.message && <li key={index}>{error.message}</li>,\n )}\n </ul>\n );\n }, [children, errors]);\n\n if (!content) {\n return null;\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn(\"text-destructive text-xs/relaxed font-normal\", className)}\n {...props}\n >\n {content}\n </div>\n );\n}\n\nexport {\n Field,\n FieldLabel,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldContent,\n FieldTitle,\n};\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"#questpie/admin/client/lib/utils\";\n\nconst alertVariants = cva(\n \"grid gap-0.5 border px-2 py-1.5 text-left text-xs/relaxed has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-1.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-3.5 w-full relative group/alert\",\n {\n variants: {\n variant: {\n default: \"bg-card text-card-foreground\",\n destructive:\n \"text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n },\n);\n\nfunction Alert({\n className,\n variant,\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof alertVariants>) {\n return (\n <div\n data-slot=\"alert\"\n role=\"alert\"\n className={cn(alertVariants({ variant }), className)}\n {...props}\n />\n );\n}\n\nfunction AlertTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-title\"\n className={cn(\n \"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction AlertDescription({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-description\"\n className={cn(\n \"text-muted-foreground text-xs/relaxed text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction AlertAction({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-action\"\n className={cn(\"absolute top-1.5 right-2\", className)}\n {...props}\n />\n );\n}\n\nexport { Alert, AlertTitle, AlertDescription, AlertAction };\n","/**\n * Auth Layout - centered card layout for authentication pages\n */\n\nimport * as React from \"react\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"../../components/ui/card\";\nimport { cn } from \"../../lib/utils\";\n\nexport type AuthLayoutProps = {\n /** Page title */\n title: string;\n /** Page description */\n description?: string;\n /** Logo component or element */\n logo?: React.ReactNode;\n /** Footer content (e.g., links to other auth pages) */\n footer?: React.ReactNode;\n /** Form content */\n children: React.ReactNode;\n /** Additional class name for the card */\n className?: string;\n};\n\n/**\n * Centered layout for authentication pages (login, register, forgot password, etc.)\n *\n * @example\n * ```tsx\n * <AuthLayout\n * title=\"Sign in\"\n * description=\"Enter your credentials to access the admin panel\"\n * logo={<Logo />}\n * footer={<Link to=\"/forgot-password\">Forgot password?</Link>}\n * >\n * <LoginForm />\n * </AuthLayout>\n * ```\n */\nexport function AuthLayout({\n title,\n description,\n logo,\n footer,\n children,\n className,\n}: AuthLayoutProps) {\n return (\n <div className=\"bg-background flex min-h-screen flex-col items-center justify-center p-4\">\n <div className=\"w-full max-w-sm space-y-6\">\n {/* Logo */}\n {logo && <div className=\"flex justify-center\">{logo}</div>}\n\n {/* Main Card */}\n <Card className={cn(\"w-full\", className)}>\n <CardHeader className=\"text-center\">\n <CardTitle className=\"text-lg\">{title}</CardTitle>\n {description && <CardDescription>{description}</CardDescription>}\n </CardHeader>\n <CardContent>{children}</CardContent>\n </Card>\n\n {/* Footer */}\n {footer && (\n <div className=\"text-muted-foreground text-center text-xs\">\n {footer}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { g as cn, h as Button } from "./content-locales-provider-BXvuIgfg.mjs";
|
|
2
|
+
import "./runtime-6VZM878K.mjs";
|
|
3
|
+
import { a as ResponsiveDialogDescription, c as ResponsiveDialogTitle, i as ResponsiveDialogContent, n as useUpload, o as ResponsiveDialogFooter, r as ResponsiveDialog, s as ResponsiveDialogHeader, t as Dropzone } from "./dropzone-Do3awXKd.mjs";
|
|
4
|
+
import { CheckCircle, WarningCircle, X } from "@phosphor-icons/react";
|
|
5
|
+
import * as React$1 from "react";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { toast } from "sonner";
|
|
8
|
+
|
|
9
|
+
//#region src/client/components/media/bulk-upload-dialog.tsx
|
|
10
|
+
/**
|
|
11
|
+
* BulkUploadDialog Component
|
|
12
|
+
*
|
|
13
|
+
* Dialog for bulk uploading multiple files to the assets collection.
|
|
14
|
+
* Triggered from the assets collection header action "Upload Files".
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Multi-file dropzone
|
|
18
|
+
* - Individual file progress tracking
|
|
19
|
+
* - Validation (type, size)
|
|
20
|
+
* - Error handling with retry
|
|
21
|
+
* - Success notification
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <BulkUploadDialog
|
|
26
|
+
* collection="assets"
|
|
27
|
+
* onClose={() => setOpen(false)}
|
|
28
|
+
* onSuccess={() => queryClient.invalidateQueries()}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Sanitize filename for safe storage
|
|
34
|
+
*/
|
|
35
|
+
function sanitizeFilename(filename) {
|
|
36
|
+
const lastDot = filename.lastIndexOf(".");
|
|
37
|
+
const ext = lastDot > 0 ? filename.slice(lastDot) : "";
|
|
38
|
+
return ((lastDot > 0 ? filename.slice(0, lastDot) : filename).normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/\s+/g, "-").replace(/[^a-zA-Z0-9._-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase() || "file") + ext.toLowerCase();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Format file size
|
|
42
|
+
*/
|
|
43
|
+
function formatFileSize(bytes) {
|
|
44
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
45
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
46
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
47
|
+
}
|
|
48
|
+
function FileItem({ file, onRemove }) {
|
|
49
|
+
const statusIcon = {
|
|
50
|
+
pending: null,
|
|
51
|
+
uploading: null,
|
|
52
|
+
success: /* @__PURE__ */ jsx(CheckCircle, {
|
|
53
|
+
weight: "fill",
|
|
54
|
+
className: "size-5 text-green-600"
|
|
55
|
+
}),
|
|
56
|
+
error: /* @__PURE__ */ jsx(WarningCircle, {
|
|
57
|
+
weight: "fill",
|
|
58
|
+
className: "size-5 text-destructive"
|
|
59
|
+
})
|
|
60
|
+
};
|
|
61
|
+
const statusColor = {
|
|
62
|
+
pending: "text-muted-foreground",
|
|
63
|
+
uploading: "text-primary",
|
|
64
|
+
success: "text-green-600",
|
|
65
|
+
error: "text-destructive"
|
|
66
|
+
};
|
|
67
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
68
|
+
className: cn("flex items-start gap-3 rounded-lg border p-3", file.status === "error" && "border-destructive/50 bg-destructive/5", file.status === "success" && "border-green-600/50 bg-green-600/5"),
|
|
69
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
70
|
+
className: "min-w-0 flex-1",
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsxs("div", {
|
|
73
|
+
className: "flex items-center gap-2",
|
|
74
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
75
|
+
className: "truncate text-sm font-medium",
|
|
76
|
+
title: file.file.name,
|
|
77
|
+
children: file.file.name
|
|
78
|
+
}), statusIcon[file.status]]
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ jsx("p", {
|
|
81
|
+
className: "text-muted-foreground text-xs",
|
|
82
|
+
children: formatFileSize(file.file.size)
|
|
83
|
+
}),
|
|
84
|
+
file.status === "uploading" && /* @__PURE__ */ jsxs("div", {
|
|
85
|
+
className: "mt-2",
|
|
86
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
87
|
+
className: "bg-muted h-1.5 overflow-hidden rounded-full",
|
|
88
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
89
|
+
className: "bg-primary h-full rounded-full transition-all duration-300",
|
|
90
|
+
style: { width: `${file.progress}%` }
|
|
91
|
+
})
|
|
92
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
93
|
+
className: cn("mt-1 text-xs", statusColor[file.status]),
|
|
94
|
+
children: [
|
|
95
|
+
"Uploading... ",
|
|
96
|
+
file.progress,
|
|
97
|
+
"%"
|
|
98
|
+
]
|
|
99
|
+
})]
|
|
100
|
+
}),
|
|
101
|
+
file.status === "error" && file.error && /* @__PURE__ */ jsx("p", {
|
|
102
|
+
className: "mt-1 text-xs text-destructive",
|
|
103
|
+
children: file.error
|
|
104
|
+
}),
|
|
105
|
+
file.status === "success" && /* @__PURE__ */ jsx("p", {
|
|
106
|
+
className: cn("mt-1 text-xs", statusColor[file.status]),
|
|
107
|
+
children: "Uploaded successfully"
|
|
108
|
+
})
|
|
109
|
+
]
|
|
110
|
+
}), file.status === "pending" && onRemove && /* @__PURE__ */ jsx(Button, {
|
|
111
|
+
type: "button",
|
|
112
|
+
variant: "ghost",
|
|
113
|
+
size: "icon-xs",
|
|
114
|
+
onClick: onRemove,
|
|
115
|
+
className: "text-muted-foreground hover:text-destructive shrink-0",
|
|
116
|
+
children: /* @__PURE__ */ jsx(X, { weight: "bold" })
|
|
117
|
+
})]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function BulkUploadDialog({ collection = "assets", onClose, onSuccess }) {
|
|
121
|
+
const { uploadMany } = useUpload();
|
|
122
|
+
const [files, setFiles] = React$1.useState([]);
|
|
123
|
+
const [isUploading, setIsUploading] = React$1.useState(false);
|
|
124
|
+
const hasFiles = files.length > 0;
|
|
125
|
+
const pendingFiles = files.filter((f) => f.status === "pending");
|
|
126
|
+
const uploadedFiles = files.filter((f) => f.status === "success");
|
|
127
|
+
const failedFiles = files.filter((f) => f.status === "error");
|
|
128
|
+
const canUpload = pendingFiles.length > 0 && !isUploading;
|
|
129
|
+
const allComplete = hasFiles && pendingFiles.length === 0 && !isUploading;
|
|
130
|
+
const handleDrop = (droppedFiles) => {
|
|
131
|
+
const newFiles = droppedFiles.map((file) => {
|
|
132
|
+
const sanitizedName = sanitizeFilename(file.name);
|
|
133
|
+
return new File([file], sanitizedName, { type: file.type });
|
|
134
|
+
}).map((file) => ({
|
|
135
|
+
file,
|
|
136
|
+
status: "pending",
|
|
137
|
+
progress: 0
|
|
138
|
+
}));
|
|
139
|
+
setFiles((prev) => [...prev, ...newFiles]);
|
|
140
|
+
};
|
|
141
|
+
const handleRemove = (index) => {
|
|
142
|
+
setFiles((prev) => prev.filter((_, i) => i !== index));
|
|
143
|
+
};
|
|
144
|
+
const handleUploadAll = async () => {
|
|
145
|
+
if (!canUpload) return;
|
|
146
|
+
setIsUploading(true);
|
|
147
|
+
try {
|
|
148
|
+
const filesToUpload = files.filter((f) => f.status === "pending").map((f) => f.file);
|
|
149
|
+
for (let i = 0; i < filesToUpload.length; i++) {
|
|
150
|
+
const file = filesToUpload[i];
|
|
151
|
+
const fileIndex = files.findIndex((f) => f.file === file && f.status === "pending");
|
|
152
|
+
setFiles((prev) => prev.map((f, idx) => idx === fileIndex ? {
|
|
153
|
+
...f,
|
|
154
|
+
status: "uploading"
|
|
155
|
+
} : f));
|
|
156
|
+
try {
|
|
157
|
+
const asset = await uploadMany([file], {
|
|
158
|
+
collection,
|
|
159
|
+
onProgress: (progress) => {
|
|
160
|
+
setFiles((prev) => prev.map((f, idx) => idx === fileIndex ? {
|
|
161
|
+
...f,
|
|
162
|
+
progress
|
|
163
|
+
} : f));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
setFiles((prev) => prev.map((f, idx) => idx === fileIndex ? {
|
|
167
|
+
...f,
|
|
168
|
+
status: "success",
|
|
169
|
+
progress: 100,
|
|
170
|
+
asset: asset[0]
|
|
171
|
+
} : f));
|
|
172
|
+
} catch (err) {
|
|
173
|
+
const errorMessage = err instanceof Error ? err.message : "Upload failed";
|
|
174
|
+
setFiles((prev) => prev.map((f, idx) => idx === fileIndex ? {
|
|
175
|
+
...f,
|
|
176
|
+
status: "error",
|
|
177
|
+
error: errorMessage
|
|
178
|
+
} : f));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const successCount = files.filter((f) => f.status === "success").length;
|
|
182
|
+
const failureCount = files.filter((f) => f.status === "error").length;
|
|
183
|
+
if (successCount > 0) {
|
|
184
|
+
toast.success(`${successCount} file${successCount !== 1 ? "s" : ""} uploaded successfully`);
|
|
185
|
+
onSuccess?.();
|
|
186
|
+
}
|
|
187
|
+
if (failureCount > 0) toast.error(`${failureCount} file${failureCount !== 1 ? "s" : ""} failed to upload`);
|
|
188
|
+
} finally {
|
|
189
|
+
setIsUploading(false);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const handleClose = () => {
|
|
193
|
+
if (isUploading) {
|
|
194
|
+
toast.warning("Please wait for uploads to complete");
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
onClose();
|
|
198
|
+
};
|
|
199
|
+
const handleDone = () => {
|
|
200
|
+
setFiles([]);
|
|
201
|
+
onClose();
|
|
202
|
+
};
|
|
203
|
+
return /* @__PURE__ */ jsx(ResponsiveDialog, {
|
|
204
|
+
open: true,
|
|
205
|
+
onOpenChange: handleClose,
|
|
206
|
+
children: /* @__PURE__ */ jsxs(ResponsiveDialogContent, {
|
|
207
|
+
className: "flex max-h-[90vh] flex-col sm:max-w-2xl",
|
|
208
|
+
children: [
|
|
209
|
+
/* @__PURE__ */ jsxs(ResponsiveDialogHeader, { children: [/* @__PURE__ */ jsx(ResponsiveDialogTitle, { children: "Upload Files" }), /* @__PURE__ */ jsx(ResponsiveDialogDescription, { children: "Add multiple files to your media library" })] }),
|
|
210
|
+
/* @__PURE__ */ jsxs("div", {
|
|
211
|
+
className: "flex-1 space-y-4 overflow-y-auto",
|
|
212
|
+
children: [!allComplete && /* @__PURE__ */ jsx(Dropzone, {
|
|
213
|
+
onDrop: handleDrop,
|
|
214
|
+
multiple: true,
|
|
215
|
+
disabled: isUploading,
|
|
216
|
+
label: "Drop files here or click to browse",
|
|
217
|
+
hint: "Upload multiple files at once"
|
|
218
|
+
}), hasFiles && /* @__PURE__ */ jsxs("div", {
|
|
219
|
+
className: "space-y-2",
|
|
220
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
221
|
+
className: "flex items-center justify-between",
|
|
222
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
223
|
+
className: "text-sm font-medium",
|
|
224
|
+
children: [
|
|
225
|
+
"Files (",
|
|
226
|
+
files.length,
|
|
227
|
+
")"
|
|
228
|
+
]
|
|
229
|
+
}), uploadedFiles.length > 0 && /* @__PURE__ */ jsxs("p", {
|
|
230
|
+
className: "text-muted-foreground text-xs",
|
|
231
|
+
children: [
|
|
232
|
+
uploadedFiles.length,
|
|
233
|
+
" uploaded",
|
|
234
|
+
failedFiles.length > 0 && `, ${failedFiles.length} failed`
|
|
235
|
+
]
|
|
236
|
+
})]
|
|
237
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
238
|
+
className: "space-y-2",
|
|
239
|
+
children: files.map((file, index) => /* @__PURE__ */ jsx(FileItem, {
|
|
240
|
+
file,
|
|
241
|
+
onRemove: file.status === "pending" ? () => handleRemove(index) : void 0
|
|
242
|
+
}, `${file.file.name}-${index}`))
|
|
243
|
+
})]
|
|
244
|
+
})]
|
|
245
|
+
}),
|
|
246
|
+
/* @__PURE__ */ jsx(ResponsiveDialogFooter, {
|
|
247
|
+
className: "border-t pt-4",
|
|
248
|
+
children: allComplete ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Button, {
|
|
249
|
+
variant: "outline",
|
|
250
|
+
onClick: handleDone,
|
|
251
|
+
children: "Done"
|
|
252
|
+
}) }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
253
|
+
variant: "outline",
|
|
254
|
+
onClick: handleClose,
|
|
255
|
+
disabled: isUploading,
|
|
256
|
+
children: "Cancel"
|
|
257
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
258
|
+
onClick: handleUploadAll,
|
|
259
|
+
disabled: !canUpload || isUploading,
|
|
260
|
+
children: [
|
|
261
|
+
isUploading ? "Uploading..." : "Upload",
|
|
262
|
+
" ",
|
|
263
|
+
pendingFiles.length > 0 && `(${pendingFiles.length})`
|
|
264
|
+
]
|
|
265
|
+
})] })
|
|
266
|
+
})
|
|
267
|
+
]
|
|
268
|
+
})
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
export { BulkUploadDialog };
|
|
274
|
+
//# sourceMappingURL=bulk-upload-dialog-h7zXD78Y.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk-upload-dialog-h7zXD78Y.mjs","names":["React","newFiles: FileUploadState[]"],"sources":["../src/client/components/media/bulk-upload-dialog.tsx"],"sourcesContent":["/**\n * BulkUploadDialog Component\n *\n * Dialog for bulk uploading multiple files to the assets collection.\n * Triggered from the assets collection header action \"Upload Files\".\n *\n * Features:\n * - Multi-file dropzone\n * - Individual file progress tracking\n * - Validation (type, size)\n * - Error handling with retry\n * - Success notification\n *\n * @example\n * ```tsx\n * <BulkUploadDialog\n * collection=\"assets\"\n * onClose={() => setOpen(false)}\n * onSuccess={() => queryClient.invalidateQueries()}\n * />\n * ```\n */\n\nimport * as React from \"react\";\nimport { Trash, CheckCircle, WarningCircle, X } from \"@phosphor-icons/react\";\nimport { toast } from \"sonner\";\nimport { cn } from \"../../lib/utils\";\nimport { useUpload, type Asset } from \"../../hooks/use-upload\";\nimport { Button } from \"../ui/button\";\nimport {\n\tResponsiveDialog,\n\tResponsiveDialogContent,\n\tResponsiveDialogHeader,\n\tResponsiveDialogTitle,\n\tResponsiveDialogDescription,\n\tResponsiveDialogFooter,\n} from \"../ui/responsive-dialog\";\nimport { Dropzone } from \"../primitives/dropzone\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BulkUploadDialogProps {\n\t/**\n\t * Target collection\n\t * @default \"assets\"\n\t */\n\tcollection?: string;\n\n\t/**\n\t * Called when dialog should close\n\t */\n\tonClose: () => void;\n\n\t/**\n\t * Called after successful upload\n\t */\n\tonSuccess?: () => void;\n}\n\n/**\n * File upload state\n */\ninterface FileUploadState {\n\tfile: File;\n\tstatus: \"pending\" | \"uploading\" | \"success\" | \"error\";\n\tprogress: number;\n\tasset?: Asset;\n\terror?: string;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Sanitize filename for safe storage\n */\nfunction sanitizeFilename(filename: string): string {\n\tconst lastDot = filename.lastIndexOf(\".\");\n\tconst ext = lastDot > 0 ? filename.slice(lastDot) : \"\";\n\tconst name = lastDot > 0 ? filename.slice(0, lastDot) : filename;\n\n\tconst sanitized = name\n\t\t.normalize(\"NFD\")\n\t\t.replace(/[\\u0300-\\u036f]/g, \"\") // Remove diacritics\n\t\t.replace(/\\s+/g, \"-\") // Replace spaces with hyphens\n\t\t.replace(/[^a-zA-Z0-9._-]/g, \"\") // Remove invalid chars\n\t\t.replace(/-+/g, \"-\") // Collapse multiple hyphens\n\t\t.replace(/^-|-$/g, \"\") // Remove leading/trailing hyphens\n\t\t.toLowerCase();\n\n\treturn (sanitized || \"file\") + ext.toLowerCase();\n}\n\n/**\n * Format file size\n */\nfunction formatFileSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes} B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n\treturn `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ============================================================================\n// File Item Component\n// ============================================================================\n\ninterface FileItemProps {\n\tfile: FileUploadState;\n\tonRemove?: () => void;\n}\n\nfunction FileItem({ file, onRemove }: FileItemProps) {\n\tconst statusIcon = {\n\t\tpending: null,\n\t\tuploading: null,\n\t\tsuccess: <CheckCircle weight=\"fill\" className=\"size-5 text-green-600\" />,\n\t\terror: <WarningCircle weight=\"fill\" className=\"size-5 text-destructive\" />,\n\t};\n\n\tconst statusColor = {\n\t\tpending: \"text-muted-foreground\",\n\t\tuploading: \"text-primary\",\n\t\tsuccess: \"text-green-600\",\n\t\terror: \"text-destructive\",\n\t};\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"flex items-start gap-3 rounded-lg border p-3\",\n\t\t\t\tfile.status === \"error\" && \"border-destructive/50 bg-destructive/5\",\n\t\t\t\tfile.status === \"success\" && \"border-green-600/50 bg-green-600/5\",\n\t\t\t)}\n\t\t>\n\t\t\t{/* File info */}\n\t\t\t<div className=\"min-w-0 flex-1\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<p className=\"truncate text-sm font-medium\" title={file.file.name}>\n\t\t\t\t\t\t{file.file.name}\n\t\t\t\t\t</p>\n\t\t\t\t\t{statusIcon[file.status]}\n\t\t\t\t</div>\n\n\t\t\t\t<p className=\"text-muted-foreground text-xs\">\n\t\t\t\t\t{formatFileSize(file.file.size)}\n\t\t\t\t</p>\n\n\t\t\t\t{/* Progress bar */}\n\t\t\t\t{file.status === \"uploading\" && (\n\t\t\t\t\t<div className=\"mt-2\">\n\t\t\t\t\t\t<div className=\"bg-muted h-1.5 overflow-hidden rounded-full\">\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"bg-primary h-full rounded-full transition-all duration-300\"\n\t\t\t\t\t\t\t\tstyle={{ width: `${file.progress}%` }}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p className={cn(\"mt-1 text-xs\", statusColor[file.status])}>\n\t\t\t\t\t\t\tUploading... {file.progress}%\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Error message */}\n\t\t\t\t{file.status === \"error\" && file.error && (\n\t\t\t\t\t<p className=\"mt-1 text-xs text-destructive\">{file.error}</p>\n\t\t\t\t)}\n\n\t\t\t\t{/* Success message */}\n\t\t\t\t{file.status === \"success\" && (\n\t\t\t\t\t<p className={cn(\"mt-1 text-xs\", statusColor[file.status])}>\n\t\t\t\t\t\tUploaded successfully\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Remove button (only for pending files) */}\n\t\t\t{file.status === \"pending\" && onRemove && (\n\t\t\t\t<Button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-xs\"\n\t\t\t\t\tonClick={onRemove}\n\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive shrink-0\"\n\t\t\t\t>\n\t\t\t\t\t<X weight=\"bold\" />\n\t\t\t\t</Button>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport function BulkUploadDialog({\n\tcollection = \"assets\",\n\tonClose,\n\tonSuccess,\n}: BulkUploadDialogProps) {\n\tconst { uploadMany } = useUpload();\n\n\t// State\n\tconst [files, setFiles] = React.useState<FileUploadState[]>([]);\n\tconst [isUploading, setIsUploading] = React.useState(false);\n\n\t// Computed states\n\tconst hasFiles = files.length > 0;\n\tconst pendingFiles = files.filter((f) => f.status === \"pending\");\n\tconst uploadedFiles = files.filter((f) => f.status === \"success\");\n\tconst failedFiles = files.filter((f) => f.status === \"error\");\n\tconst canUpload = pendingFiles.length > 0 && !isUploading;\n\tconst allComplete =\n\t\thasFiles && pendingFiles.length === 0 && !isUploading;\n\n\t// Handle file drop\n\tconst handleDrop = (droppedFiles: File[]) => {\n\t\t// Sanitize filenames\n\t\tconst sanitizedFiles = droppedFiles.map((file) => {\n\t\t\tconst sanitizedName = sanitizeFilename(file.name);\n\t\t\treturn new File([file], sanitizedName, { type: file.type });\n\t\t});\n\n\t\tconst newFiles: FileUploadState[] = sanitizedFiles.map((file) => ({\n\t\t\tfile,\n\t\t\tstatus: \"pending\",\n\t\t\tprogress: 0,\n\t\t}));\n\n\t\tsetFiles((prev) => [...prev, ...newFiles]);\n\t};\n\n\t// Handle remove file\n\tconst handleRemove = (index: number) => {\n\t\tsetFiles((prev) => prev.filter((_, i) => i !== index));\n\t};\n\n\t// Handle upload all\n\tconst handleUploadAll = async () => {\n\t\tif (!canUpload) return;\n\n\t\tsetIsUploading(true);\n\n\t\ttry {\n\t\t\t// Get pending files\n\t\t\tconst filesToUpload = files\n\t\t\t\t.filter((f) => f.status === \"pending\")\n\t\t\t\t.map((f) => f.file);\n\n\t\t\t// Upload sequentially with progress tracking\n\t\t\tfor (let i = 0; i < filesToUpload.length; i++) {\n\t\t\t\tconst file = filesToUpload[i];\n\t\t\t\tconst fileIndex = files.findIndex(\n\t\t\t\t\t(f) => f.file === file && f.status === \"pending\",\n\t\t\t\t);\n\n\t\t\t\t// Mark as uploading\n\t\t\t\tsetFiles((prev) =>\n\t\t\t\t\tprev.map((f, idx) =>\n\t\t\t\t\t\tidx === fileIndex ? { ...f, status: \"uploading\" as const } : f,\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\ttry {\n\t\t\t\t\t// Upload file\n\t\t\t\t\tconst asset = await uploadMany([file], {\n\t\t\t\t\t\tcollection,\n\t\t\t\t\t\tonProgress: (progress) => {\n\t\t\t\t\t\t\tsetFiles((prev) =>\n\t\t\t\t\t\t\t\tprev.map((f, idx) =>\n\t\t\t\t\t\t\t\t\tidx === fileIndex ? { ...f, progress } : f,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\t// Mark as success\n\t\t\t\t\tsetFiles((prev) =>\n\t\t\t\t\t\tprev.map((f, idx) =>\n\t\t\t\t\t\t\tidx === fileIndex\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t...f,\n\t\t\t\t\t\t\t\t\t\tstatus: \"success\" as const,\n\t\t\t\t\t\t\t\t\t\tprogress: 100,\n\t\t\t\t\t\t\t\t\t\tasset: asset[0],\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: f,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// Mark as error\n\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\terr instanceof Error ? err.message : \"Upload failed\";\n\n\t\t\t\t\tsetFiles((prev) =>\n\t\t\t\t\t\tprev.map((f, idx) =>\n\t\t\t\t\t\t\tidx === fileIndex\n\t\t\t\t\t\t\t\t? { ...f, status: \"error\" as const, error: errorMessage }\n\t\t\t\t\t\t\t\t: f,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Show success toast\n\t\t\tconst successCount = files.filter((f) => f.status === \"success\").length;\n\t\t\tconst failureCount = files.filter((f) => f.status === \"error\").length;\n\n\t\t\tif (successCount > 0) {\n\t\t\t\ttoast.success(\n\t\t\t\t\t`${successCount} file${successCount !== 1 ? \"s\" : \"\"} uploaded successfully`,\n\t\t\t\t);\n\t\t\t\tonSuccess?.();\n\t\t\t}\n\n\t\t\tif (failureCount > 0) {\n\t\t\t\ttoast.error(\n\t\t\t\t\t`${failureCount} file${failureCount !== 1 ? \"s\" : \"\"} failed to upload`,\n\t\t\t\t);\n\t\t\t}\n\t\t} finally {\n\t\t\tsetIsUploading(false);\n\t\t}\n\t};\n\n\t// Handle close\n\tconst handleClose = () => {\n\t\tif (isUploading) {\n\t\t\ttoast.warning(\"Please wait for uploads to complete\");\n\t\t\treturn;\n\t\t}\n\t\tonClose();\n\t};\n\n\t// Handle done (close after all complete)\n\tconst handleDone = () => {\n\t\tsetFiles([]);\n\t\tonClose();\n\t};\n\n\treturn (\n\t\t<ResponsiveDialog open onOpenChange={handleClose}>\n\t\t\t<ResponsiveDialogContent className=\"flex max-h-[90vh] flex-col sm:max-w-2xl\">\n\t\t\t\t<ResponsiveDialogHeader>\n\t\t\t\t\t<ResponsiveDialogTitle>Upload Files</ResponsiveDialogTitle>\n\t\t\t\t\t<ResponsiveDialogDescription>\n\t\t\t\t\t\tAdd multiple files to your media library\n\t\t\t\t\t</ResponsiveDialogDescription>\n\t\t\t\t</ResponsiveDialogHeader>\n\n\t\t\t\t{/* Content */}\n\t\t\t\t<div className=\"flex-1 space-y-4 overflow-y-auto\">\n\t\t\t\t\t{/* Dropzone */}\n\t\t\t\t\t{!allComplete && (\n\t\t\t\t\t\t<Dropzone\n\t\t\t\t\t\t\tonDrop={handleDrop}\n\t\t\t\t\t\t\tmultiple={true}\n\t\t\t\t\t\t\tdisabled={isUploading}\n\t\t\t\t\t\t\tlabel=\"Drop files here or click to browse\"\n\t\t\t\t\t\t\thint=\"Upload multiple files at once\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Files list */}\n\t\t\t\t\t{hasFiles && (\n\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">\n\t\t\t\t\t\t\t\t\tFiles ({files.length})\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t{uploadedFiles.length > 0 && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-xs\">\n\t\t\t\t\t\t\t\t\t\t{uploadedFiles.length} uploaded\n\t\t\t\t\t\t\t\t\t\t{failedFiles.length > 0 &&\n\t\t\t\t\t\t\t\t\t\t\t`, ${failedFiles.length} failed`}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t{files.map((file, index) => (\n\t\t\t\t\t\t\t\t\t<FileItem\n\t\t\t\t\t\t\t\t\t\tkey={`${file.file.name}-${index}`}\n\t\t\t\t\t\t\t\t\t\tfile={file}\n\t\t\t\t\t\t\t\t\t\tonRemove={\n\t\t\t\t\t\t\t\t\t\t\tfile.status === \"pending\"\n\t\t\t\t\t\t\t\t\t\t\t\t? () => handleRemove(index)\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Footer */}\n\t\t\t\t<ResponsiveDialogFooter className=\"border-t pt-4\">\n\t\t\t\t\t{allComplete ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<Button variant=\"outline\" onClick={handleDone}>\n\t\t\t\t\t\t\t\tDone\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tonClick={handleClose}\n\t\t\t\t\t\t\t\tdisabled={isUploading}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tonClick={handleUploadAll}\n\t\t\t\t\t\t\t\tdisabled={!canUpload || isUploading}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isUploading ? \"Uploading...\" : \"Upload\"}{\" \"}\n\t\t\t\t\t\t\t\t{pendingFiles.length > 0 && `(${pendingFiles.length})`}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</ResponsiveDialogFooter>\n\t\t\t</ResponsiveDialogContent>\n\t\t</ResponsiveDialog>\n\t);\n}\n"],"mappings":"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"}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import "
|
|
1
|
+
import { g as cn } from "./content-locales-provider-BXvuIgfg.mjs";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { cn } from "../../lib/utils";
|
|
4
3
|
|
|
5
|
-
//#region src/components/ui/card.tsx
|
|
4
|
+
//#region src/client/components/ui/card.tsx
|
|
6
5
|
function Card({ className, size = "default", ...props }) {
|
|
7
6
|
return /* @__PURE__ */ jsx("div", {
|
|
8
7
|
"data-slot": "card",
|
|
9
8
|
"data-size": size,
|
|
10
|
-
className: cn("
|
|
9
|
+
className: cn("bg-card/40 backdrop-blur-lg text-card-foreground border-border/60 gap-5 overflow-hidden border py-5 text-xs/relaxed data-[size=sm]:gap-4 data-[size=sm]:py-4 group/card flex flex-col", className),
|
|
11
10
|
...props
|
|
12
11
|
});
|
|
13
12
|
}
|
|
14
13
|
function CardHeader({ className, ...props }) {
|
|
15
14
|
return /* @__PURE__ */ jsx("div", {
|
|
16
15
|
"data-slot": "card-header",
|
|
17
|
-
className: cn("gap-1
|
|
16
|
+
className: cn("gap-1.5 px-5 group-data-[size=sm]/card:px-4 [.border-b]:pb-5 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]", className),
|
|
18
17
|
...props
|
|
19
18
|
});
|
|
20
19
|
}
|
|
@@ -42,17 +41,18 @@ function CardAction({ className, ...props }) {
|
|
|
42
41
|
function CardContent({ className, ...props }) {
|
|
43
42
|
return /* @__PURE__ */ jsx("div", {
|
|
44
43
|
"data-slot": "card-content",
|
|
45
|
-
className: cn("px-
|
|
44
|
+
className: cn("px-5 group-data-[size=sm]/card:px-4", className),
|
|
46
45
|
...props
|
|
47
46
|
});
|
|
48
47
|
}
|
|
49
48
|
function CardFooter({ className, ...props }) {
|
|
50
49
|
return /* @__PURE__ */ jsx("div", {
|
|
51
50
|
"data-slot": "card-footer",
|
|
52
|
-
className: cn("
|
|
51
|
+
className: cn("px-5 group-data-[size=sm]/card:px-4 [.border-t]:pt-5 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center", className),
|
|
53
52
|
...props
|
|
54
53
|
});
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
//#endregion
|
|
58
|
-
export {
|
|
57
|
+
export { CardFooter as a, CardDescription as i, CardAction as n, CardHeader as o, CardContent as r, CardTitle as s, Card as t };
|
|
58
|
+
//# sourceMappingURL=card-BKHjBQfw.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"card-BKHjBQfw.mjs","names":[],"sources":["../src/client/components/ui/card.tsx"],"sourcesContent":["import type * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Card({\n className,\n size = \"default\",\n ...props\n}: React.ComponentProps<\"div\"> & { size?: \"default\" | \"sm\" }) {\n return (\n <div\n data-slot=\"card\"\n data-size={size}\n className={cn(\n \"bg-card/40 backdrop-blur-lg text-card-foreground border-border/60 gap-5 overflow-hidden border py-5 text-xs/relaxed data-[size=sm]:gap-4 data-[size=sm]:py-4 group/card flex flex-col\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-header\"\n className={cn(\n \"gap-1.5 px-5 group-data-[size=sm]/card:px-4 [.border-b]:pb-5 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-title\"\n className={cn(\"text-sm font-medium\", className)}\n {...props}\n />\n );\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-description\"\n className={cn(\"text-muted-foreground text-xs/relaxed\", className)}\n {...props}\n />\n );\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-action\"\n className={cn(\n \"col-start-2 row-span-2 row-start-1 self-start justify-self-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-content\"\n className={cn(\"px-5 group-data-[size=sm]/card:px-4\", className)}\n {...props}\n />\n );\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"card-footer\"\n className={cn(\n \"px-5 group-data-[size=sm]/card:px-4 [.border-t]:pt-5 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardAction,\n CardDescription,\n CardContent,\n};\n"],"mappings":";;;;AAIA,SAAS,KAAK,EACZ,WACA,OAAO,WACP,GAAG,SACyD;AAC5D,QACE,oBAAC;EACC,aAAU;EACV,aAAW;EACX,WAAW,GACT,yLACA,UACD;EACD,GAAI;GACJ;;AAIN,SAAS,WAAW,EAAE,WAAW,GAAG,SAAsC;AACxE,QACE,oBAAC;EACC,aAAU;EACV,WAAW,GACT,2RACA,UACD;EACD,GAAI;GACJ;;AAIN,SAAS,UAAU,EAAE,WAAW,GAAG,SAAsC;AACvE,QACE,oBAAC;EACC,aAAU;EACV,WAAW,GAAG,uBAAuB,UAAU;EAC/C,GAAI;GACJ;;AAIN,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAsC;AAC7E,QACE,oBAAC;EACC,aAAU;EACV,WAAW,GAAG,yCAAyC,UAAU;EACjE,GAAI;GACJ;;AAIN,SAAS,WAAW,EAAE,WAAW,GAAG,SAAsC;AACxE,QACE,oBAAC;EACC,aAAU;EACV,WAAW,GACT,kEACA,UACD;EACD,GAAI;GACJ;;AAIN,SAAS,YAAY,EAAE,WAAW,GAAG,SAAsC;AACzE,QACE,oBAAC;EACC,aAAU;EACV,WAAW,GAAG,uCAAuC,UAAU;EAC/D,GAAI;GACJ;;AAIN,SAAS,WAAW,EAAE,WAAW,GAAG,SAAsC;AACxE,QACE,oBAAC;EACC,aAAU;EACV,WAAW,GACT,qHACA,UACD;EACD,GAAI;GACJ"}
|