@donotdev/crud 0.0.4 → 0.0.6

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 (150) hide show
  1. package/dist/CrudService.d.ts +7 -3
  2. package/dist/CrudService.d.ts.map +1 -1
  3. package/dist/CrudService.js +1 -1
  4. package/dist/CrudStore.d.ts +6 -1
  5. package/dist/CrudStore.d.ts.map +1 -1
  6. package/dist/CrudStore.js +1 -1
  7. package/dist/FieldRegistry.d.ts +126 -0
  8. package/dist/FieldRegistry.d.ts.map +1 -0
  9. package/dist/FieldRegistry.js +1 -0
  10. package/dist/adapters/FirestoreAdapter.d.ts.map +1 -1
  11. package/dist/adapters/FirestoreAdapter.js +1 -1
  12. package/dist/builtinFieldTypes.d.ts +5 -0
  13. package/dist/builtinFieldTypes.d.ts.map +1 -0
  14. package/dist/builtinFieldTypes.js +1 -0
  15. package/dist/components/ControlledFields.d.ts +42 -24
  16. package/dist/components/ControlledFields.d.ts.map +1 -1
  17. package/dist/components/ControlledFields.js +1 -1
  18. package/dist/components/DisplayFieldRenderer.d.ts +36 -0
  19. package/dist/components/DisplayFieldRenderer.d.ts.map +1 -0
  20. package/dist/components/DisplayFieldRenderer.js +1 -0
  21. package/dist/components/EntityFormRenderer.d.ts +36 -8
  22. package/dist/components/EntityFormRenderer.d.ts.map +1 -1
  23. package/dist/components/EntityFormRenderer.js +5 -1
  24. package/dist/components/FormFieldRenderer.d.ts +3 -16
  25. package/dist/components/FormFieldRenderer.d.ts.map +1 -1
  26. package/dist/components/FormFieldRenderer.js +1 -1
  27. package/dist/components/form/fields/AddressFieldComponent.d.ts +3 -1
  28. package/dist/components/form/fields/AddressFieldComponent.d.ts.map +1 -1
  29. package/dist/components/form/fields/AddressFieldComponent.js +1 -1
  30. package/dist/components/form/fields/CheckboxFieldComponent.d.ts +2 -0
  31. package/dist/components/form/fields/CheckboxFieldComponent.d.ts.map +1 -1
  32. package/dist/components/form/fields/CheckboxFieldComponent.js +1 -1
  33. package/dist/components/form/fields/ComboboxComponent.d.ts +43 -0
  34. package/dist/components/form/fields/ComboboxComponent.d.ts.map +1 -0
  35. package/dist/components/form/fields/ComboboxComponent.js +1 -0
  36. package/dist/components/form/fields/CurrencyFieldComponent.d.ts +41 -0
  37. package/dist/components/form/fields/CurrencyFieldComponent.d.ts.map +1 -0
  38. package/dist/components/form/fields/CurrencyFieldComponent.js +1 -0
  39. package/dist/components/form/fields/DateFieldComponent.d.ts +2 -0
  40. package/dist/components/form/fields/DateFieldComponent.d.ts.map +1 -1
  41. package/dist/components/form/fields/DateFieldComponent.js +1 -1
  42. package/dist/components/form/fields/DropdownComponent.d.ts.map +1 -1
  43. package/dist/components/form/fields/DropdownComponent.js +1 -1
  44. package/dist/components/form/fields/FileFieldComponent.d.ts +2 -0
  45. package/dist/components/form/fields/FileFieldComponent.d.ts.map +1 -1
  46. package/dist/components/form/fields/FileFieldComponent.js +1 -1
  47. package/dist/components/form/fields/GeoPointFieldComponent.d.ts +2 -0
  48. package/dist/components/form/fields/GeoPointFieldComponent.d.ts.map +1 -1
  49. package/dist/components/form/fields/GeoPointFieldComponent.js +1 -1
  50. package/dist/components/form/fields/ImageFieldComponent.d.ts +32 -17
  51. package/dist/components/form/fields/ImageFieldComponent.d.ts.map +1 -1
  52. package/dist/components/form/fields/ImageFieldComponent.js +1 -1
  53. package/dist/components/form/fields/MapFieldComponent.d.ts +2 -0
  54. package/dist/components/form/fields/MapFieldComponent.d.ts.map +1 -1
  55. package/dist/components/form/fields/MapFieldComponent.js +1 -1
  56. package/dist/components/form/fields/MultiDropdownComponent.d.ts +2 -2
  57. package/dist/components/form/fields/MultiDropdownComponent.d.ts.map +1 -1
  58. package/dist/components/form/fields/MultiDropdownComponent.js +1 -1
  59. package/dist/components/form/fields/MultiInputTextFieldComponent.d.ts +2 -0
  60. package/dist/components/form/fields/MultiInputTextFieldComponent.d.ts.map +1 -1
  61. package/dist/components/form/fields/MultiInputTextFieldComponent.js +1 -1
  62. package/dist/components/form/fields/NumberFieldComponent.d.ts.map +1 -1
  63. package/dist/components/form/fields/NumberFieldComponent.js +1 -1
  64. package/dist/components/form/fields/PasswordFieldComponent.d.ts.map +1 -1
  65. package/dist/components/form/fields/PasswordFieldComponent.js +1 -1
  66. package/dist/components/form/fields/PhoneNumberComponent.d.ts +31 -17
  67. package/dist/components/form/fields/PhoneNumberComponent.d.ts.map +1 -1
  68. package/dist/components/form/fields/PhoneNumberComponent.js +1 -1
  69. package/dist/components/form/fields/RadioFieldComponent.d.ts.map +1 -1
  70. package/dist/components/form/fields/RadioFieldComponent.js +1 -1
  71. package/dist/components/form/fields/RangeFieldComponent.d.ts.map +1 -1
  72. package/dist/components/form/fields/RangeFieldComponent.js +1 -1
  73. package/dist/components/form/fields/ReferenceFieldComponent.d.ts +7 -2
  74. package/dist/components/form/fields/ReferenceFieldComponent.d.ts.map +1 -1
  75. package/dist/components/form/fields/ReferenceFieldComponent.js +1 -1
  76. package/dist/components/form/fields/SwitchFieldComponent.d.ts +28 -0
  77. package/dist/components/form/fields/SwitchFieldComponent.d.ts.map +1 -0
  78. package/dist/components/form/fields/SwitchFieldComponent.js +1 -0
  79. package/dist/components/form/fields/TextAreaComponent.d.ts.map +1 -1
  80. package/dist/components/form/fields/TextAreaComponent.js +1 -1
  81. package/dist/components/form/fields/TextFieldComponent.d.ts.map +1 -1
  82. package/dist/components/form/fields/TextFieldComponent.js +1 -1
  83. package/dist/components/form/fields/TimestampFieldComponent.d.ts +2 -0
  84. package/dist/components/form/fields/TimestampFieldComponent.d.ts.map +1 -1
  85. package/dist/components/form/fields/TimestampFieldComponent.js +1 -1
  86. package/dist/components/form/fields/index.d.ts +7 -1
  87. package/dist/components/form/fields/index.d.ts.map +1 -1
  88. package/dist/components/form/fields/index.js +1 -1
  89. package/dist/components/form/internal/ImageViewerDialog.d.ts +25 -0
  90. package/dist/components/form/internal/ImageViewerDialog.d.ts.map +1 -0
  91. package/dist/components/form/internal/ImageViewerDialog.js +1 -0
  92. package/dist/components/index.d.ts +2 -0
  93. package/dist/components/index.d.ts.map +1 -1
  94. package/dist/components/index.js +1 -1
  95. package/dist/context/FormUploadContext.d.ts +36 -0
  96. package/dist/context/FormUploadContext.d.ts.map +1 -0
  97. package/dist/context/FormUploadContext.js +1 -0
  98. package/dist/context/index.d.ts +2 -0
  99. package/dist/context/index.d.ts.map +1 -0
  100. package/dist/context/index.js +1 -0
  101. package/dist/forms/hooks/index.d.ts +11 -0
  102. package/dist/forms/hooks/index.d.ts.map +1 -0
  103. package/dist/forms/hooks/index.js +1 -0
  104. package/dist/forms/hooks/useEntityField.d.ts +67 -0
  105. package/dist/forms/hooks/useEntityField.d.ts.map +1 -0
  106. package/dist/forms/hooks/useEntityField.js +1 -0
  107. package/dist/forms/hooks/useEntityForm.d.ts +89 -0
  108. package/dist/forms/hooks/useEntityForm.d.ts.map +1 -0
  109. package/dist/forms/hooks/useEntityForm.js +1 -0
  110. package/dist/forms/index.d.ts +37 -0
  111. package/dist/forms/index.d.ts.map +1 -0
  112. package/dist/forms/index.js +1 -0
  113. package/dist/forms/types.d.ts +185 -0
  114. package/dist/forms/types.d.ts.map +1 -0
  115. package/dist/forms/types.js +0 -0
  116. package/dist/forms/utils/createEntitySchema.d.ts +53 -0
  117. package/dist/forms/utils/createEntitySchema.d.ts.map +1 -0
  118. package/dist/forms/utils/createEntitySchema.js +1 -0
  119. package/dist/forms/utils/getFieldsForOperation.d.ts +87 -0
  120. package/dist/forms/utils/getFieldsForOperation.d.ts.map +1 -0
  121. package/dist/forms/utils/getFieldsForOperation.js +1 -0
  122. package/dist/forms/utils/index.d.ts +14 -0
  123. package/dist/forms/utils/index.d.ts.map +1 -0
  124. package/dist/forms/utils/index.js +1 -0
  125. package/dist/forms/utils/isFieldEditable.d.ts +43 -0
  126. package/dist/forms/utils/isFieldEditable.d.ts.map +1 -0
  127. package/dist/forms/utils/isFieldEditable.js +1 -0
  128. package/dist/forms/utils/normalizeToFieldConfig.d.ts +47 -0
  129. package/dist/forms/utils/normalizeToFieldConfig.d.ts.map +1 -0
  130. package/dist/forms/utils/normalizeToFieldConfig.js +1 -0
  131. package/dist/forms/utils/validateEntity.d.ts +77 -0
  132. package/dist/forms/utils/validateEntity.d.ts.map +1 -0
  133. package/dist/forms/utils/validateEntity.js +1 -0
  134. package/dist/index.d.ts +4 -1
  135. package/dist/index.d.ts.map +1 -1
  136. package/dist/index.js +1 -1
  137. package/dist/tsconfig.tsbuildinfo +1 -1
  138. package/dist/useCrud.d.ts +67 -15
  139. package/dist/useCrud.d.ts.map +1 -1
  140. package/dist/useCrud.js +1 -1
  141. package/dist/utils/imageProcessing.d.ts +49 -0
  142. package/dist/utils/imageProcessing.d.ts.map +1 -0
  143. package/dist/utils/imageProcessing.js +1 -0
  144. package/dist/utils/imageStorage.d.ts +35 -0
  145. package/dist/utils/imageStorage.d.ts.map +1 -0
  146. package/dist/utils/imageStorage.js +1 -0
  147. package/dist/utils/imageUtils.d.ts +86 -0
  148. package/dist/utils/imageUtils.d.ts.map +1 -0
  149. package/dist/utils/imageUtils.js +1 -0
  150. package/package.json +10 -7
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,Fragment as Y,jsxs as a}from"react/jsx-runtime";import{ChevronLeft as Z,ChevronRight as q,RotateCw as J,Trash2 as K,Crop as Q,Eye as X,Save as ee,Undo2 as te}from"lucide-react";import{useCallback as O,useEffect as ae,useState as c}from"react";import ie from"react-easy-crop";import{Button as l,BUTTON_VARIANT as s,Dialog as oe,Stack as r,Text as T,Input as V,Label as f,Slider as F,Tabs as re}from"@donotdev/components";import{processImage as ne}from"../../../utils/imageProcessing";import{createPreviewURL as le,revokePreviewURL as se}from"../../../utils/imageUtils";function ce({images:n,selectedIndex:i,open:N,onClose:C,onUpdate:A,onDelete:U,onNavigate:p,multiple:d=!1}){const[R,m]=c("view"),[j,x]=c({x:0,y:0}),[P,b]=c(1),[u,h]=c(0),[k,z]=c(null),[L,$]=c(!1),[w,S]=c({alt:"",caption:""}),[B,v]=c(!1),o=n[i],H=d&&i>0,W=d&&i<n.length-1;ae(()=>{o&&(x({x:0,y:0}),b(1),h(o.rotation*90),S({alt:"",caption:""}),m("view"),v(!1))},[o]);const D=(t,g)=>{S(y=>({...y,[t]:g}))},G=O((t,g)=>{z(g)},[]),M=async()=>{if(!(!o||!k)){$(!0);try{const t=await ne(o.file,{crop:k,maxWidth:2048,maxHeight:2048,quality:.95}),g=new File([t.fullBlob],o.file.name,{type:"image/webp"}),y=le(g);A(i,{file:g,previewURL:y,uploaded:null,uploadProgress:null,error:null,rotation:0}),o.previewURL.startsWith("blob:")&&se(o.previewURL),m("view")}catch{}finally{$(!1)}}},_=O(()=>{U(i),v(!1),n.length===1?C():i>=n.length-1&&p(i-1)},[i,n.length,U,C,p]);if(!o)return null;const E=o.uploadProgress!==null&&o.uploadProgress<100,I=o.previewURL||o.uploaded?.fullUrl;return e(oe,{open:N,onOpenChange:t=>!t&&C(),title:d?`Image ${i+1} of ${n.length}`:"Image Details",description:d?`View, edit, and manage image ${i+1} of ${n.length}. Use the tabs to view metadata or edit the image with crop and rotation tools.`:"View, edit, and manage this image. Use the tabs to view metadata or edit the image with crop and rotation tools.",showClose:!0,children:a("div",{style:{display:"grid",gridTemplateColumns:"1fr 300px",gap:"var(--gap-lg)",height:"60vh",minHeight:"500px"},children:[e("div",{style:{position:"relative",backgroundColor:"var(--surface-muted)",borderRadius:"var(--radius-lg)",overflow:"hidden",display:"flex",alignItems:"center",justifyContent:"center"},children:R==="edit"?e("div",{style:{position:"absolute",top:0,left:0,right:0,bottom:0},children:e(ie,{image:I,crop:j,zoom:P,rotation:u,onCropChange:x,onCropComplete:G,onZoomChange:b,onRotationChange:h})}):a(Y,{children:[H&&e(l,{variant:s.GHOST,icon:Z,onClick:()=>p(i-1),"aria-label":"Previous image",style:{position:"absolute",left:"var(--gap-sm)",zIndex:10,backgroundColor:"rgba(0,0,0,0.3)",color:"white",border:"none"}}),e("img",{src:I,alt:w.alt||o.file.name||`Image ${i+1}${d?` of ${n.length}`:""}`,loading:"eager",decoding:"async",fetchPriority:"high",style:{maxWidth:"100%",maxHeight:"100%",objectFit:"contain",transform:`rotate(${u}deg)`},"aria-label":`Image ${i+1}${d?` of ${n.length}`:""}`}),W&&e(l,{variant:s.GHOST,icon:q,onClick:()=>p(i+1),"aria-label":"Next image",style:{position:"absolute",right:"var(--gap-sm)",zIndex:10,backgroundColor:"rgba(0,0,0,0.3)",color:"white",border:"none"}})]})}),e(r,{gap:"large",style:{overflowY:"auto",height:"100%"},children:e(re,{value:R,onValueChange:t=>m(t),items:[{value:"view",label:a(r,{direction:"row",align:"center",gap:"tight",children:[e(X,{className:"dndev-size-sm"}),e(T,{children:"View"})]}),content:a(r,{gap:"medium",style:{paddingTop:"var(--gap-md)"},children:[a(r,{gap:"tight",children:[e(f,{htmlFor:"img-alt",children:"Alt Text (Accessibility)"}),e(V,{id:"img-alt",value:w.alt,onChange:t=>D("alt",t.target.value),placeholder:"Describe the image..."})]}),a(r,{gap:"tight",children:[e(f,{htmlFor:"img-caption",children:"Caption"}),e(V,{id:"img-caption",value:w.caption,onChange:t=>D("caption",t.target.value),placeholder:"Image caption or credits"})]}),e("div",{style:{marginTop:"auto",paddingTop:"var(--gap-lg)"},children:B?a(r,{gap:"medium",style:{border:"1px solid var(--destructive)",padding:"var(--gap-md)",borderRadius:"var(--radius-md)"},children:[e(T,{variant:"destructive",level:"small",children:"Permanently delete this image?"}),a(r,{direction:"row",gap:"tight",children:[e(l,{variant:s.DESTRUCTIVE,onClick:_,fullWidth:!0,children:"Yes, Delete"}),e(l,{variant:s.OUTLINE,onClick:()=>v(!1),fullWidth:!0,children:"Cancel"})]})]}):e(l,{variant:s.OUTLINE,className:"text-destructive hover:bg-destructive/10",icon:K,onClick:()=>v(!0),fullWidth:!0,disabled:E,children:"Delete Image"})})]})},{value:"edit",label:a(r,{direction:"row",align:"center",gap:"tight",children:[e(Q,{className:"dndev-size-sm"}),e(T,{children:"Edit"})]}),disabled:E,content:a(r,{gap:"medium",style:{paddingTop:"var(--gap-md)"},children:[a(r,{gap:"tight",children:[e(f,{children:"Zoom"}),e(F,{value:[P],min:1,max:3,step:.1,onValueChange:t=>b(t[0]??1)})]}),a(r,{gap:"tight",children:[a(f,{children:["Rotation (",u,"\xB0)"]}),a(r,{direction:"row",gap:"tight",children:[e(l,{variant:s.OUTLINE,onClick:()=>h(t=>t-90),icon:te,title:"-90\xB0"}),e(F,{value:[u],min:0,max:360,step:90,onValueChange:t=>h(t[0]??0),style:{flex:1}}),e(l,{variant:s.OUTLINE,onClick:()=>h(t=>t+90),icon:J,title:"+90\xB0"})]})]}),a(r,{gap:"medium",style:{marginTop:"var(--gap-xl)"},children:[e(l,{variant:s.PRIMARY,onClick:M,disabled:L,icon:ee,children:L?"Processing...":"Save Changes"}),e(l,{variant:s.GHOST,onClick:()=>m("view"),children:"Cancel"})]})]})}]})})]})})}var fe=ce;export{ce as ImageViewerDialog,fe as default};
@@ -10,6 +10,8 @@ export { default as CrudButton } from './CrudButton';
10
10
  export type { CrudButtonProps } from './CrudButton';
11
11
  export { EntityFormRenderer } from './EntityFormRenderer';
12
12
  export { FormFieldRenderer } from './FormFieldRenderer';
13
+ export { DisplayFieldRenderer } from './DisplayFieldRenderer';
14
+ export type { DisplayFieldRendererProps } from './DisplayFieldRenderer';
13
15
  export { default as FormLayout } from './FormLayout';
14
16
  export * from './ControlledFields';
15
17
  export * from './form/fields';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,cAAc,CAAC;AACrD,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC"}
@@ -1 +1 @@
1
- import{default as e}from"./CrudButton";import{EntityFormRenderer as m}from"./EntityFormRenderer";import{FormFieldRenderer as d}from"./FormFieldRenderer";import{default as x}from"./FormLayout";export*from"./ControlledFields";export*from"./form/fields";export{e as CrudButton,m as EntityFormRenderer,d as FormFieldRenderer,x as FormLayout};
1
+ import{default as o}from"./CrudButton";import{EntityFormRenderer as m}from"./EntityFormRenderer";import{FormFieldRenderer as d}from"./FormFieldRenderer";import{DisplayFieldRenderer as x}from"./DisplayFieldRenderer";import{default as l}from"./FormLayout";export*from"./ControlledFields";export*from"./form/fields";export{o as CrudButton,x as DisplayFieldRenderer,m as EntityFormRenderer,d as FormFieldRenderer,l as FormLayout};
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @fileoverview Form Upload Context
3
+ * @description Context for coordinating deferred uploads (images, files) before form submit.
4
+ * Components register their upload functions, EntityFormRenderer calls them before validation.
5
+ *
6
+ * @version 0.0.1
7
+ * @since 0.0.1
8
+ * @author AMBROISE PARK Consulting
9
+ */
10
+ import { type ReactNode } from 'react';
11
+ type UploadFunction = () => Promise<void>;
12
+ interface FormUploadContextValue {
13
+ /** Register an upload function for a field */
14
+ registerUpload: (fieldName: string, uploadFn: UploadFunction) => void;
15
+ /** Unregister upload function when field unmounts */
16
+ unregisterUpload: (fieldName: string) => void;
17
+ /** Call all registered upload functions (before form submit) */
18
+ uploadAll: () => Promise<void>;
19
+ /** Check if any uploads are pending */
20
+ hasPendingUploads: () => boolean;
21
+ }
22
+ /**
23
+ * Hook to access form upload context
24
+ * Returns null if not within FormUploadProvider (for standalone usage)
25
+ */
26
+ export declare function useFormUpload(): FormUploadContextValue | null;
27
+ interface FormUploadProviderProps {
28
+ children: ReactNode;
29
+ }
30
+ /**
31
+ * Provider for form upload coordination
32
+ * Wrap forms that contain deferred upload fields (images, files)
33
+ */
34
+ export declare function FormUploadProvider({ children }: FormUploadProviderProps): import("react/jsx-runtime").JSX.Element;
35
+ export {};
36
+ //# sourceMappingURL=FormUploadContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormUploadContext.d.ts","sourceRoot":"","sources":["../../src/context/FormUploadContext.tsx"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,KAAK,cAAc,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1C,UAAU,sBAAsB;IAC9B,8CAA8C;IAC9C,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;IACtE,qDAAqD;IACrD,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,gEAAgE;IAChE,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,uCAAuC;IACvC,iBAAiB,EAAE,MAAM,OAAO,CAAC;CAClC;AAID;;;GAGG;AACH,wBAAgB,aAAa,IAAI,sBAAsB,GAAG,IAAI,CAE7D;AAED,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE,uBAAuB,2CAiCvE"}
@@ -0,0 +1 @@
1
+ "use client";import{jsx as i}from"react/jsx-runtime";import{createContext as p,useContext as d,useCallback as t,useRef as m}from"react";const n=p(null);function U(){return d(n)}function g({children:s}){const r=m(new Map),a=t((e,o)=>{r.current.set(e,o)},[]),l=t(e=>{r.current.delete(e)},[]),u=t(async()=>{const e=Array.from(r.current.values());e.length!==0&&await Promise.all(e.map(o=>o()))},[]),c=t(()=>r.current.size>0,[]);return i(n.Provider,{value:{registerUpload:a,unregisterUpload:l,uploadAll:u,hasPendingUploads:c},children:s})}export{g as FormUploadProvider,U as useFormUpload};
@@ -0,0 +1,2 @@
1
+ export { FormUploadProvider, useFormUpload } from './FormUploadContext';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1 @@
1
+ import{FormUploadProvider as d,useFormUpload as e}from"./FormUploadContext";export{d as FormUploadProvider,e as useFormUpload};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview Form hooks barrel export
3
+ * @description Exports all form hooks for custom form building.
4
+ *
5
+ * @version 0.0.1
6
+ * @since 0.0.1
7
+ * @author AMBROISE PARK Consulting
8
+ */
9
+ export { useEntityForm } from './useEntityForm';
10
+ export { useEntityField } from './useEntityField';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/forms/hooks/index.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1 @@
1
+ import{useEntityForm as o}from"./useEntityForm";import{useEntityField as i}from"./useEntityField";export{i as useEntityField,o as useEntityForm};
@@ -0,0 +1,67 @@
1
+ import type { Entity } from '@donotdev/core';
2
+ import type { EntityFormReturn, InferEntityData, EntityFieldReturn } from '../types';
3
+ /**
4
+ * Granular hook for single field control within an entity form.
5
+ *
6
+ * Useful when you need fine-grained control over field rendering,
7
+ * such as custom components, conditional logic, or complex layouts.
8
+ *
9
+ * @template E - Entity type from defineEntity()
10
+ * @template K - Field key from entity
11
+ * @param form - Form instance from useEntityForm
12
+ * @param fieldName - Name of the field to control
13
+ * @returns Field state, config, and editability information
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * import { useEntityForm, useEntityField } from '@donotdev/crud/forms';
18
+ *
19
+ * function CustomEmailField({ form }) {
20
+ * const { field, meta, config, isEditable } = useEntityField(form, 'email');
21
+ *
22
+ * if (!isEditable) {
23
+ * return <span>{field.value}</span>;
24
+ * }
25
+ *
26
+ * return (
27
+ * <div>
28
+ * <label>{config.label}</label>
29
+ * <input
30
+ * type="email"
31
+ * value={field.value ?? ''}
32
+ * onChange={field.onChange}
33
+ * onBlur={field.onBlur}
34
+ * />
35
+ * {meta.error && <span className="error">{meta.error}</span>}
36
+ * </div>
37
+ * );
38
+ * }
39
+ * ```
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * // With conditional rendering
44
+ * function ConditionalField({ form }) {
45
+ * const typeField = useEntityField(form, 'type');
46
+ * const lengthField = useEntityField(form, 'truckBedLength');
47
+ *
48
+ * return (
49
+ * <>
50
+ * <Select {...typeField.field} options={typeField.config.validation?.options} />
51
+ * {typeField.field.value === 'truck' && lengthField.isVisible && (
52
+ * <Input {...lengthField.field} label={lengthField.config.label} />
53
+ * )}
54
+ * </>
55
+ * );
56
+ * }
57
+ * ```
58
+ *
59
+ * @see {@link useEntityForm} for creating the form instance
60
+ * @see {@link EntityFieldReturn} for return type
61
+ *
62
+ * @version 0.0.1
63
+ * @since 0.0.1
64
+ * @author AMBROISE PARK Consulting
65
+ */
66
+ export declare function useEntityField<E extends Entity, K extends keyof InferEntityData<E>>(form: EntityFormReturn<E>, fieldName: K): EntityFieldReturn<E, K>;
67
+ //# sourceMappingURL=useEntityField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEntityField.d.ts","sourceRoot":"","sources":["../../../src/forms/hooks/useEntityField.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,MAAM,EAA4B,MAAM,gBAAgB,CAAC;AAGvE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAgB,cAAc,CAC5B,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,MAAM,eAAe,CAAC,CAAC,CAAC,EAClC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAqElE"}
@@ -0,0 +1 @@
1
+ "use client";import{useMemo as r}from"react";import{useController as m}from"react-hook-form";import{normalizeToFieldConfig as p,isFieldEditable as g}from"../utils";function E(l,e){const{entity:d,operation:o,viewerRole:c,fields:u}=l,s=d.fields[e],i=r(()=>s?p(e,s):{name:e,type:"text",label:e,required:!1},[e,s]),n=r(()=>u.some(y=>y.name===e),[u,e]),a=r(()=>!n||o==="edit"&&i.visibility==="technical"&&i.editable===void 0?!1:g(i.editable,c,o),[n,o,i.visibility,i.editable,c]),{field:f,fieldState:t}=m({name:e,control:l.control}),b=r(()=>({error:t.error?.message,touched:t.isTouched,isDirty:t.isDirty}),[t.error?.message,t.isTouched,t.isDirty]);return{field:f,meta:b,config:i,isEditable:a,isVisible:n}}export{E as useEntityField};
@@ -0,0 +1,89 @@
1
+ import type { Entity } from '@donotdev/core';
2
+ import type { UseEntityFormOptions, EntityFormReturn } from '../types';
3
+ /**
4
+ * Creates a type-safe form instance from an entity definition.
5
+ *
6
+ * Automatically configures React Hook Form with:
7
+ * - Pre-filtered fields based on operation and visibility
8
+ * - Editability computed per-field based on viewer role
9
+ * - Type-safe form values inferred from entity
10
+ *
11
+ * @template E - Entity type from defineEntity()
12
+ * @param entity - Entity definition from defineEntity()
13
+ * @param options - Form configuration options
14
+ * @returns Form instance with React Hook Form API plus entity-aware extensions
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * import { useEntityForm } from '@donotdev/crud/forms';
19
+ * import { productEntity } from './entities/product';
20
+ *
21
+ * function ProductForm({ existingProduct, onSuccess }) {
22
+ * const form = useEntityForm(productEntity, {
23
+ * operation: existingProduct ? 'edit' : 'create',
24
+ * defaultValues: existingProduct,
25
+ * viewerRole: 'admin'
26
+ * });
27
+ *
28
+ * const onSubmit = async (data) => {
29
+ * await saveProduct(data);
30
+ * onSuccess();
31
+ * };
32
+ *
33
+ * return (
34
+ * <form onSubmit={form.handleSubmit(onSubmit)}>
35
+ * {form.fields.map(({ name, config, editable }) => (
36
+ * editable ? (
37
+ * <input
38
+ * key={name}
39
+ * {...form.register(name)}
40
+ * placeholder={config.label}
41
+ * />
42
+ * ) : (
43
+ * <span key={name}>{form.getValues(name)}</span>
44
+ * )
45
+ * ))}
46
+ * <button type="submit" disabled={form.formState.isSubmitting}>
47
+ * {form.operation === 'create' ? 'Create' : 'Update'}
48
+ * </button>
49
+ * </form>
50
+ * );
51
+ * }
52
+ * ```
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * // With custom UI components
57
+ * import { useEntityForm } from '@donotdev/crud/forms';
58
+ * import { Input, Select, DatePicker } from './ui';
59
+ *
60
+ * function CustomForm() {
61
+ * const form = useEntityForm(orderEntity, { operation: 'create' });
62
+ *
63
+ * // Custom 2-column layout
64
+ * return (
65
+ * <form onSubmit={form.handleSubmit(onSubmit)}>
66
+ * <div className="grid grid-cols-2 gap-4">
67
+ * <Input label="Customer" {...form.register('customer')} />
68
+ * <DatePicker label="Date" {...form.register('orderDate')} />
69
+ * </div>
70
+ * <Select
71
+ * label="Status"
72
+ * options={form.fields.find(f => f.name === 'status')?.config.validation?.options}
73
+ * {...form.register('status')}
74
+ * />
75
+ * </form>
76
+ * );
77
+ * }
78
+ * ```
79
+ *
80
+ * @see {@link Entity} for entity definition structure
81
+ * @see {@link EntityFormReturn} for return type
82
+ * @see {@link getFieldsForOperation} for field filtering logic
83
+ *
84
+ * @version 0.0.1
85
+ * @since 0.0.1
86
+ * @author AMBROISE PARK Consulting
87
+ */
88
+ export declare function useEntityForm<E extends Entity>(entity: E, options?: UseEntityFormOptions<E>): EntityFormReturn<E>;
89
+ //# sourceMappingURL=useEntityForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useEntityForm.d.ts","sourceRoot":"","sources":["../../../src/forms/hooks/useEntityForm.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAM7C,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAEjB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5C,MAAM,EAAE,CAAC,EACT,OAAO,GAAE,oBAAoB,CAAC,CAAC,CAAM,GACpC,gBAAgB,CAAC,CAAC,CAAC,CA4LrB"}
@@ -0,0 +1 @@
1
+ "use client";import{useMemo as o,useEffect as g,useRef as h}from"react";import{useForm as k}from"react-hook-form";import{useLocalStorage as x}from"@donotdev/core";import{useAuth as H}from"@donotdev/auth";import{valibotResolver as w}from"@hookform/resolvers/valibot";import{createEntitySchema as y,getFieldsForOperation as K}from"../utils";function U(t,E={}){let v;try{v=H("userRole")}catch{}const{operation:L,defaultValues:r,viewerRole:m=v||"admin",mode:O="onSubmit",t:j,autoSave:s=!1}=E,e=L??(r?"edit":"create"),S=o(()=>y(t,"create"),[t]),R=o(()=>y(t,"draft"),[t]),T=o(()=>w(S),[S]),V=o(()=>w(R),[R]),A=o(()=>async(a,n,i)=>(a?.status==="draft"||r?.status==="draft"?V:T)(a,n,i),[T,V,r]),u=k({defaultValues:r,mode:O,resolver:A}),C=o(()=>`${t.name.toLowerCase()}-form-draft`,[t.name]),{value:d,setValue:D,removeValue:F}=x(C,{defaultValue:null,syncAcrossTabs:!0}),f=h(!1);g(()=>{!s||e!=="create"||r||f.current||(d&&u.reset(d),f.current=!0)},[s,e,r,d,u]);const p=h(!1),l=h(null);g(()=>{if(!s||e!=="create")return;const a=u.watch(n=>{p.current||!f.current||(l.current&&clearTimeout(l.current),l.current=setTimeout(()=>{Object.values(n).some(c=>c!=null&&c!=="")&&D(n)},3e3))});return()=>{a.unsubscribe(),l.current&&clearTimeout(l.current)}},[s,e,u,D]);const b=u.handleSubmit,M=o(()=>!s||e!=="create"?b:((a,n)=>b(async(i,c)=>{p.current=!0;try{await a(i,c),F()}finally{p.current=!1}},n)),[b,s,e,F]),P=o(()=>K(t,{operation:e,viewerRole:m,availableFields:e==="edit"&&r?Object.keys(r):void 0}),[t,e,m,r]);return{...u,handleSubmit:M,fields:P,operation:e,entity:t,t:j??(a=>a),viewerRole:m}}export{U as useEntityForm};
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @fileoverview Form building blocks for custom forms
3
+ * @description Exposes low-level form utilities from @donotdev/crud for building custom forms.
4
+ *
5
+ * Import from '@donotdev/crud/forms' to access form building blocks:
6
+ * - Hooks: useEntityForm, useEntityField
7
+ * - Utilities: getFieldsForOperation, createEntitySchema, validateEntity
8
+ * - Types: InferEntityData, EntityFormReturn, etc.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * import { useEntityForm, getFieldsForOperation } from '@donotdev/crud/forms';
13
+ * import { productEntity } from './entities/product';
14
+ *
15
+ * function ProductForm() {
16
+ * const form = useEntityForm(productEntity, { operation: 'create' });
17
+ *
18
+ * return (
19
+ * <form onSubmit={form.handleSubmit(onSubmit)}>
20
+ * {form.fields.map(({ name, config, editable }) => (
21
+ * <Input key={name} label={config.label} {...form.register(name)} />
22
+ * ))}
23
+ * </form>
24
+ * );
25
+ * }
26
+ * ```
27
+ *
28
+ * @version 0.0.1
29
+ * @since 0.0.1
30
+ * @author AMBROISE PARK Consulting
31
+ */
32
+ export { useEntityForm } from './hooks/useEntityForm';
33
+ export { useEntityField } from './hooks/useEntityField';
34
+ export { isFieldEditable, normalizeToFieldConfig, getFieldsForOperation, createEntitySchema, validateEntity, } from './utils';
35
+ export type { ViewerRole, RenderableField, GetFieldsForOperationOptions, EntityFieldsInput, SchemaOperation, ValidationIssue, ValidationResult, } from './utils';
36
+ export type { InferEntityData, InferEntityInput, InferEntityOutput, UseEntityFormOptions, EntityFormReturn, EntityFieldReturn, Entity, EntityField, FieldConfig, FieldType, Visibility, Editable, ValidationRules, dndevSchema, } from './types';
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/forms/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGxD,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,YAAY,EAEV,UAAU,EACV,eAAe,EACf,4BAA4B,EAC5B,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAEV,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EAEjB,MAAM,EACN,WAAW,EACX,WAAW,EACX,SAAS,EACT,UAAU,EACV,QAAQ,EACR,eAAe,EACf,WAAW,GACZ,MAAM,SAAS,CAAC"}
@@ -0,0 +1 @@
1
+ import{useEntityForm as i}from"./hooks/useEntityForm";import{useEntityField as r}from"./hooks/useEntityField";import{isFieldEditable as l,normalizeToFieldConfig as n,getFieldsForOperation as d,createEntitySchema as m,validateEntity as F}from"./utils";export{m as createEntitySchema,d as getFieldsForOperation,l as isFieldEditable,n as normalizeToFieldConfig,r as useEntityField,i as useEntityForm,F as validateEntity};
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @fileoverview Form type exports
3
+ * @description Type utilities for type-safe form building with entity definitions.
4
+ *
5
+ * @version 0.0.1
6
+ * @since 0.0.1
7
+ * @author AMBROISE PARK Consulting
8
+ */
9
+ import type { UseFormReturn, ControllerRenderProps, FieldPath } from 'react-hook-form';
10
+ import type { Entity, EntityField, FieldConfig, FieldType, FieldTypeToValue, Visibility, Editable, ValidationRules, dndevSchema } from '@donotdev/core';
11
+ import type { RenderableField, ViewerRole } from './utils';
12
+ export type { Entity, EntityField, FieldConfig, FieldType, Visibility, Editable, ValidationRules, dndevSchema, RenderableField, ViewerRole, };
13
+ /**
14
+ * Infers the data type from an entity definition.
15
+ *
16
+ * Maps each field in the entity to its corresponding TypeScript type
17
+ * based on the field's `type` property.
18
+ *
19
+ * @template E - Entity type from defineEntity()
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const productEntity = defineEntity({
24
+ * name: 'Product',
25
+ * collection: 'products',
26
+ * fields: {
27
+ * name: { type: 'text', visibility: 'guest' },
28
+ * price: { type: 'number', visibility: 'guest' },
29
+ * active: { type: 'boolean', visibility: 'user' }
30
+ * }
31
+ * });
32
+ *
33
+ * type ProductData = InferEntityData<typeof productEntity>;
34
+ * // { name: string; price: number; active: boolean; id: string; ... }
35
+ * ```
36
+ *
37
+ * @version 0.0.1
38
+ * @since 0.0.1
39
+ * @author AMBROISE PARK Consulting
40
+ */
41
+ export type InferEntityData<E extends Entity> = {
42
+ [K in keyof E['fields']]: E['fields'][K] extends {
43
+ type: infer T;
44
+ } ? T extends FieldType ? FieldTypeToValue[T] : unknown : unknown;
45
+ };
46
+ /**
47
+ * Infers form input type (values before validation).
48
+ * All fields are optional/partial since form may be incomplete.
49
+ *
50
+ * @template E - Entity type from defineEntity()
51
+ *
52
+ * @version 0.0.1
53
+ * @since 0.0.1
54
+ * @author AMBROISE PARK Consulting
55
+ */
56
+ export type InferEntityInput<E extends Entity> = Partial<InferEntityData<E>>;
57
+ /**
58
+ * Infers form output type (values after validation).
59
+ * Represents validated, complete data ready for submission.
60
+ *
61
+ * @template E - Entity type from defineEntity()
62
+ *
63
+ * @version 0.0.1
64
+ * @since 0.0.1
65
+ * @author AMBROISE PARK Consulting
66
+ */
67
+ export type InferEntityOutput<E extends Entity> = InferEntityData<E>;
68
+ /**
69
+ * Options for useEntityForm hook.
70
+ *
71
+ * @template E - Entity type from defineEntity()
72
+ *
73
+ * @version 0.0.1
74
+ * @since 0.0.1
75
+ * @author AMBROISE PARK Consulting
76
+ */
77
+ export interface UseEntityFormOptions<E extends Entity> {
78
+ /**
79
+ * Form operation type.
80
+ * - 'create': Excludes technical fields, all other fields editable
81
+ * - 'edit': Shows all fields from defaultValues, technical fields read-only
82
+ * @default 'create' (or 'edit' if defaultValues provided)
83
+ */
84
+ operation?: 'create' | 'edit';
85
+ /**
86
+ * Initial form values.
87
+ * For edit mode, should come from backend (already visibility-filtered).
88
+ */
89
+ defaultValues?: Partial<InferEntityData<E>>;
90
+ /**
91
+ * Viewer role for editability checks.
92
+ * Determines which fields render as inputs vs read-only.
93
+ * @default 'admin'
94
+ */
95
+ viewerRole?: ViewerRole;
96
+ /**
97
+ * React Hook Form mode.
98
+ * @default 'onSubmit'
99
+ */
100
+ mode?: 'onBlur' | 'onChange' | 'onSubmit' | 'onTouched' | 'all';
101
+ /**
102
+ * Translation function for field labels.
103
+ * If not provided, uses entity labels or generates from field names.
104
+ */
105
+ t?: (key: string, options?: Record<string, unknown>) => string;
106
+ /**
107
+ * Enable auto-save to localStorage for crash recovery.
108
+ * Saves form state every 3 seconds (debounced) with multi-tab sync.
109
+ * Auto-generates key: `<entity-name>-form-draft`
110
+ * Auto-cleans up on successful submit.
111
+ * @default false
112
+ */
113
+ autoSave?: boolean;
114
+ }
115
+ /**
116
+ * Return type for useEntityForm hook.
117
+ *
118
+ * Extends React Hook Form's UseFormReturn with entity-aware additions.
119
+ *
120
+ * @template E - Entity type from defineEntity()
121
+ *
122
+ * @version 0.0.1
123
+ * @since 0.0.1
124
+ * @author AMBROISE PARK Consulting
125
+ */
126
+ export interface EntityFormReturn<E extends Entity> extends UseFormReturn<InferEntityData<E>> {
127
+ /**
128
+ * Pre-filtered fields ready for rendering.
129
+ * Already filtered by operation and visibility.
130
+ */
131
+ fields: RenderableField[];
132
+ /**
133
+ * Current operation type.
134
+ */
135
+ operation: 'create' | 'edit';
136
+ /**
137
+ * Original entity definition.
138
+ */
139
+ entity: E;
140
+ /**
141
+ * Translation function for labels.
142
+ */
143
+ t: (key: string, options?: Record<string, unknown>) => string;
144
+ /**
145
+ * Current viewer role.
146
+ */
147
+ viewerRole: ViewerRole;
148
+ }
149
+ /**
150
+ * Return type for useEntityField hook.
151
+ *
152
+ * @template E - Entity type from defineEntity()
153
+ * @template K - Field key from entity
154
+ *
155
+ * @version 0.0.1
156
+ * @since 0.0.1
157
+ * @author AMBROISE PARK Consulting
158
+ */
159
+ export interface EntityFieldReturn<E extends Entity, K extends keyof InferEntityData<E>> {
160
+ /**
161
+ * React Hook Form field props (value, onChange, onBlur, etc.)
162
+ */
163
+ field: ControllerRenderProps<InferEntityData<E>, FieldPath<InferEntityData<E>>>;
164
+ /**
165
+ * Field metadata (error, touched, dirty state).
166
+ */
167
+ meta: {
168
+ error: string | undefined;
169
+ touched: boolean;
170
+ isDirty: boolean;
171
+ };
172
+ /**
173
+ * Normalized field configuration.
174
+ */
175
+ config: FieldConfig;
176
+ /**
177
+ * Whether field is editable (based on role, operation, config).
178
+ */
179
+ isEditable: boolean;
180
+ /**
181
+ * Whether field is visible (based on filtering).
182
+ */
183
+ isVisible: boolean;
184
+ }
185
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/forms/types.ts"],"names":[],"mappings":"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"}
File without changes
@@ -0,0 +1,53 @@
1
+ import type { Entity, dndevSchema } from '@donotdev/core';
2
+ /**
3
+ * Schema operation type
4
+ * - 'create': Makes technical fields optional (backend creates them automatically)
5
+ * - 'update': Makes all fields optional except id (for partial updates). Technical fields can be updated if provided.
6
+ * - 'full': Includes all fields as-is
7
+ * - 'draft': Like create, but all required fields become optional (for saving drafts)
8
+ */
9
+ export type SchemaOperation = 'create' | 'update' | 'full' | 'draft';
10
+ /**
11
+ * Creates a Valibot schema from an entity definition.
12
+ *
13
+ * Handles visibility and operation type to generate appropriate schemas:
14
+ * - 'create': Makes technical fields optional (backend creates them automatically: id, timestamps, status)
15
+ * - 'update': Makes all fields optional except id. Technical fields can be updated if provided.
16
+ * - 'full': Includes all fields as-is
17
+ * - 'draft': Like create, but all required fields become optional (for saving drafts)
18
+ *
19
+ * @param entity - Entity definition from defineEntity()
20
+ * @param operation - Schema operation type
21
+ * @returns Valibot schema with dndev metadata
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { createEntitySchema } from '@donotdev/crud/forms';
26
+ * import { productEntity } from './entities/product';
27
+ *
28
+ * // For create form validation (full required validation)
29
+ * const createSchema = createEntitySchema(productEntity, 'create');
30
+ *
31
+ * // For draft saves (all required fields optional)
32
+ * const draftSchema = createEntitySchema(productEntity, 'draft');
33
+ *
34
+ * // For update form validation
35
+ * const updateSchema = createEntitySchema(productEntity, 'update');
36
+ *
37
+ * // Use with React Hook Form
38
+ * import { valibotResolver } from '@hookform/resolvers/valibot';
39
+ *
40
+ * const form = useForm({
41
+ * resolver: valibotResolver(createSchema)
42
+ * });
43
+ * ```
44
+ *
45
+ * @see {@link Entity} for entity definition structure
46
+ * @see {@link SchemaOperation} for operation types
47
+ *
48
+ * @version 0.0.1
49
+ * @since 0.0.1
50
+ * @author AMBROISE PARK Consulting
51
+ */
52
+ export declare function createEntitySchema<E extends Entity>(entity: E, operation?: SchemaOperation): dndevSchema<Record<string, unknown>>;
53
+ //# sourceMappingURL=createEntitySchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createEntitySchema.d.ts","sourceRoot":"","sources":["../../../src/forms/utils/createEntitySchema.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAe,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAyIvE;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EACjD,MAAM,EAAE,CAAC,EACT,SAAS,GAAE,eAA0B,GACpC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAkDtC"}
@@ -0,0 +1 @@
1
+ import*as e from"valibot";import{TECHNICAL_FIELD_NAMES as m}from"@donotdev/core";function f(n,t={}){const{type:l,validation:a}=n,{isDraft:s=!1}=t;let i;switch(l){case"number":case"range":i=e.number(),a?.min!==void 0&&(i=e.pipe(i,e.minValue(a.min))),a?.max!==void 0&&(i=e.pipe(i,e.maxValue(a.max)));break;case"boolean":case"checkbox":i=e.boolean();break;case"email":i=e.pipe(e.string(),e.email());break;case"url":i=e.pipe(e.string(),e.url());break;case"multiselect":case"array":i=e.array(e.string());break;case"select":case"radio":if(a?.options&&Array.isArray(a.options)){const c=a.options.map(o=>o.value);i=e.picklist(c)}else i=e.string();break;case"date":case"datetime-local":case"time":case"month":case"week":case"timestamp":i=e.string();break;case"image":i=e.object({fullUrl:e.string(),thumbUrl:e.optional(e.string())});break;case"images":i=e.array(e.object({fullUrl:e.string(),thumbUrl:e.optional(e.string())}));break;default:i=e.string(),a?.minLength!==void 0&&(i=e.pipe(i,e.minLength(a.minLength))),a?.maxLength!==void 0&&(i=e.pipe(i,e.maxLength(a.maxLength))),a?.pattern&&(i=e.pipe(i,e.regex(new RegExp(a.pattern))));break}return(!a?.required||s)&&(i=e.optional(i)),i}function d(n,t="create"){const l=t==="draft",a={};for(const[i,c]of Object.entries(n.fields)){if(c.visibility==="hidden")continue;const o=m.includes(i);let r=f(c,{isDraft:l});(t==="create"||t==="draft")&&o&&(r=e.optional(r)),t==="update"&&i!=="id"&&(r=e.optional(r)),a[i]=r}t==="update"&&(a.id=e.pipe(e.string(),e.minLength(1,"ID is required")));const s=e.object(a);return s.metadata={collection:n.collection,entity:n.name},s}export{d as createEntitySchema};