@opensaas/stack-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/.turbo/turbo-build.log +8 -0
  2. package/README.md +286 -0
  3. package/dist/components/AdminUI.d.ts +24 -0
  4. package/dist/components/AdminUI.d.ts.map +1 -0
  5. package/dist/components/AdminUI.js +48 -0
  6. package/dist/components/ConfirmDialog.d.ts +16 -0
  7. package/dist/components/ConfirmDialog.d.ts.map +1 -0
  8. package/dist/components/ConfirmDialog.js +11 -0
  9. package/dist/components/Dashboard.d.ts +12 -0
  10. package/dist/components/Dashboard.d.ts.map +1 -0
  11. package/dist/components/Dashboard.js +30 -0
  12. package/dist/components/ItemForm.d.ts +17 -0
  13. package/dist/components/ItemForm.d.ts.map +1 -0
  14. package/dist/components/ItemForm.js +97 -0
  15. package/dist/components/ItemFormClient.d.ts +22 -0
  16. package/dist/components/ItemFormClient.d.ts.map +1 -0
  17. package/dist/components/ItemFormClient.js +127 -0
  18. package/dist/components/ListView.d.ts +17 -0
  19. package/dist/components/ListView.d.ts.map +1 -0
  20. package/dist/components/ListView.js +76 -0
  21. package/dist/components/ListViewClient.d.ts +19 -0
  22. package/dist/components/ListViewClient.d.ts.map +1 -0
  23. package/dist/components/ListViewClient.js +108 -0
  24. package/dist/components/LoadingSpinner.d.ts +10 -0
  25. package/dist/components/LoadingSpinner.d.ts.map +1 -0
  26. package/dist/components/LoadingSpinner.js +14 -0
  27. package/dist/components/Navigation.d.ts +13 -0
  28. package/dist/components/Navigation.d.ts.map +1 -0
  29. package/dist/components/Navigation.js +20 -0
  30. package/dist/components/SkeletonLoader.d.ts +22 -0
  31. package/dist/components/SkeletonLoader.d.ts.map +1 -0
  32. package/dist/components/SkeletonLoader.js +25 -0
  33. package/dist/components/fields/CheckboxField.d.ts +11 -0
  34. package/dist/components/fields/CheckboxField.d.ts.map +1 -0
  35. package/dist/components/fields/CheckboxField.js +10 -0
  36. package/dist/components/fields/ComboboxField.d.ts +18 -0
  37. package/dist/components/fields/ComboboxField.d.ts.map +1 -0
  38. package/dist/components/fields/ComboboxField.js +32 -0
  39. package/dist/components/fields/FieldRenderer.d.ts +22 -0
  40. package/dist/components/fields/FieldRenderer.d.ts.map +1 -0
  41. package/dist/components/fields/FieldRenderer.js +81 -0
  42. package/dist/components/fields/IntegerField.d.ts +15 -0
  43. package/dist/components/fields/IntegerField.d.ts.map +1 -0
  44. package/dist/components/fields/IntegerField.js +14 -0
  45. package/dist/components/fields/PasswordField.d.ts +18 -0
  46. package/dist/components/fields/PasswordField.d.ts.map +1 -0
  47. package/dist/components/fields/PasswordField.js +42 -0
  48. package/dist/components/fields/RelationshipField.d.ts +20 -0
  49. package/dist/components/fields/RelationshipField.d.ts.map +1 -0
  50. package/dist/components/fields/RelationshipField.js +11 -0
  51. package/dist/components/fields/RelationshipManager.d.ts +19 -0
  52. package/dist/components/fields/RelationshipManager.d.ts.map +1 -0
  53. package/dist/components/fields/RelationshipManager.js +37 -0
  54. package/dist/components/fields/SelectField.d.ts +16 -0
  55. package/dist/components/fields/SelectField.d.ts.map +1 -0
  56. package/dist/components/fields/SelectField.js +11 -0
  57. package/dist/components/fields/TextField.d.ts +13 -0
  58. package/dist/components/fields/TextField.d.ts.map +1 -0
  59. package/dist/components/fields/TextField.js +11 -0
  60. package/dist/components/fields/TimestampField.d.ts +12 -0
  61. package/dist/components/fields/TimestampField.d.ts.map +1 -0
  62. package/dist/components/fields/TimestampField.js +12 -0
  63. package/dist/components/fields/index.d.ts +23 -0
  64. package/dist/components/fields/index.d.ts.map +1 -0
  65. package/dist/components/fields/index.js +13 -0
  66. package/dist/components/fields/registry.d.ts +43 -0
  67. package/dist/components/fields/registry.d.ts.map +1 -0
  68. package/dist/components/fields/registry.js +42 -0
  69. package/dist/components/standalone/DeleteButton.d.ts +35 -0
  70. package/dist/components/standalone/DeleteButton.d.ts.map +1 -0
  71. package/dist/components/standalone/DeleteButton.js +46 -0
  72. package/dist/components/standalone/ItemCreateForm.d.ts +34 -0
  73. package/dist/components/standalone/ItemCreateForm.d.ts.map +1 -0
  74. package/dist/components/standalone/ItemCreateForm.js +91 -0
  75. package/dist/components/standalone/ItemEditForm.d.ts +37 -0
  76. package/dist/components/standalone/ItemEditForm.d.ts.map +1 -0
  77. package/dist/components/standalone/ItemEditForm.js +112 -0
  78. package/dist/components/standalone/ListTable.d.ts +33 -0
  79. package/dist/components/standalone/ListTable.d.ts.map +1 -0
  80. package/dist/components/standalone/ListTable.js +94 -0
  81. package/dist/components/standalone/SearchBar.d.ts +29 -0
  82. package/dist/components/standalone/SearchBar.d.ts.map +1 -0
  83. package/dist/components/standalone/SearchBar.js +43 -0
  84. package/dist/components/standalone/index.d.ts +11 -0
  85. package/dist/components/standalone/index.d.ts.map +1 -0
  86. package/dist/components/standalone/index.js +6 -0
  87. package/dist/index.d.ts +27 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +19 -0
  90. package/dist/lib/serializeFieldConfig.d.ts +43 -0
  91. package/dist/lib/serializeFieldConfig.d.ts.map +1 -0
  92. package/dist/lib/serializeFieldConfig.js +48 -0
  93. package/dist/lib/theme.d.ts +17 -0
  94. package/dist/lib/theme.d.ts.map +1 -0
  95. package/dist/lib/theme.js +192 -0
  96. package/dist/lib/utils.d.ts +18 -0
  97. package/dist/lib/utils.d.ts.map +1 -0
  98. package/dist/lib/utils.js +76 -0
  99. package/dist/primitives/button.d.ts +12 -0
  100. package/dist/primitives/button.d.ts.map +1 -0
  101. package/dist/primitives/button.js +33 -0
  102. package/dist/primitives/calendar.d.ts +9 -0
  103. package/dist/primitives/calendar.d.ts.map +1 -0
  104. package/dist/primitives/calendar.js +48 -0
  105. package/dist/primitives/card.d.ts +9 -0
  106. package/dist/primitives/card.d.ts.map +1 -0
  107. package/dist/primitives/card.js +16 -0
  108. package/dist/primitives/checkbox.d.ts +5 -0
  109. package/dist/primitives/checkbox.d.ts.map +1 -0
  110. package/dist/primitives/checkbox.js +7 -0
  111. package/dist/primitives/combobox.d.ts +14 -0
  112. package/dist/primitives/combobox.d.ts.map +1 -0
  113. package/dist/primitives/combobox.js +20 -0
  114. package/dist/primitives/datetime-picker.d.ts +9 -0
  115. package/dist/primitives/datetime-picker.d.ts.map +1 -0
  116. package/dist/primitives/datetime-picker.js +42 -0
  117. package/dist/primitives/dialog.d.ts +20 -0
  118. package/dist/primitives/dialog.d.ts.map +1 -0
  119. package/dist/primitives/dialog.js +21 -0
  120. package/dist/primitives/index.d.ts +14 -0
  121. package/dist/primitives/index.d.ts.map +1 -0
  122. package/dist/primitives/index.js +14 -0
  123. package/dist/primitives/input.d.ts +5 -0
  124. package/dist/primitives/input.d.ts.map +1 -0
  125. package/dist/primitives/input.js +8 -0
  126. package/dist/primitives/label.d.ts +6 -0
  127. package/dist/primitives/label.d.ts.map +1 -0
  128. package/dist/primitives/label.js +9 -0
  129. package/dist/primitives/popover.d.ts +7 -0
  130. package/dist/primitives/popover.d.ts.map +1 -0
  131. package/dist/primitives/popover.js +10 -0
  132. package/dist/primitives/select.d.ts +14 -0
  133. package/dist/primitives/select.d.ts.map +1 -0
  134. package/dist/primitives/select.js +24 -0
  135. package/dist/primitives/table.d.ts +11 -0
  136. package/dist/primitives/table.d.ts.map +1 -0
  137. package/dist/primitives/table.js +20 -0
  138. package/dist/primitives/time-picker.d.ts +8 -0
  139. package/dist/primitives/time-picker.d.ts.map +1 -0
  140. package/dist/primitives/time-picker.js +27 -0
  141. package/dist/server/index.d.ts +2 -0
  142. package/dist/server/index.d.ts.map +1 -0
  143. package/dist/server/index.js +2 -0
  144. package/dist/server/types.d.ts +15 -0
  145. package/dist/server/types.d.ts.map +1 -0
  146. package/dist/server/types.js +1 -0
  147. package/dist/styles/globals.css +1896 -0
  148. package/package.json +91 -0
  149. package/postcss.config.cjs +5 -0
  150. package/src/components/AdminUI.tsx +112 -0
  151. package/src/components/ConfirmDialog.tsx +56 -0
  152. package/src/components/Dashboard.tsx +134 -0
  153. package/src/components/ItemForm.tsx +195 -0
  154. package/src/components/ItemFormClient.tsx +237 -0
  155. package/src/components/ListView.tsx +153 -0
  156. package/src/components/ListViewClient.tsx +282 -0
  157. package/src/components/LoadingSpinner.tsx +32 -0
  158. package/src/components/Navigation.tsx +117 -0
  159. package/src/components/SkeletonLoader.tsx +82 -0
  160. package/src/components/fields/CheckboxField.tsx +54 -0
  161. package/src/components/fields/ComboboxField.tsx +127 -0
  162. package/src/components/fields/FieldRenderer.tsx +132 -0
  163. package/src/components/fields/IntegerField.tsx +68 -0
  164. package/src/components/fields/PasswordField.tsx +159 -0
  165. package/src/components/fields/RelationshipField.tsx +71 -0
  166. package/src/components/fields/RelationshipManager.tsx +189 -0
  167. package/src/components/fields/SelectField.tsx +71 -0
  168. package/src/components/fields/TextField.tsx +59 -0
  169. package/src/components/fields/TimestampField.tsx +49 -0
  170. package/src/components/fields/index.ts +27 -0
  171. package/src/components/fields/registry.ts +72 -0
  172. package/src/components/standalone/DeleteButton.tsx +114 -0
  173. package/src/components/standalone/ItemCreateForm.tsx +161 -0
  174. package/src/components/standalone/ItemEditForm.tsx +193 -0
  175. package/src/components/standalone/ListTable.tsx +211 -0
  176. package/src/components/standalone/SearchBar.tsx +86 -0
  177. package/src/components/standalone/index.ts +13 -0
  178. package/src/index.ts +74 -0
  179. package/src/lib/serializeFieldConfig.ts +88 -0
  180. package/src/lib/theme.ts +202 -0
  181. package/src/lib/utils.ts +81 -0
  182. package/src/primitives/button.tsx +49 -0
  183. package/src/primitives/calendar.tsx +160 -0
  184. package/src/primitives/card.tsx +58 -0
  185. package/src/primitives/checkbox.tsx +27 -0
  186. package/src/primitives/combobox.tsx +159 -0
  187. package/src/primitives/datetime-picker.tsx +130 -0
  188. package/src/primitives/dialog.tsx +108 -0
  189. package/src/primitives/index.ts +54 -0
  190. package/src/primitives/input.tsx +24 -0
  191. package/src/primitives/label.tsx +19 -0
  192. package/src/primitives/popover.tsx +31 -0
  193. package/src/primitives/select.tsx +158 -0
  194. package/src/primitives/table.tsx +91 -0
  195. package/src/primitives/time-picker.tsx +65 -0
  196. package/src/server/index.ts +3 -0
  197. package/src/server/types.ts +15 -0
  198. package/src/styles/globals.css +123 -0
  199. package/tailwind.config.ts +3 -0
  200. package/tests/components/TextField.test.tsx +94 -0
  201. package/tests/setup.ts +11 -0
  202. package/tsconfig.json +26 -0
  203. package/vitest.config.ts +22 -0
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { getFieldComponent } from './registry.js';
4
+ import { formatFieldName } from '../../lib/utils.js';
5
+ import { getUrlKey } from '@opensaas/stack-core';
6
+ /**
7
+ * Internal component that receives the resolved Component
8
+ */
9
+ function FieldRendererInner({ Component, fieldName, fieldConfig, value, onChange, error, disabled, mode, relationshipItems, relationshipLoading, basePath,
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ }) {
12
+ const label = fieldConfig.label || formatFieldName(fieldName);
13
+ const isRequired = fieldConfig.validation &&
14
+ typeof fieldConfig.validation === 'object' &&
15
+ fieldConfig.validation !== null &&
16
+ 'isRequired' in fieldConfig.validation
17
+ ? fieldConfig.validation.isRequired
18
+ : false;
19
+ // Build props based on field type
20
+ const baseProps = {
21
+ name: fieldName,
22
+ value,
23
+ onChange,
24
+ label,
25
+ error,
26
+ disabled,
27
+ required: isRequired,
28
+ mode,
29
+ };
30
+ // Add field-type-specific props
31
+ const specificProps = {};
32
+ if (fieldConfig.type === 'select' && 'options' in fieldConfig && fieldConfig.options) {
33
+ specificProps.options = fieldConfig.options.map((opt) => typeof opt === 'string' ? { label: opt, value: opt } : opt);
34
+ }
35
+ if (fieldConfig.type === 'password') {
36
+ specificProps.showConfirm = mode === 'edit';
37
+ }
38
+ if (fieldConfig.type === 'relationship') {
39
+ specificProps.items = relationshipItems;
40
+ specificProps.isLoading = relationshipLoading;
41
+ specificProps.many = fieldConfig.many || false;
42
+ // Extract related list key from ref (format: 'ListName.fieldName')
43
+ if (fieldConfig.ref) {
44
+ const [relatedListName] = fieldConfig.ref.split('.');
45
+ specificProps.relatedListKey = getUrlKey(relatedListName);
46
+ specificProps.basePath = basePath;
47
+ }
48
+ }
49
+ // Pass through any UI options from fieldConfig.ui (excluding component and fieldType)
50
+ if (fieldConfig.ui) {
51
+ const { _component, _fieldType, ...uiOptions } = fieldConfig.ui;
52
+ Object.assign(specificProps, uiOptions);
53
+ }
54
+ const allProps = { ...baseProps, ...specificProps };
55
+ return _jsx(Component, { ...allProps });
56
+ }
57
+ /**
58
+ * Factory component that renders the appropriate field type
59
+ * based on the field configuration and component registry
60
+ */
61
+ export function FieldRenderer(props) {
62
+ const { fieldName, fieldConfig, mode = 'edit' } = props;
63
+ const label = fieldConfig.label || formatFieldName(fieldName);
64
+ // Skip rendering ID and timestamp fields in forms
65
+ if (mode === 'edit' && ['id', 'createdAt', 'updatedAt'].includes(fieldName)) {
66
+ return null;
67
+ }
68
+ // Get component from:
69
+ // 1. Per-field component override (ui.component)
70
+ // 2. Custom field type override (ui.fieldType) - uses global registry
71
+ // 3. Default field type (fieldConfig.type) - uses global registry
72
+ const Component = fieldConfig.ui?.component ||
73
+ (fieldConfig.ui?.fieldType
74
+ ? getFieldComponent(fieldConfig.ui.fieldType)
75
+ : getFieldComponent(fieldConfig.type));
76
+ if (!Component) {
77
+ console.warn(`No component registered for field type: ${fieldConfig.type}`);
78
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium text-muted-foreground", children: String(label) }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Unsupported field type: ", fieldConfig.type] })] }));
79
+ }
80
+ return _jsx(FieldRendererInner, { ...props, Component: Component });
81
+ }
@@ -0,0 +1,15 @@
1
+ export interface IntegerFieldProps {
2
+ name: string;
3
+ value: number | null;
4
+ onChange: (value: number | null) => void;
5
+ label: string;
6
+ placeholder?: string;
7
+ error?: string;
8
+ disabled?: boolean;
9
+ required?: boolean;
10
+ mode?: 'read' | 'edit';
11
+ min?: number;
12
+ max?: number;
13
+ }
14
+ export declare function IntegerField({ name, value, onChange, label, placeholder, error, disabled, required, mode, min, max, }: IntegerFieldProps): import("react/jsx-runtime").JSX.Element;
15
+ //# sourceMappingURL=IntegerField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntegerField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/IntegerField.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAa,EACb,GAAG,EACH,GAAG,GACJ,EAAE,iBAAiB,2CAmCnB"}
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Input } from '../../primitives/input.js';
4
+ import { Label } from '../../primitives/label.js';
5
+ import { cn } from '../../lib/utils.js';
6
+ export function IntegerField({ name, value, onChange, label, placeholder, error, disabled, required, mode = 'edit', min, max, }) {
7
+ if (mode === 'read') {
8
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-muted-foreground", children: label }), _jsx("p", { className: "text-sm", children: value !== null ? value : '-' })] }));
9
+ }
10
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: name, children: [label, required && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), _jsx(Input, { id: name, name: name, type: "number", value: value ?? '', onChange: (e) => {
11
+ const val = e.target.value;
12
+ onChange(val === '' ? null : parseInt(val, 10));
13
+ }, placeholder: placeholder, disabled: disabled, required: required, min: min, max: max, className: cn(error && 'border-destructive') }), error && _jsx("p", { className: "text-sm text-destructive", children: error })] }));
14
+ }
@@ -0,0 +1,18 @@
1
+ export interface PasswordFieldProps {
2
+ name: string;
3
+ value: string | {
4
+ isSet: boolean;
5
+ };
6
+ onChange: (value: string | {
7
+ isSet: boolean;
8
+ } | undefined) => void;
9
+ label: string;
10
+ placeholder?: string;
11
+ error?: string;
12
+ disabled?: boolean;
13
+ required?: boolean;
14
+ mode?: 'read' | 'edit';
15
+ showConfirm?: boolean;
16
+ }
17
+ export declare function PasswordField({ name, value, onChange, label, placeholder, error, disabled, required, mode, showConfirm, }: PasswordFieldProps): import("react/jsx-runtime").JSX.Element;
18
+ //# sourceMappingURL=PasswordField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PasswordField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/PasswordField.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,KAAK,IAAI,CAAA;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAa,EACb,WAAkB,GACnB,EAAE,kBAAkB,2CA8HpB"}
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Input } from '../../primitives/input.js';
4
+ import { Label } from '../../primitives/label.js';
5
+ import { Button } from '../../primitives/button.js';
6
+ import { cn } from '../../lib/utils.js';
7
+ import { useState } from 'react';
8
+ export function PasswordField({ name, value, onChange, label, placeholder, error, disabled, required, mode = 'edit', showConfirm = true, }) {
9
+ // Check if value is the isSet object
10
+ const isSetObject = typeof value === 'object' && value !== null && 'isSet' in value;
11
+ const isPasswordSet = isSetObject ? value.isSet : false;
12
+ const [isChangingPassword, setIsChangingPassword] = useState(false);
13
+ const [passwordValue, setPasswordValue] = useState('');
14
+ const [confirmValue, setConfirmValue] = useState('');
15
+ const [showPassword, setShowPassword] = useState(false);
16
+ if (mode === 'read') {
17
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-muted-foreground", children: label }), _jsx("p", { className: "text-sm", children: isPasswordSet ? '••••••••' : 'Not set' })] }));
18
+ }
19
+ // If not changing password and it's set, show the button
20
+ if (!isChangingPassword && isSetObject) {
21
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: label }), _jsx("div", { children: _jsx(Button, { type: "button", variant: "outline", onClick: () => setIsChangingPassword(true), disabled: disabled, children: isPasswordSet ? 'Change Password' : 'Set Password' }) })] }));
22
+ }
23
+ const confirmError = showConfirm && passwordValue !== confirmValue && confirmValue !== ''
24
+ ? 'Passwords do not match'
25
+ : undefined;
26
+ const handleCancel = () => {
27
+ setIsChangingPassword(false);
28
+ setPasswordValue('');
29
+ setConfirmValue('');
30
+ setShowPassword(false);
31
+ // Reset to the isSet object
32
+ if (isSetObject) {
33
+ onChange(value);
34
+ }
35
+ };
36
+ const handlePasswordChange = (newValue) => {
37
+ setPasswordValue(newValue);
38
+ // Update the parent with the actual password string
39
+ onChange(newValue || undefined);
40
+ };
41
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: name, children: [label, required && !isPasswordSet && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), _jsxs("div", { className: "relative", children: [_jsx(Input, { id: name, name: name, type: showPassword ? 'text' : 'password', value: passwordValue, onChange: (e) => handlePasswordChange(e.target.value), placeholder: placeholder, disabled: disabled, required: required && !isPasswordSet, className: cn('pr-10', error && 'border-destructive') }), _jsx("button", { type: "button", onClick: () => setShowPassword(!showPassword), className: "absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground", children: showPassword ? '👁️' : '👁️‍🗨️' })] }), error && _jsx("p", { className: "text-sm text-destructive", children: error })] }), showConfirm && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: `${name}-confirm`, children: ["Confirm ", label, required && !isPasswordSet && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), _jsx(Input, { id: `${name}-confirm`, name: `${name}-confirm`, type: showPassword ? 'text' : 'password', value: confirmValue, onChange: (e) => setConfirmValue(e.target.value), placeholder: `Confirm ${placeholder || label.toLowerCase()}`, disabled: disabled, required: required && !isPasswordSet, className: cn(confirmError && 'border-destructive') }), confirmError && _jsx("p", { className: "text-sm text-destructive", children: confirmError })] })), isSetObject && (_jsx("div", { children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleCancel, disabled: disabled, children: "Cancel" }) }))] }));
42
+ }
@@ -0,0 +1,20 @@
1
+ export interface RelationshipFieldProps {
2
+ name: string;
3
+ value: string | string[] | null;
4
+ onChange: (value: string | string[] | null) => void;
5
+ label: string;
6
+ items: Array<{
7
+ id: string;
8
+ label: string;
9
+ }>;
10
+ error?: string;
11
+ disabled?: boolean;
12
+ required?: boolean;
13
+ mode?: 'read' | 'edit';
14
+ isLoading?: boolean;
15
+ many?: boolean;
16
+ relatedListKey?: string;
17
+ basePath?: string;
18
+ }
19
+ export declare function RelationshipField({ name, value, onChange, label, items, error, disabled, required, mode, isLoading, many, relatedListKey, basePath, }: RelationshipFieldProps): import("react/jsx-runtime").JSX.Element;
20
+ //# sourceMappingURL=RelationshipField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelationshipField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/RelationshipField.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,CAAA;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAa,EACb,SAAiB,EACjB,IAAY,EACZ,cAAc,EACd,QAAQ,GACT,EAAE,sBAAsB,2CAmCxB"}
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { ComboboxField } from './ComboboxField.js';
4
+ import { RelationshipManager } from './RelationshipManager.js';
5
+ export function RelationshipField({ name, value, onChange, label, items, error, disabled, required, mode = 'edit', isLoading = false, many = false, relatedListKey, basePath, }) {
6
+ // Delegate to specialized components based on cardinality
7
+ if (many) {
8
+ return (_jsx(RelationshipManager, { name: name, value: Array.isArray(value) ? value : [], onChange: onChange, label: label, items: items, error: error, disabled: disabled, required: required, mode: mode, isLoading: isLoading, relatedListKey: relatedListKey, basePath: basePath }));
9
+ }
10
+ return (_jsx(ComboboxField, { name: name, value: typeof value === 'string' ? value : null, onChange: onChange, label: label, items: items, error: error, disabled: disabled, required: required, mode: mode, isLoading: isLoading }));
11
+ }
@@ -0,0 +1,19 @@
1
+ export interface RelationshipManagerProps {
2
+ name: string;
3
+ value: string[];
4
+ onChange: (value: string[]) => void;
5
+ label: string;
6
+ items: Array<{
7
+ id: string;
8
+ label: string;
9
+ }>;
10
+ error?: string;
11
+ disabled?: boolean;
12
+ required?: boolean;
13
+ mode?: 'read' | 'edit';
14
+ isLoading?: boolean;
15
+ relatedListKey?: string;
16
+ basePath?: string;
17
+ }
18
+ export declare function RelationshipManager({ name: _name, value, onChange, label, items, error, disabled, required, mode, isLoading, relatedListKey, basePath, }: RelationshipManagerProps): import("react/jsx-runtime").JSX.Element;
19
+ //# sourceMappingURL=RelationshipManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelationshipManager.d.ts","sourceRoot":"","sources":["../../../src/components/fields/RelationshipManager.tsx"],"names":[],"mappings":"AAwBA,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EAAE,KAAK,EACX,KAAK,EACL,QAAQ,EACR,KAAK,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAa,EACb,SAAiB,EACjB,cAAc,EACd,QAAmB,GACpB,EAAE,wBAAwB,2CAwI1B"}
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import Link from 'next/link';
5
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '../../primitives/table.js';
6
+ import { Combobox, ComboboxTrigger, ComboboxContent, ComboboxSearch, ComboboxList, ComboboxEmpty, ComboboxItem, } from '../../primitives/combobox.js';
7
+ import { Button } from '../../primitives/button.js';
8
+ export function RelationshipManager({ name: _name, value, onChange, label, items, error, disabled, required, mode = 'edit', isLoading = false, relatedListKey, basePath = '/admin', }) {
9
+ const [showConnectModal, setShowConnectModal] = useState(false);
10
+ const [searchQuery, setSearchQuery] = useState('');
11
+ const selectedIds = Array.isArray(value) ? value : [];
12
+ const selectedItems = items.filter((item) => selectedIds.includes(item.id));
13
+ const availableItems = items.filter((item) => !selectedIds.includes(item.id));
14
+ // Read mode
15
+ if (mode === 'read') {
16
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-sm font-medium text-muted-foreground", children: label }), _jsx("p", { className: "text-sm", children: selectedItems.length > 0 ? selectedItems.map((item) => item.label).join(', ') : '-' })] }));
17
+ }
18
+ // Filter available items based on search
19
+ const filteredAvailableItems = searchQuery
20
+ ? availableItems.filter((item) => item.label.toLowerCase().includes(searchQuery.toLowerCase()))
21
+ : availableItems;
22
+ const handleRemove = (itemId) => {
23
+ onChange(selectedIds.filter((id) => id !== itemId));
24
+ };
25
+ const handleConnect = (itemId) => {
26
+ onChange([...selectedIds, itemId]);
27
+ setShowConnectModal(false);
28
+ setSearchQuery('');
29
+ };
30
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "text-sm font-medium", children: [label, required && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), selectedItems.length > 0 ? (_jsx("div", { className: "rounded-md border border-input", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Name" }), _jsx(TableHead, { className: "w-[100px] text-right", children: "Actions" })] }) }), _jsx(TableBody, { children: selectedItems.map((item) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: relatedListKey ? (_jsx(Link, { href: `${basePath}/${relatedListKey}/${item.id}`, className: "text-primary hover:underline", children: item.label })) : (item.label) }), _jsx(TableCell, { className: "text-right", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => handleRemove(item.id), disabled: disabled, children: "Remove" }) })] }, item.id))) })] }) })) : (_jsx("div", { className: "rounded-md border border-input border-dashed p-8 text-center", children: _jsx("p", { className: "text-sm text-muted-foreground", children: "No items connected. Click \"Connect Existing\" to add items." }) })), _jsx("div", { className: "flex gap-2", children: _jsxs(Combobox, { open: showConnectModal, onOpenChange: setShowConnectModal, children: [_jsx(ComboboxTrigger, { disabled: disabled || isLoading || availableItems.length === 0, className: "h-9 px-3", children: _jsx("span", { children: isLoading ? 'Loading...' : 'Connect Existing' }) }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxSearch, { placeholder: "Search...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onKeyDown: (e) => {
31
+ if (e.key === 'Enter') {
32
+ e.preventDefault();
33
+ }
34
+ } }), _jsx(ComboboxList, { children: filteredAvailableItems.length === 0 ? (_jsx(ComboboxEmpty, { children: availableItems.length === 0
35
+ ? 'All items are already connected'
36
+ : 'No results found' })) : (filteredAvailableItems.map((item) => (_jsx(ComboboxItem, { onClick: () => handleConnect(item.id), children: item.label }, item.id)))) })] })] }) }), error && _jsx("p", { className: "text-sm text-destructive mt-2", children: error })] }));
37
+ }
@@ -0,0 +1,16 @@
1
+ export interface SelectFieldProps {
2
+ name: string;
3
+ value: string | null;
4
+ onChange: (value: string | null) => void;
5
+ label: string;
6
+ options: Array<{
7
+ label: string;
8
+ value: string;
9
+ }>;
10
+ error?: string;
11
+ disabled?: boolean;
12
+ required?: boolean;
13
+ mode?: 'read' | 'edit';
14
+ }
15
+ export declare function SelectField({ name, value, onChange, label, options, error, disabled, required, mode, }: SelectFieldProps): import("react/jsx-runtime").JSX.Element;
16
+ //# sourceMappingURL=SelectField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/SelectField.tsx"],"names":[],"mappings":"AAWA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAChD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,OAAO,EACP,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAa,GACd,EAAE,gBAAgB,2CAqClB"}
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../primitives/select.js';
4
+ import { Label } from '../../primitives/label.js';
5
+ export function SelectField({ name, value, onChange, label, options, error, disabled, required, mode = 'edit', }) {
6
+ if (mode === 'read') {
7
+ const selectedOption = options.find((opt) => opt.value === value);
8
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-muted-foreground", children: label }), _jsx("p", { className: "text-sm", children: selectedOption?.label || '-' })] }));
9
+ }
10
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: name, children: [label, required && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), _jsxs(Select, { value: value || undefined, onValueChange: (val) => onChange(val || null), disabled: disabled, required: required, children: [_jsx(SelectTrigger, { id: name, className: error ? 'border-destructive' : '', children: _jsx(SelectValue, { placeholder: "Select an option..." }) }), _jsx(SelectContent, { children: options.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value))) })] }), error && _jsx("p", { className: "text-sm text-destructive", children: error })] }));
11
+ }
@@ -0,0 +1,13 @@
1
+ export interface TextFieldProps {
2
+ name: string;
3
+ value: string;
4
+ onChange: (value: string) => void;
5
+ label: string;
6
+ placeholder?: string;
7
+ error?: string;
8
+ disabled?: boolean;
9
+ required?: boolean;
10
+ mode?: 'read' | 'edit';
11
+ }
12
+ export declare function TextField({ name, value, onChange, label, placeholder, error, disabled, required, mode, }: TextFieldProps): import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=TextField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/TextField.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAED,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAa,GACd,EAAE,cAAc,2CA8BhB"}
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Input } from '../../primitives/input.js';
4
+ import { Label } from '../../primitives/label.js';
5
+ import { cn } from '../../lib/utils.js';
6
+ export function TextField({ name, value, onChange, label, placeholder, error, disabled, required, mode = 'edit', }) {
7
+ if (mode === 'read') {
8
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-muted-foreground", children: label }), _jsx("p", { className: "text-sm", children: value || '-' })] }));
9
+ }
10
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: name, children: [label, required && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), _jsx(Input, { id: name, name: name, type: "text", value: value || '', onChange: (e) => onChange(e.target.value), placeholder: placeholder, disabled: disabled, required: required, className: cn(error && 'border-destructive') }), error && _jsx("p", { className: "text-sm text-destructive", children: error })] }));
11
+ }
@@ -0,0 +1,12 @@
1
+ export interface TimestampFieldProps {
2
+ name: string;
3
+ value: Date | string | null;
4
+ onChange: (value: Date | null) => void;
5
+ label: string;
6
+ error?: string;
7
+ disabled?: boolean;
8
+ required?: boolean;
9
+ mode?: 'read' | 'edit';
10
+ }
11
+ export declare function TimestampField({ name, value, onChange, label, error, disabled, required, mode, }: TimestampFieldProps): import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=TimestampField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimestampField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/TimestampField.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAA;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,IAAa,GACd,EAAE,mBAAmB,2CAsBrB"}
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Label } from '../../primitives/label.js';
4
+ import { DateTimePicker } from '../../primitives/datetime-picker.js';
5
+ import { format } from 'date-fns';
6
+ export function TimestampField({ name, value, onChange, label, error, disabled, required, mode = 'edit', }) {
7
+ const dateValue = value ? new Date(value) : null;
8
+ if (mode === 'read') {
9
+ return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-muted-foreground", children: label }), _jsx("p", { className: "text-sm", children: dateValue ? format(dateValue, 'PPpp') : '-' })] }));
10
+ }
11
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: name, children: [label, required && _jsx("span", { className: "text-destructive ml-1", children: "*" })] }), _jsx(DateTimePicker, { value: dateValue, onChange: onChange, disabled: disabled }), error && _jsx("p", { className: "text-sm text-destructive", children: error })] }));
12
+ }
@@ -0,0 +1,23 @@
1
+ export { TextField } from './TextField.js';
2
+ export { IntegerField } from './IntegerField.js';
3
+ export { CheckboxField } from './CheckboxField.js';
4
+ export { SelectField } from './SelectField.js';
5
+ export { TimestampField } from './TimestampField.js';
6
+ export { PasswordField } from './PasswordField.js';
7
+ export { RelationshipField } from './RelationshipField.js';
8
+ export { ComboboxField } from './ComboboxField.js';
9
+ export { RelationshipManager } from './RelationshipManager.js';
10
+ export { FieldRenderer } from './FieldRenderer.js';
11
+ export { fieldComponentRegistry, registerFieldComponent, getFieldComponent } from './registry.js';
12
+ export type { TextFieldProps } from './TextField.js';
13
+ export type { IntegerFieldProps } from './IntegerField.js';
14
+ export type { CheckboxFieldProps } from './CheckboxField.js';
15
+ export type { SelectFieldProps } from './SelectField.js';
16
+ export type { TimestampFieldProps } from './TimestampField.js';
17
+ export type { PasswordFieldProps } from './PasswordField.js';
18
+ export type { RelationshipFieldProps } from './RelationshipField.js';
19
+ export type { ComboboxFieldProps } from './ComboboxField.js';
20
+ export type { RelationshipManagerProps } from './RelationshipManager.js';
21
+ export type { FieldRendererProps } from './FieldRenderer.js';
22
+ export type { FieldComponent, FieldComponentProps } from './registry.js';
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/fields/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGlD,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGjG,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxD,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AACpE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,YAAY,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAA;AACxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,13 @@
1
+ // Field components
2
+ export { TextField } from './TextField.js';
3
+ export { IntegerField } from './IntegerField.js';
4
+ export { CheckboxField } from './CheckboxField.js';
5
+ export { SelectField } from './SelectField.js';
6
+ export { TimestampField } from './TimestampField.js';
7
+ export { PasswordField } from './PasswordField.js';
8
+ export { RelationshipField } from './RelationshipField.js';
9
+ export { ComboboxField } from './ComboboxField.js';
10
+ export { RelationshipManager } from './RelationshipManager.js';
11
+ export { FieldRenderer } from './FieldRenderer.js';
12
+ // Registry for custom field types
13
+ export { fieldComponentRegistry, registerFieldComponent, getFieldComponent } from './registry.js';
@@ -0,0 +1,43 @@
1
+ import type { ComponentType } from 'react';
2
+ /**
3
+ * Base props that all field components must accept
4
+ * Field components can extend this with additional field-specific props
5
+ */
6
+ export type FieldComponentProps = {
7
+ name: string;
8
+ value: unknown;
9
+ onChange: (value: unknown) => void;
10
+ label: string;
11
+ error?: string;
12
+ disabled?: boolean;
13
+ required?: boolean;
14
+ mode?: 'read' | 'edit';
15
+ };
16
+ /**
17
+ * Type for field component
18
+ * Field components must accept props that extend FieldComponentProps.
19
+ * The registry uses ComponentType<any> because components have different
20
+ * specific prop types (e.g., value: string vs value: number), but all
21
+ * must include the base FieldComponentProps structure.
22
+ */
23
+ export type FieldComponent = ComponentType<FieldComponentProps & Record<string, unknown>>;
24
+ /**
25
+ * Registry mapping field types to their default UI components
26
+ * This can be extended for custom field types
27
+ * Uses ComponentType<any> to allow components with more specific prop types
28
+ */
29
+ export declare const fieldComponentRegistry: Record<string, ComponentType<any>>;
30
+ /**
31
+ * Register a custom field component for a field type
32
+ * Useful for adding support for custom field types
33
+ *
34
+ * @param fieldType - The field type identifier
35
+ * @param component - A React component that accepts FieldComponentProps (and optionally additional props)
36
+ */
37
+ export declare function registerFieldComponent(fieldType: string, component: ComponentType<any>): void;
38
+ /**
39
+ * Get the component for a field type
40
+ * Returns undefined if no component is registered
41
+ */
42
+ export declare function getFieldComponent(fieldType: string): ComponentType<any> | undefined;
43
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/components/fields/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAS1C;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,aAAa,CAAC,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAEzF;;;;GAIG;AAEH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAQrE,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EAEjB,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,GAC5B,IAAI,CAEN;AAED;;;GAGG;AAEH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS,CAEnF"}
@@ -0,0 +1,42 @@
1
+ import { TextField } from './TextField.js';
2
+ import { IntegerField } from './IntegerField.js';
3
+ import { CheckboxField } from './CheckboxField.js';
4
+ import { SelectField } from './SelectField.js';
5
+ import { TimestampField } from './TimestampField.js';
6
+ import { PasswordField } from './PasswordField.js';
7
+ import { RelationshipField } from './RelationshipField.js';
8
+ /**
9
+ * Registry mapping field types to their default UI components
10
+ * This can be extended for custom field types
11
+ * Uses ComponentType<any> to allow components with more specific prop types
12
+ */
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ export const fieldComponentRegistry = {
15
+ text: TextField,
16
+ integer: IntegerField,
17
+ checkbox: CheckboxField,
18
+ select: SelectField,
19
+ timestamp: TimestampField,
20
+ password: PasswordField,
21
+ relationship: RelationshipField,
22
+ };
23
+ /**
24
+ * Register a custom field component for a field type
25
+ * Useful for adding support for custom field types
26
+ *
27
+ * @param fieldType - The field type identifier
28
+ * @param component - A React component that accepts FieldComponentProps (and optionally additional props)
29
+ */
30
+ export function registerFieldComponent(fieldType,
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ component) {
33
+ fieldComponentRegistry[fieldType] = component;
34
+ }
35
+ /**
36
+ * Get the component for a field type
37
+ * Returns undefined if no component is registered
38
+ */
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ export function getFieldComponent(fieldType) {
41
+ return fieldComponentRegistry[fieldType];
42
+ }
@@ -0,0 +1,35 @@
1
+ export interface DeleteButtonProps {
2
+ onDelete: () => Promise<{
3
+ success: boolean;
4
+ error?: string;
5
+ }>;
6
+ itemName?: string;
7
+ confirmTitle?: string;
8
+ confirmMessage?: string;
9
+ confirmLabel?: string;
10
+ cancelLabel?: string;
11
+ buttonLabel?: string;
12
+ variant?: 'danger' | 'warning';
13
+ buttonVariant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
14
+ size?: 'default' | 'sm' | 'lg' | 'icon';
15
+ className?: string;
16
+ disabled?: boolean;
17
+ }
18
+ /**
19
+ * Standalone delete button with confirmation dialog
20
+ * Can be embedded in any custom page
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * <DeleteButton
25
+ * onDelete={async () => {
26
+ * await deletePost(postId);
27
+ * return { success: true };
28
+ * }}
29
+ * itemName="post"
30
+ * confirmMessage="This will permanently delete the post and all its comments."
31
+ * />
32
+ * ```
33
+ */
34
+ export declare function DeleteButton({ onDelete, itemName, confirmTitle, confirmMessage, confirmLabel, cancelLabel, buttonLabel, variant, buttonVariant, size, className, disabled, }: DeleteButtonProps): import("react/jsx-runtime").JSX.Element;
35
+ //# sourceMappingURL=DeleteButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeleteButton.d.ts","sourceRoot":"","sources":["../../../src/components/standalone/DeleteButton.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC9B,aAAa,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAA;IACtF,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAA;IACvC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,QAAiB,EACjB,YAAY,EACZ,cAAc,EACd,YAAuB,EACvB,WAAsB,EACtB,WAAsB,EACtB,OAAkB,EAClB,aAA6B,EAC7B,IAAgB,EAChB,SAAS,EACT,QAAgB,GACjB,EAAE,iBAAiB,2CA8DnB"}
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Button } from '../../primitives/button.js';
5
+ import { ConfirmDialog } from '../ConfirmDialog.js';
6
+ import { LoadingSpinner } from '../LoadingSpinner.js';
7
+ /**
8
+ * Standalone delete button with confirmation dialog
9
+ * Can be embedded in any custom page
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * <DeleteButton
14
+ * onDelete={async () => {
15
+ * await deletePost(postId);
16
+ * return { success: true };
17
+ * }}
18
+ * itemName="post"
19
+ * confirmMessage="This will permanently delete the post and all its comments."
20
+ * />
21
+ * ```
22
+ */
23
+ export function DeleteButton({ onDelete, itemName = 'item', confirmTitle, confirmMessage, confirmLabel = 'Delete', cancelLabel = 'Cancel', buttonLabel = 'Delete', variant = 'danger', buttonVariant = 'destructive', size = 'default', className, disabled = false, }) {
24
+ const [showConfirm, setShowConfirm] = useState(false);
25
+ const [isPending, setIsPending] = useState(false);
26
+ const [error, setError] = useState(null);
27
+ const handleDelete = async () => {
28
+ setShowConfirm(false);
29
+ setIsPending(true);
30
+ setError(null);
31
+ try {
32
+ const result = await onDelete();
33
+ if (!result.success) {
34
+ setError(result.error || `Failed to delete ${itemName}`);
35
+ }
36
+ }
37
+ catch (err) {
38
+ setError(err.message || `Failed to delete ${itemName}`);
39
+ }
40
+ finally {
41
+ setIsPending(false);
42
+ }
43
+ };
44
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { type: "button", variant: buttonVariant, size: size, onClick: () => setShowConfirm(true), disabled: disabled || isPending, className: className, children: [isPending && (_jsx(LoadingSpinner, { size: "sm", className: "border-primary-foreground border-t-transparent mr-2" })), buttonLabel] }), error && (_jsx("div", { className: "mt-2 bg-destructive/10 border border-destructive text-destructive rounded-lg p-3", children: _jsx("p", { className: "text-sm font-medium", children: error }) })), _jsx(ConfirmDialog, { isOpen: showConfirm, title: confirmTitle || `Delete ${itemName}`, message: confirmMessage ||
45
+ `Are you sure you want to delete this ${itemName}? This action cannot be undone.`, confirmLabel: confirmLabel, cancelLabel: cancelLabel, variant: variant, onConfirm: handleDelete, onCancel: () => setShowConfirm(false) })] }));
46
+ }