@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
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Controller } from "react-hook-form";
|
|
2
|
-
import { TextInput } from "../primitives/text-input";
|
|
3
|
-
import { FieldWrapper } from "./field-wrapper";
|
|
4
|
-
import { useResolvedControl } from "./field-utils";
|
|
5
|
-
import type { BaseFieldProps } from "./field-types";
|
|
6
|
-
|
|
7
|
-
export function EmailField({
|
|
8
|
-
name,
|
|
9
|
-
label,
|
|
10
|
-
description,
|
|
11
|
-
placeholder,
|
|
12
|
-
required,
|
|
13
|
-
disabled,
|
|
14
|
-
localized,
|
|
15
|
-
locale,
|
|
16
|
-
control,
|
|
17
|
-
className,
|
|
18
|
-
}: BaseFieldProps) {
|
|
19
|
-
const resolvedControl = useResolvedControl(control);
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<Controller
|
|
23
|
-
name={name}
|
|
24
|
-
control={resolvedControl}
|
|
25
|
-
render={({ field, fieldState }) => (
|
|
26
|
-
<FieldWrapper
|
|
27
|
-
name={name}
|
|
28
|
-
label={label}
|
|
29
|
-
description={description}
|
|
30
|
-
required={required}
|
|
31
|
-
disabled={disabled}
|
|
32
|
-
localized={localized}
|
|
33
|
-
locale={locale}
|
|
34
|
-
error={fieldState.error?.message}
|
|
35
|
-
>
|
|
36
|
-
<TextInput
|
|
37
|
-
id={name}
|
|
38
|
-
value={field.value ?? ""}
|
|
39
|
-
onChange={field.onChange}
|
|
40
|
-
type="email"
|
|
41
|
-
placeholder={placeholder}
|
|
42
|
-
disabled={disabled}
|
|
43
|
-
autoComplete="email"
|
|
44
|
-
aria-invalid={!!fieldState.error}
|
|
45
|
-
className={className}
|
|
46
|
-
/>
|
|
47
|
-
</FieldWrapper>
|
|
48
|
-
)}
|
|
49
|
-
/>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EmbeddedCollectionField Component
|
|
3
|
-
*
|
|
4
|
-
* Manages embedded collections with inline, modal, or drawer editing.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as React from "react";
|
|
8
|
-
import { useFieldArray, useFormContext, useWatch } from "react-hook-form";
|
|
9
|
-
import { CaretDown, CaretUp, Pencil, Plus, Trash } from "@phosphor-icons/react";
|
|
10
|
-
import { Button } from "../ui/button";
|
|
11
|
-
import { LocaleBadge } from "./locale-badge";
|
|
12
|
-
import {
|
|
13
|
-
Dialog,
|
|
14
|
-
DialogContent,
|
|
15
|
-
DialogDescription,
|
|
16
|
-
DialogHeader,
|
|
17
|
-
DialogTitle,
|
|
18
|
-
} from "../ui/dialog";
|
|
19
|
-
import {
|
|
20
|
-
Sheet,
|
|
21
|
-
SheetContent,
|
|
22
|
-
SheetDescription,
|
|
23
|
-
SheetHeader,
|
|
24
|
-
SheetTitle,
|
|
25
|
-
} from "../ui/sheet";
|
|
26
|
-
import type { EmbeddedCollectionProps } from "../../config/component-registry";
|
|
27
|
-
|
|
28
|
-
type RowLabel = ((item: any) => string) | string;
|
|
29
|
-
|
|
30
|
-
export interface EmbeddedCollectionFieldProps
|
|
31
|
-
extends Omit<EmbeddedCollectionProps, "rowLabel"> {
|
|
32
|
-
rowLabel?: RowLabel;
|
|
33
|
-
renderFields?: (index: number) => React.ReactNode;
|
|
34
|
-
minItems?: number;
|
|
35
|
-
maxItems?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function EmbeddedCollectionField({
|
|
39
|
-
name,
|
|
40
|
-
value,
|
|
41
|
-
collection,
|
|
42
|
-
mode = "inline",
|
|
43
|
-
orderable = false,
|
|
44
|
-
rowLabel,
|
|
45
|
-
label,
|
|
46
|
-
description,
|
|
47
|
-
required,
|
|
48
|
-
disabled,
|
|
49
|
-
readOnly,
|
|
50
|
-
error,
|
|
51
|
-
placeholder,
|
|
52
|
-
localized,
|
|
53
|
-
locale,
|
|
54
|
-
renderFields,
|
|
55
|
-
minItems,
|
|
56
|
-
maxItems,
|
|
57
|
-
}: EmbeddedCollectionFieldProps) {
|
|
58
|
-
const form = useFormContext();
|
|
59
|
-
const { control } = form;
|
|
60
|
-
const { fields, append, remove, move } = useFieldArray({ control, name });
|
|
61
|
-
const values = (useWatch({ control, name }) as any[] | undefined) ?? value;
|
|
62
|
-
|
|
63
|
-
const [activeIndex, setActiveIndex] = React.useState<number | null>(null);
|
|
64
|
-
const [isOpen, setIsOpen] = React.useState(false);
|
|
65
|
-
|
|
66
|
-
const canAddMore = !maxItems || fields.length < maxItems;
|
|
67
|
-
const canRemove = !readOnly && (!minItems || fields.length > minItems);
|
|
68
|
-
const fallbackLabel = label || collection || "Item";
|
|
69
|
-
|
|
70
|
-
const resolveRowLabel = React.useCallback(
|
|
71
|
-
(item: any, index: number) => {
|
|
72
|
-
if (typeof rowLabel === "function") {
|
|
73
|
-
const labelValue = rowLabel(item);
|
|
74
|
-
if (labelValue) return labelValue;
|
|
75
|
-
}
|
|
76
|
-
if (typeof rowLabel === "string") {
|
|
77
|
-
const labelValue = item?.[rowLabel];
|
|
78
|
-
if (labelValue) return String(labelValue);
|
|
79
|
-
}
|
|
80
|
-
if (item?.title) return String(item.title);
|
|
81
|
-
if (item?.name) return String(item.name);
|
|
82
|
-
return `${fallbackLabel} ${index + 1}`;
|
|
83
|
-
},
|
|
84
|
-
[fallbackLabel, rowLabel],
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const handleAdd = () => {
|
|
88
|
-
if (disabled || readOnly || !canAddMore) return;
|
|
89
|
-
append({});
|
|
90
|
-
if (mode !== "inline") {
|
|
91
|
-
const nextIndex = fields.length;
|
|
92
|
-
setActiveIndex(nextIndex);
|
|
93
|
-
setIsOpen(true);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const handleRemove = (index: number) => {
|
|
98
|
-
if (!canRemove) return;
|
|
99
|
-
remove(index);
|
|
100
|
-
if (activeIndex === null) return;
|
|
101
|
-
if (index === activeIndex) {
|
|
102
|
-
setIsOpen(false);
|
|
103
|
-
setActiveIndex(null);
|
|
104
|
-
} else if (index < activeIndex) {
|
|
105
|
-
setActiveIndex(activeIndex - 1);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const handleMove = (from: number, to: number) => {
|
|
110
|
-
if (to < 0 || to >= fields.length) return;
|
|
111
|
-
move(from, to);
|
|
112
|
-
if (activeIndex === null) return;
|
|
113
|
-
if (activeIndex === from) {
|
|
114
|
-
setActiveIndex(to);
|
|
115
|
-
} else if (activeIndex === to) {
|
|
116
|
-
setActiveIndex(from);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const handleOpenChange = (open: boolean) => {
|
|
121
|
-
setIsOpen(open);
|
|
122
|
-
if (!open) {
|
|
123
|
-
setActiveIndex(null);
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const renderItemFields = (index: number) => {
|
|
128
|
-
if (!renderFields) {
|
|
129
|
-
return (
|
|
130
|
-
<div className="rounded-lg border border-dashed p-4 text-center">
|
|
131
|
-
<p className="text-sm text-muted-foreground">
|
|
132
|
-
Form fields not configured.
|
|
133
|
-
</p>
|
|
134
|
-
</div>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
return renderFields(index);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const emptyState = (
|
|
141
|
-
<div className="rounded-lg border border-dashed p-4 text-center">
|
|
142
|
-
<p className="text-sm text-muted-foreground">
|
|
143
|
-
{placeholder || `No ${fallbackLabel} added yet`}
|
|
144
|
-
</p>
|
|
145
|
-
</div>
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const editorContent =
|
|
149
|
-
activeIndex !== null ? renderItemFields(activeIndex) : null;
|
|
150
|
-
const editorTitle =
|
|
151
|
-
activeIndex !== null
|
|
152
|
-
? resolveRowLabel(values?.[activeIndex], activeIndex)
|
|
153
|
-
: fallbackLabel;
|
|
154
|
-
|
|
155
|
-
const showEditor = mode === "modal" || mode === "drawer";
|
|
156
|
-
|
|
157
|
-
return (
|
|
158
|
-
<div className="space-y-2">
|
|
159
|
-
{label && (
|
|
160
|
-
<div className="flex items-center gap-2">
|
|
161
|
-
<label htmlFor={name} className="text-sm font-medium">
|
|
162
|
-
{label}
|
|
163
|
-
{required && <span className="text-destructive">*</span>}
|
|
164
|
-
{maxItems && (
|
|
165
|
-
<span className="ml-2 text-xs text-muted-foreground">
|
|
166
|
-
({fields.length}/{maxItems})
|
|
167
|
-
</span>
|
|
168
|
-
)}
|
|
169
|
-
</label>
|
|
170
|
-
{localized && <LocaleBadge locale={locale || "i18n"} />}
|
|
171
|
-
</div>
|
|
172
|
-
)}
|
|
173
|
-
{description && (
|
|
174
|
-
<p className="text-sm text-muted-foreground">{description}</p>
|
|
175
|
-
)}
|
|
176
|
-
|
|
177
|
-
<div className="space-y-3">
|
|
178
|
-
{fields.length === 0
|
|
179
|
-
? emptyState
|
|
180
|
-
: fields.map((field, index) => {
|
|
181
|
-
const itemValue = values?.[index];
|
|
182
|
-
const itemLabel = resolveRowLabel(itemValue, index);
|
|
183
|
-
const canMoveUp = orderable && index > 0;
|
|
184
|
-
const canMoveDown = orderable && index < fields.length - 1;
|
|
185
|
-
|
|
186
|
-
return (
|
|
187
|
-
<div key={field.id} className="rounded-lg border bg-card">
|
|
188
|
-
<div className="flex items-center justify-between border-b px-3 py-2">
|
|
189
|
-
<div className="flex items-center gap-2">
|
|
190
|
-
<span className="text-xs text-muted-foreground">
|
|
191
|
-
#{index + 1}
|
|
192
|
-
</span>
|
|
193
|
-
<span className="text-sm font-medium">{itemLabel}</span>
|
|
194
|
-
</div>
|
|
195
|
-
<div className="flex items-center gap-1">
|
|
196
|
-
{orderable && !readOnly && (
|
|
197
|
-
<>
|
|
198
|
-
<Button
|
|
199
|
-
type="button"
|
|
200
|
-
variant="ghost"
|
|
201
|
-
size="icon"
|
|
202
|
-
className="h-6 w-6"
|
|
203
|
-
onClick={() => handleMove(index, index - 1)}
|
|
204
|
-
disabled={!canMoveUp || disabled}
|
|
205
|
-
title="Move up"
|
|
206
|
-
>
|
|
207
|
-
<CaretUp className="h-3 w-3" />
|
|
208
|
-
</Button>
|
|
209
|
-
<Button
|
|
210
|
-
type="button"
|
|
211
|
-
variant="ghost"
|
|
212
|
-
size="icon"
|
|
213
|
-
className="h-6 w-6"
|
|
214
|
-
onClick={() => handleMove(index, index + 1)}
|
|
215
|
-
disabled={!canMoveDown || disabled}
|
|
216
|
-
title="Move down"
|
|
217
|
-
>
|
|
218
|
-
<CaretDown className="h-3 w-3" />
|
|
219
|
-
</Button>
|
|
220
|
-
</>
|
|
221
|
-
)}
|
|
222
|
-
{mode !== "inline" && (
|
|
223
|
-
<Button
|
|
224
|
-
type="button"
|
|
225
|
-
variant="ghost"
|
|
226
|
-
size="icon"
|
|
227
|
-
className="h-6 w-6"
|
|
228
|
-
onClick={() => {
|
|
229
|
-
setActiveIndex(index);
|
|
230
|
-
setIsOpen(true);
|
|
231
|
-
}}
|
|
232
|
-
disabled={disabled || !renderFields}
|
|
233
|
-
title={readOnly ? "View" : "Edit"}
|
|
234
|
-
>
|
|
235
|
-
<Pencil className="h-3 w-3" />
|
|
236
|
-
</Button>
|
|
237
|
-
)}
|
|
238
|
-
{!readOnly && canRemove && (
|
|
239
|
-
<Button
|
|
240
|
-
type="button"
|
|
241
|
-
variant="ghost"
|
|
242
|
-
size="icon"
|
|
243
|
-
className="h-6 w-6"
|
|
244
|
-
onClick={() => handleRemove(index)}
|
|
245
|
-
disabled={disabled}
|
|
246
|
-
title="Remove"
|
|
247
|
-
>
|
|
248
|
-
<Trash className="h-3 w-3" />
|
|
249
|
-
</Button>
|
|
250
|
-
)}
|
|
251
|
-
</div>
|
|
252
|
-
</div>
|
|
253
|
-
{mode === "inline" && (
|
|
254
|
-
<div className="space-y-3 p-3">
|
|
255
|
-
{renderItemFields(index)}
|
|
256
|
-
</div>
|
|
257
|
-
)}
|
|
258
|
-
</div>
|
|
259
|
-
);
|
|
260
|
-
})}
|
|
261
|
-
</div>
|
|
262
|
-
|
|
263
|
-
{!readOnly && canAddMore && (
|
|
264
|
-
<Button
|
|
265
|
-
type="button"
|
|
266
|
-
variant="outline"
|
|
267
|
-
onClick={handleAdd}
|
|
268
|
-
disabled={disabled}
|
|
269
|
-
>
|
|
270
|
-
<Plus className="h-4 w-4" />
|
|
271
|
-
Add {fallbackLabel}
|
|
272
|
-
</Button>
|
|
273
|
-
)}
|
|
274
|
-
|
|
275
|
-
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
276
|
-
|
|
277
|
-
{showEditor && mode === "modal" && (
|
|
278
|
-
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
|
|
279
|
-
<DialogContent className="sm:max-w-2xl">
|
|
280
|
-
<DialogHeader>
|
|
281
|
-
<DialogTitle>{editorTitle}</DialogTitle>
|
|
282
|
-
{description && (
|
|
283
|
-
<DialogDescription>{description}</DialogDescription>
|
|
284
|
-
)}
|
|
285
|
-
</DialogHeader>
|
|
286
|
-
<div className="space-y-4">{editorContent}</div>
|
|
287
|
-
</DialogContent>
|
|
288
|
-
</Dialog>
|
|
289
|
-
)}
|
|
290
|
-
|
|
291
|
-
{showEditor && mode === "drawer" && (
|
|
292
|
-
<Sheet open={isOpen} onOpenChange={handleOpenChange}>
|
|
293
|
-
<SheetContent side="right" className="sm:max-w-lg">
|
|
294
|
-
<SheetHeader>
|
|
295
|
-
<SheetTitle>{editorTitle}</SheetTitle>
|
|
296
|
-
{description && (
|
|
297
|
-
<SheetDescription>{description}</SheetDescription>
|
|
298
|
-
)}
|
|
299
|
-
</SheetHeader>
|
|
300
|
-
<div className="px-4 pb-4">{editorContent}</div>
|
|
301
|
-
<div className="px-4 pb-4">
|
|
302
|
-
<Button
|
|
303
|
-
type="button"
|
|
304
|
-
variant="outline"
|
|
305
|
-
onClick={() => handleOpenChange(false)}
|
|
306
|
-
>
|
|
307
|
-
Close
|
|
308
|
-
</Button>
|
|
309
|
-
</div>
|
|
310
|
-
</SheetContent>
|
|
311
|
-
</Sheet>
|
|
312
|
-
)}
|
|
313
|
-
</div>
|
|
314
|
-
);
|
|
315
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import type * as React from "react";
|
|
2
|
-
import type { Control } from "react-hook-form";
|
|
3
|
-
import type {
|
|
4
|
-
SelectOption as PrimitiveSelectOption,
|
|
5
|
-
SelectOptionGroup,
|
|
6
|
-
} from "../primitives/types";
|
|
7
|
-
|
|
8
|
-
// Re-export SelectOption for backwards compatibility
|
|
9
|
-
export type SelectOption<TValue = string> = PrimitiveSelectOption<TValue>;
|
|
10
|
-
export type { SelectOptionGroup };
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Base props shared by all field components.
|
|
14
|
-
* These integrate with react-hook-form via Controller.
|
|
15
|
-
*/
|
|
16
|
-
export type BaseFieldProps = {
|
|
17
|
-
/** Field name (path in form values) */
|
|
18
|
-
name: string;
|
|
19
|
-
/** Field label */
|
|
20
|
-
label?: string;
|
|
21
|
-
/** Helper text shown below the field */
|
|
22
|
-
description?: string;
|
|
23
|
-
/** Placeholder text */
|
|
24
|
-
placeholder?: string;
|
|
25
|
-
/** Mark field as required (visual indicator) */
|
|
26
|
-
required?: boolean;
|
|
27
|
-
/** Disable the field */
|
|
28
|
-
disabled?: boolean;
|
|
29
|
-
/** Show locale badge for localized fields */
|
|
30
|
-
localized?: boolean;
|
|
31
|
-
/** Current locale code for localized fields */
|
|
32
|
-
locale?: string;
|
|
33
|
-
/** Form control from react-hook-form (optional if using FormProvider) */
|
|
34
|
-
control?: Control<any>;
|
|
35
|
-
/** Additional className for the field wrapper */
|
|
36
|
-
className?: string;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Field types supported by the FormField component
|
|
41
|
-
*/
|
|
42
|
-
export type FormFieldType =
|
|
43
|
-
| "text"
|
|
44
|
-
| "email"
|
|
45
|
-
| "password"
|
|
46
|
-
| "url"
|
|
47
|
-
| "tel"
|
|
48
|
-
| "search"
|
|
49
|
-
| "number"
|
|
50
|
-
| "textarea"
|
|
51
|
-
| "checkbox"
|
|
52
|
-
| "switch"
|
|
53
|
-
| "select"
|
|
54
|
-
| "multiselect"
|
|
55
|
-
| "date"
|
|
56
|
-
| "datetime"
|
|
57
|
-
| "daterange"
|
|
58
|
-
| "tags";
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Props for the generic FormField component
|
|
62
|
-
*/
|
|
63
|
-
export type FormFieldProps = BaseFieldProps & {
|
|
64
|
-
type?: FormFieldType;
|
|
65
|
-
options?: SelectOption[];
|
|
66
|
-
component?: React.ComponentType<any>;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Props for text-based fields
|
|
71
|
-
*/
|
|
72
|
-
export type TextFieldProps = BaseFieldProps & {
|
|
73
|
-
type?: "text" | "email" | "password" | "url" | "tel" | "search";
|
|
74
|
-
maxLength?: number;
|
|
75
|
-
autoComplete?: string;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Props for number field
|
|
80
|
-
*/
|
|
81
|
-
export type NumberFieldProps = BaseFieldProps & {
|
|
82
|
-
min?: number;
|
|
83
|
-
max?: number;
|
|
84
|
-
step?: number;
|
|
85
|
-
showButtons?: boolean;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Props for textarea field
|
|
90
|
-
*/
|
|
91
|
-
export type TextareaFieldProps = BaseFieldProps & {
|
|
92
|
-
rows?: number;
|
|
93
|
-
maxLength?: number;
|
|
94
|
-
autoResize?: boolean;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Props for select field
|
|
99
|
-
*/
|
|
100
|
-
export type SelectFieldProps<TValue = string> = BaseFieldProps & {
|
|
101
|
-
options?: SelectOption<TValue>[];
|
|
102
|
-
loadOptions?: (search: string) => Promise<SelectOption<TValue>[]>;
|
|
103
|
-
multiple?: boolean;
|
|
104
|
-
clearable?: boolean;
|
|
105
|
-
maxSelections?: number;
|
|
106
|
-
emptyMessage?: string;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Props for date field
|
|
111
|
-
*/
|
|
112
|
-
export type DateFieldProps = BaseFieldProps & {
|
|
113
|
-
minDate?: Date;
|
|
114
|
-
maxDate?: Date;
|
|
115
|
-
format?: string;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Props for datetime field
|
|
120
|
-
*/
|
|
121
|
-
export type DateTimeFieldProps = DateFieldProps & {
|
|
122
|
-
precision?: "minute" | "second";
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Props for date range field
|
|
127
|
-
*/
|
|
128
|
-
export type DateRangeFieldProps = BaseFieldProps & {
|
|
129
|
-
minDate?: Date;
|
|
130
|
-
maxDate?: Date;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Props for tags field
|
|
135
|
-
*/
|
|
136
|
-
export type TagsFieldProps = BaseFieldProps & {
|
|
137
|
-
suggestions?: string[];
|
|
138
|
-
maxTags?: number;
|
|
139
|
-
allowDuplicates?: boolean;
|
|
140
|
-
pattern?: RegExp;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Props for checkbox/switch fields
|
|
145
|
-
*/
|
|
146
|
-
export type BooleanFieldProps = BaseFieldProps;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Props for checkbox group field
|
|
150
|
-
*/
|
|
151
|
-
export type CheckboxGroupFieldProps<TValue = string> = BaseFieldProps & {
|
|
152
|
-
options: SelectOption<TValue>[];
|
|
153
|
-
orientation?: "horizontal" | "vertical";
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Props for radio group field
|
|
158
|
-
*/
|
|
159
|
-
export type RadioGroupFieldProps<TValue = string> = BaseFieldProps & {
|
|
160
|
-
options: SelectOption<TValue>[];
|
|
161
|
-
orientation?: "horizontal" | "vertical";
|
|
162
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {
|
|
3
|
-
Field,
|
|
4
|
-
FieldContent,
|
|
5
|
-
FieldDescription,
|
|
6
|
-
FieldError,
|
|
7
|
-
FieldLabel,
|
|
8
|
-
} from "../ui/field";
|
|
9
|
-
import { LocaleBadge } from "./locale-badge";
|
|
10
|
-
|
|
11
|
-
type FieldWrapperProps = {
|
|
12
|
-
name: string;
|
|
13
|
-
label?: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
required?: boolean;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
error?: string;
|
|
18
|
-
localized?: boolean;
|
|
19
|
-
locale?: string;
|
|
20
|
-
children: React.ReactNode;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export function FieldWrapper({
|
|
24
|
-
name,
|
|
25
|
-
label,
|
|
26
|
-
description,
|
|
27
|
-
required,
|
|
28
|
-
disabled,
|
|
29
|
-
error,
|
|
30
|
-
localized,
|
|
31
|
-
locale,
|
|
32
|
-
children,
|
|
33
|
-
}: FieldWrapperProps) {
|
|
34
|
-
return (
|
|
35
|
-
<Field data-disabled={disabled} data-invalid={!!error}>
|
|
36
|
-
<div className="space-y-2">
|
|
37
|
-
{label && (
|
|
38
|
-
<FieldLabel htmlFor={name} className="flex items-center gap-2">
|
|
39
|
-
<span className="flex items-center gap-1">
|
|
40
|
-
{label}
|
|
41
|
-
{required && <span className="text-destructive">*</span>}
|
|
42
|
-
</span>
|
|
43
|
-
{localized && <LocaleBadge locale={locale || "i18n"} />}
|
|
44
|
-
</FieldLabel>
|
|
45
|
-
)}
|
|
46
|
-
<FieldContent>{children}</FieldContent>
|
|
47
|
-
{description && <FieldDescription>{description}</FieldDescription>}
|
|
48
|
-
{error && <FieldError errors={[{ message: error }]} />}
|
|
49
|
-
</div>
|
|
50
|
-
</Field>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Field components for admin forms
|
|
3
|
-
*
|
|
4
|
-
* All field components are integrated with react-hook-form via Controller.
|
|
5
|
-
* They use the primitive components from ../primitives for the actual UI.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Complex field components
|
|
9
|
-
export { RelationSelect } from "./relation-select";
|
|
10
|
-
export type { RelationSelectProps } from "./relation-select";
|
|
11
|
-
|
|
12
|
-
export { RelationPicker } from "./relation-picker";
|
|
13
|
-
export type { RelationPickerProps } from "./relation-picker";
|
|
14
|
-
|
|
15
|
-
export { RelationField } from "./relation-field";
|
|
16
|
-
export type { RelationFieldProps } from "./relation-field";
|
|
17
|
-
|
|
18
|
-
export { JsonField } from "./json-field";
|
|
19
|
-
export type { JsonFieldProps, JsonFieldMode } from "./json-field";
|
|
20
|
-
|
|
21
|
-
export { EmbeddedCollectionField } from "./embedded-collection";
|
|
22
|
-
export type { EmbeddedCollectionFieldProps } from "./embedded-collection";
|
|
23
|
-
|
|
24
|
-
export { ArrayField } from "./array-field";
|
|
25
|
-
export type { ArrayFieldProps, ArrayFieldItemType } from "./array-field";
|
|
26
|
-
|
|
27
|
-
export { RichTextEditor } from "./rich-text-editor";
|
|
28
|
-
export type { RichTextEditorProps, RichTextFeatures } from "./rich-text-editor";
|
|
29
|
-
|
|
30
|
-
// Basic field components (using primitives)
|
|
31
|
-
export { TextField } from "./text-field";
|
|
32
|
-
export { EmailField } from "./email-field";
|
|
33
|
-
export { PasswordField } from "./password-field";
|
|
34
|
-
export { TextareaField } from "./textarea-field";
|
|
35
|
-
export { NumberField } from "./number-field";
|
|
36
|
-
export { CheckboxField } from "./checkbox-field";
|
|
37
|
-
export { SwitchField } from "./switch-field";
|
|
38
|
-
export { SelectField } from "./select-field";
|
|
39
|
-
export { DateField } from "./date-field";
|
|
40
|
-
export { DatetimeField } from "./datetime-field";
|
|
41
|
-
export { CustomField } from "./custom-field";
|
|
42
|
-
|
|
43
|
-
// Utilities
|
|
44
|
-
export { LocaleBadge } from "./locale-badge";
|
|
45
|
-
export { FieldWrapper } from "./field-wrapper";
|
|
46
|
-
export { useResolvedControl } from "./field-utils";
|
|
47
|
-
|
|
48
|
-
// Types
|
|
49
|
-
export type {
|
|
50
|
-
BaseFieldProps,
|
|
51
|
-
FormFieldProps,
|
|
52
|
-
FormFieldType,
|
|
53
|
-
SelectOption,
|
|
54
|
-
SelectOptionGroup,
|
|
55
|
-
TextFieldProps,
|
|
56
|
-
NumberFieldProps,
|
|
57
|
-
TextareaFieldProps,
|
|
58
|
-
SelectFieldProps,
|
|
59
|
-
DateFieldProps,
|
|
60
|
-
DateTimeFieldProps,
|
|
61
|
-
DateRangeFieldProps,
|
|
62
|
-
TagsFieldProps,
|
|
63
|
-
BooleanFieldProps,
|
|
64
|
-
CheckboxGroupFieldProps,
|
|
65
|
-
RadioGroupFieldProps,
|
|
66
|
-
} from "./field-types";
|