@donotdev/crud 0.0.31 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +3 -3
- package/dist/CrudService.d.ts +13 -4
- package/dist/CrudService.d.ts.map +1 -1
- package/dist/CrudService.js +2 -2
- package/dist/CrudStore.d.ts +1 -1
- package/dist/CrudStore.d.ts.map +1 -1
- package/dist/CrudStore.js +1 -1
- package/dist/FieldRegistry.d.ts +2 -12
- package/dist/FieldRegistry.d.ts.map +1 -1
- package/dist/FieldRegistry.js +1 -1
- package/dist/adapters/FunctionsAdapter.d.ts +3 -3
- package/dist/adapters/FunctionsAdapter.d.ts.map +1 -1
- package/dist/adapters/FunctionsAdapter.js +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/builtinFieldTypes.d.ts +1 -1
- package/dist/components/CrudButton.d.ts +1 -0
- package/dist/components/CrudButton.d.ts.map +1 -1
- package/dist/components/CrudButton.js +1 -1
- package/dist/components/CrudCard.d.ts +1 -1
- package/dist/components/CrudCard.d.ts.map +1 -1
- package/dist/components/CrudCard.js +1 -1
- package/dist/components/DateFilter.js +1 -1
- package/dist/components/DisplayFieldRenderer.d.ts +2 -1
- package/dist/components/DisplayFieldRenderer.d.ts.map +1 -1
- package/dist/components/DisplayFieldRenderer.js +1 -1
- package/dist/components/DisplayThumbnail.d.ts +1 -9
- package/dist/components/DisplayThumbnail.d.ts.map +1 -1
- package/dist/components/DisplayThumbnail.js +1 -1
- package/dist/components/EntityFilters.d.ts +1 -0
- package/dist/components/EntityFilters.d.ts.map +1 -1
- package/dist/components/EntityFilters.js +1 -1
- package/dist/components/FormFieldRenderer.d.ts +1 -0
- package/dist/components/FormFieldRenderer.d.ts.map +1 -1
- package/dist/components/FormFieldRenderer.js +1 -1
- package/dist/components/FormLayout.d.ts +2 -1
- package/dist/components/FormLayout.d.ts.map +1 -1
- package/dist/components/FormLayout.js +1 -1
- package/dist/components/controlled/complex/ControlledAddressField.js +1 -1
- package/dist/components/controlled/complex/ControlledDateField.d.ts.map +1 -1
- package/dist/components/controlled/complex/ControlledDateField.js +1 -1
- package/dist/components/controlled/complex/ControlledFieldArrayField.js +1 -1
- package/dist/components/controlled/complex/ControlledGeoPointField.js +1 -1
- package/dist/components/controlled/complex/ControlledMapField.js +1 -1
- package/dist/components/controlled/complex/ControlledMultiInputField.js +1 -1
- package/dist/components/controlled/complex/ControlledRichTextField.js +1 -1
- package/dist/components/controlled/complex/ControlledTimestampField.d.ts.map +1 -1
- package/dist/components/controlled/complex/ControlledTimestampField.js +1 -1
- package/dist/components/controlled/complex/index.js +1 -1
- package/dist/components/controlled/file/ControlledDocumentField.js +1 -1
- package/dist/components/controlled/file/ControlledFileField.js +1 -1
- package/dist/components/controlled/file/ControlledImageField.js +1 -1
- package/dist/components/controlled/file/ControlledMultiDocumentField.js +1 -1
- package/dist/components/controlled/file/ControlledMultiFileField.js +1 -1
- package/dist/components/controlled/file/ControlledMultiImageField.js +1 -1
- package/dist/components/controlled/file/index.js +1 -1
- package/dist/components/controlled/index.js +1 -1
- package/dist/components/controlled/input/ControlledCheckboxField.js +1 -1
- package/dist/components/controlled/input/ControlledCurrencyField.js +1 -1
- package/dist/components/controlled/input/ControlledDurationField.js +1 -1
- package/dist/components/controlled/input/ControlledGdprConsentField.js +1 -1
- package/dist/components/controlled/input/ControlledNumberField.js +1 -1
- package/dist/components/controlled/input/ControlledPasswordField.js +1 -1
- package/dist/components/controlled/input/ControlledPhoneField.js +1 -1
- package/dist/components/controlled/input/ControlledPriceField.js +1 -1
- package/dist/components/controlled/input/ControlledRangeField.js +1 -1
- package/dist/components/controlled/input/ControlledRatingField.js +1 -1
- package/dist/components/controlled/input/ControlledSwitchField.js +1 -1
- package/dist/components/controlled/input/ControlledTextField.d.ts +5 -5
- package/dist/components/controlled/input/ControlledTextField.d.ts.map +1 -1
- package/dist/components/controlled/input/ControlledTextField.js +1 -1
- package/dist/components/controlled/input/ControlledTextareaField.js +1 -1
- package/dist/components/controlled/input/index.js +1 -1
- package/dist/components/controlled/select/ControlledComboboxField.js +1 -1
- package/dist/components/controlled/select/ControlledDropdownField.d.ts +2 -4
- package/dist/components/controlled/select/ControlledDropdownField.d.ts.map +1 -1
- package/dist/components/controlled/select/ControlledDropdownField.js +1 -1
- package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts +2 -4
- package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts.map +1 -1
- package/dist/components/controlled/select/ControlledMultiDropdownField.js +1 -1
- package/dist/components/controlled/select/ControlledRadioField.d.ts +2 -4
- package/dist/components/controlled/select/ControlledRadioField.d.ts.map +1 -1
- package/dist/components/controlled/select/ControlledRadioField.js +1 -1
- package/dist/components/controlled/select/ControlledReferenceField.js +1 -1
- package/dist/components/controlled/select/ControlledYearField.js +1 -1
- package/dist/components/controlled/select/index.js +1 -1
- package/dist/components/controlled/types.js +1 -1
- package/dist/components/fields/display/AvatarFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/AvatarFieldDisplay.js +1 -1
- package/dist/components/fields/display/BadgeFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/BadgeFieldDisplay.js +1 -1
- package/dist/components/fields/display/ButtonFieldDisplay.d.ts +2 -2
- package/dist/components/fields/display/ButtonFieldDisplay.js +1 -1
- package/dist/components/fields/display/CheckboxFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/CheckboxFieldDisplay.js +1 -1
- package/dist/components/fields/display/DateFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/DateFieldDisplay.d.ts.map +1 -1
- package/dist/components/fields/display/DateFieldDisplay.js +1 -1
- package/dist/components/fields/display/DropdownDisplay.d.ts +1 -1
- package/dist/components/fields/display/DropdownDisplay.js +1 -1
- package/dist/components/fields/display/FileFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/FileFieldDisplay.js +1 -1
- package/dist/components/fields/display/GeoPointFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/GeoPointFieldDisplay.js +1 -1
- package/dist/components/fields/display/HiddenFieldDisplay.d.ts +2 -2
- package/dist/components/fields/display/HiddenFieldDisplay.js +1 -1
- package/dist/components/fields/display/ImageFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/ImageFieldDisplay.js +1 -1
- package/dist/components/fields/display/LinkFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/LinkFieldDisplay.js +1 -1
- package/dist/components/fields/display/MapFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/MapFieldDisplay.js +1 -1
- package/dist/components/fields/display/MultiDropdownDisplay.d.ts +1 -1
- package/dist/components/fields/display/MultiDropdownDisplay.js +1 -1
- package/dist/components/fields/display/MultiInputTextFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/MultiInputTextFieldDisplay.js +1 -1
- package/dist/components/fields/display/NumberFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/NumberFieldDisplay.d.ts.map +1 -1
- package/dist/components/fields/display/NumberFieldDisplay.js +1 -1
- package/dist/components/fields/display/PasswordFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/PasswordFieldDisplay.js +1 -1
- package/dist/components/fields/display/PhoneNumberDisplay.d.ts +1 -1
- package/dist/components/fields/display/PhoneNumberDisplay.js +1 -1
- package/dist/components/fields/display/RadioFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/RadioFieldDisplay.js +1 -1
- package/dist/components/fields/display/RangeFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/RangeFieldDisplay.js +1 -1
- package/dist/components/fields/display/ReferenceFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/ReferenceFieldDisplay.js +1 -1
- package/dist/components/fields/display/RichTextDisplay.d.ts +1 -1
- package/dist/components/fields/display/RichTextDisplay.js +2 -2
- package/dist/components/fields/display/TextAreaDisplay.d.ts +1 -1
- package/dist/components/fields/display/TextAreaDisplay.js +1 -1
- package/dist/components/fields/display/TextFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/TextFieldDisplay.js +1 -1
- package/dist/components/fields/display/TimestampFieldDisplay.d.ts +1 -1
- package/dist/components/fields/display/TimestampFieldDisplay.d.ts.map +1 -1
- package/dist/components/fields/display/TimestampFieldDisplay.js +1 -1
- package/dist/components/fields/display/index.d.ts +1 -1
- package/dist/components/fields/display/index.js +1 -1
- package/dist/components/form/fields/AddressFieldComponent.js +1 -1
- package/dist/components/form/fields/AvatarFieldComponent.d.ts +2 -2
- package/dist/components/form/fields/AvatarFieldComponent.js +1 -1
- package/dist/components/form/fields/BadgeFieldComponent.d.ts +2 -2
- package/dist/components/form/fields/BadgeFieldComponent.js +1 -1
- package/dist/components/form/fields/ButtonFieldComponent.js +1 -1
- package/dist/components/form/fields/CheckboxFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/CheckboxFieldComponent.js +1 -1
- package/dist/components/form/fields/ComboboxComponent.d.ts +1 -1
- package/dist/components/form/fields/ComboboxComponent.js +1 -1
- package/dist/components/form/fields/CurrencyFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/CurrencyFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/CurrencyFieldComponent.js +1 -1
- package/dist/components/form/fields/DateFieldComponent.d.ts +3 -3
- package/dist/components/form/fields/DateFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/DateFieldComponent.js +1 -1
- package/dist/components/form/fields/DocumentFieldComponent.js +1 -1
- package/dist/components/form/fields/DropdownComponent.d.ts +1 -1
- package/dist/components/form/fields/DropdownComponent.js +1 -1
- package/dist/components/form/fields/DurationFieldComponent.js +1 -1
- package/dist/components/form/fields/FileFieldComponent.js +1 -1
- package/dist/components/form/fields/GdprConsentFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/GdprConsentFieldComponent.js +1 -1
- package/dist/components/form/fields/GeoPointFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/GeoPointFieldComponent.js +1 -1
- package/dist/components/form/fields/HiddenFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/HiddenFieldComponent.js +1 -1
- package/dist/components/form/fields/ImageFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/ImageFieldComponent.js +1 -1
- package/dist/components/form/fields/MapFieldComponent.js +1 -1
- package/dist/components/form/fields/MultiDropdownComponent.d.ts +1 -1
- package/dist/components/form/fields/MultiDropdownComponent.js +1 -1
- package/dist/components/form/fields/MultiInputTextFieldComponent.js +1 -1
- package/dist/components/form/fields/NumberFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/NumberFieldComponent.js +1 -1
- package/dist/components/form/fields/PasswordFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/PasswordFieldComponent.js +1 -1
- package/dist/components/form/fields/PhoneNumberComponent.d.ts +1 -1
- package/dist/components/form/fields/PhoneNumberComponent.js +1 -1
- package/dist/components/form/fields/PriceFieldComponent.js +1 -1
- package/dist/components/form/fields/RadioFieldComponent.js +1 -1
- package/dist/components/form/fields/RangeFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/RangeFieldComponent.js +1 -1
- package/dist/components/form/fields/RatingFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/RatingFieldComponent.js +1 -1
- package/dist/components/form/fields/ReferenceFieldComponent.js +1 -1
- package/dist/components/form/fields/RichTextComponent.d.ts +1 -1
- package/dist/components/form/fields/RichTextComponent.js +1 -1
- package/dist/components/form/fields/SwitchFieldComponent.d.ts +1 -1
- package/dist/components/form/fields/SwitchFieldComponent.js +1 -1
- package/dist/components/form/fields/TextAreaComponent.d.ts +1 -1
- package/dist/components/form/fields/TextAreaComponent.js +1 -1
- package/dist/components/form/fields/TextFieldComponent.d.ts +4 -4
- package/dist/components/form/fields/TextFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/TextFieldComponent.js +1 -1
- package/dist/components/form/fields/TimestampFieldComponent.d.ts +2 -2
- package/dist/components/form/fields/TimestampFieldComponent.d.ts.map +1 -1
- package/dist/components/form/fields/TimestampFieldComponent.js +1 -1
- package/dist/components/form/fields/index.d.ts +1 -1
- package/dist/components/form/fields/index.js +1 -1
- package/dist/components/form/fields/internal/TiptapEditor.d.ts +1 -1
- package/dist/components/form/fields/internal/TiptapEditor.js +2 -2
- package/dist/components/form/fields/types.d.ts +1 -1
- package/dist/components/form/index.d.ts +1 -1
- package/dist/components/form/internal/ImageViewerDialog.js +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/contexts/UploadContext.d.ts +1 -0
- package/dist/contexts/UploadContext.d.ts.map +1 -1
- package/dist/contexts/UploadContext.js +1 -1
- package/dist/contexts/index.js +1 -1
- package/dist/fieldTypeRegistry.d.ts.map +1 -1
- package/dist/fieldTypeRegistry.js +1 -1
- package/dist/fieldTypeRegistry.store.d.ts.map +1 -1
- package/dist/fieldTypeRegistry.store.js +1 -1
- package/dist/fieldTypeRegistry.types.d.ts +4 -0
- package/dist/fieldTypeRegistry.types.d.ts.map +1 -1
- package/dist/forms/hooks/index.d.ts +1 -1
- package/dist/forms/hooks/index.js +1 -1
- package/dist/forms/hooks/useController.js +1 -1
- package/dist/forms/hooks/useEntityField.d.ts +1 -1
- package/dist/forms/hooks/useEntityField.js +1 -1
- package/dist/forms/hooks/useEntityForm.d.ts.map +1 -1
- package/dist/forms/hooks/useEntityForm.js +1 -1
- package/dist/forms/index.d.ts +1 -1
- package/dist/forms/index.js +1 -1
- package/dist/forms/types.d.ts +12 -7
- package/dist/forms/types.d.ts.map +1 -1
- package/dist/forms/utils/buildInitialValues.d.ts.map +1 -1
- package/dist/forms/utils/buildInitialValues.js +1 -1
- package/dist/forms/utils/getFieldsForOperation.d.ts +2 -2
- package/dist/forms/utils/getFieldsForOperation.d.ts.map +1 -1
- package/dist/forms/utils/getFieldsForOperation.js +1 -1
- package/dist/forms/utils/index.d.ts +1 -1
- package/dist/forms/utils/index.js +1 -1
- package/dist/forms/utils/isFieldEditable.d.ts +8 -2
- package/dist/forms/utils/isFieldEditable.d.ts.map +1 -1
- package/dist/forms/utils/isFieldEditable.js +1 -1
- package/dist/forms/utils/translateFieldLabel.d.ts +3 -3
- package/dist/forms/utils/translateFieldLabel.js +1 -1
- package/dist/forms/utils/validateEntity.d.ts +1 -1
- package/dist/forms/utils/validateEntity.js +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/useCrudFilters.d.ts +2 -0
- package/dist/hooks/useCrudFilters.d.ts.map +1 -1
- package/dist/hooks/useCrudFilters.js +1 -1
- package/dist/hooks/useCrudPageSize.d.ts +29 -0
- package/dist/hooks/useCrudPageSize.d.ts.map +1 -0
- package/dist/hooks/useCrudPageSize.js +1 -0
- package/dist/hooks/useEntityFavorites.d.ts +2 -0
- package/dist/hooks/useEntityFavorites.d.ts.map +1 -1
- package/dist/hooks/useEntityFavorites.js +1 -1
- package/dist/hooks/useFieldConditions.js +1 -1
- package/dist/hooks/useFileUpload.d.ts.map +1 -1
- package/dist/hooks/useFileUpload.js +1 -1
- package/dist/hooks/useReferenceResolver.d.ts +1 -1
- package/dist/hooks/useReferenceResolver.d.ts.map +1 -1
- package/dist/hooks/useReferenceResolver.js +1 -1
- package/dist/hooks/useRelatedItems.d.ts +2 -0
- package/dist/hooks/useRelatedItems.d.ts.map +1 -1
- package/dist/hooks/useRelatedItems.js +1 -1
- package/dist/hooks/useUnsavedChangesWarning.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/registerBuiltinFieldTypes.d.ts.map +1 -1
- package/dist/registerBuiltinFieldTypes.js +1 -1
- package/dist/stores/FormStore.d.ts +2 -0
- package/dist/stores/FormStore.d.ts.map +1 -1
- package/dist/stores/FormStore.js +1 -1
- package/dist/stores/UploadStore.d.ts +9 -3
- package/dist/stores/UploadStore.d.ts.map +1 -1
- package/dist/stores/UploadStore.js +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.js +1 -1
- package/dist/symbol-index.json +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +10 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/useBaseCrudList.js +1 -1
- package/dist/useCrud.d.ts +1 -0
- package/dist/useCrud.d.ts.map +1 -1
- package/dist/useCrud.js +1 -1
- package/dist/useCrudCardList.d.ts +5 -3
- package/dist/useCrudCardList.d.ts.map +1 -1
- package/dist/useCrudCardList.js +1 -1
- package/dist/useCrudList.d.ts +5 -3
- package/dist/useCrudList.d.ts.map +1 -1
- package/dist/useCrudList.js +1 -1
- package/dist/utils/clientListProcessing.d.ts +22 -3
- package/dist/utils/clientListProcessing.d.ts.map +1 -1
- package/dist/utils/clientListProcessing.js +1 -1
- package/dist/utils/collections.d.ts +6 -6
- package/dist/utils/collections.js +1 -1
- package/dist/utils/fileStorage.d.ts +2 -0
- package/dist/utils/fileStorage.d.ts.map +1 -1
- package/dist/utils/fileStorage.js +1 -1
- package/dist/utils/imageProcessing.d.ts +1 -1
- package/dist/utils/imageProcessing.d.ts.map +1 -1
- package/dist/utils/imageProcessing.js +1 -1
- package/dist/utils/imageStorage.d.ts +2 -0
- package/dist/utils/imageStorage.d.ts.map +1 -1
- package/dist/utils/imageStorage.js +1 -1
- package/dist/utils/imageUtils.d.ts +1 -1
- package/dist/utils/imageUtils.js +1 -1
- package/dist/utils/matchesFilter.d.ts.map +1 -1
- package/dist/utils/matchesFilter.js +1 -1
- package/dist/utils/mergeWithOptimistic.js +1 -1
- package/dist/utils/sanitizeHtml.d.ts +0 -5
- package/dist/utils/sanitizeHtml.d.ts.map +1 -1
- package/dist/utils/sanitizeHtml.js +1 -1
- package/dist/utils/scopeUtils.js +1 -1
- package/dist/utils/uploadValidation.d.ts +1 -1
- package/dist/utils/uploadValidation.js +1 -1
- package/dist/workflows/WorkflowPersistence.d.ts +1 -1
- package/dist/workflows/WorkflowPersistence.js +1 -1
- package/dist/workflows/defineWorkflow.d.ts +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js +1 -1
- package/dist/workflows/useEntityWorkflow.js +1 -1
- package/guidelines/COMPONENTS.md +234 -0
- package/guidelines/CRUD.md +340 -0
- package/guidelines/GOTCHAS.md +46 -0
- package/package.json +7 -4
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sanitize HTML using DOMPurify — handles all known XSS vectors including
|
|
3
|
-
* malformed tags, event handlers, javascript: URIs, data: URIs, SVG payloads,
|
|
4
|
-
* CSS injection, and HTML entity encoding bypasses.
|
|
5
|
-
*/
|
|
6
1
|
export declare function sanitizeHtml(html: string): string;
|
|
7
2
|
//# sourceMappingURL=sanitizeHtml.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../../src/utils/sanitizeHtml.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../../src/utils/sanitizeHtml.ts"],"names":[],"mappings":"AA6DA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import e from"dompurify";const r=["p","h1","h2","h3","h4","h5","h6","blockquote","pre","hr","ul","ol","li","a","strong","b","em","i","u","s","code","br","span","sub","sup","mark","table","thead","tbody","tr","th","td","img","div","figure","figcaption","details","summary"],i=["href","target","rel","src","alt","width","height","class","colspan","rowspan"],a=/<\/?(?:embed|source|track|area|wbr)[^>]*>/gi;function n(t){return e.sanitize(t,{ALLOWED_TAGS:r,ALLOWED_ATTR:i}).replace(a,"")}export{n as sanitizeHtml};
|
package/dist/utils/scopeUtils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getScopeValue as
|
|
1
|
+
import{getScopeValue as o,CRUD_OPERATORS as p}from"@donotdev/core";function c(t,e){if(!e)return t;const r=o(e.provider);if(!r)throw new Error(`[CRUD] Scope provider "${e.provider}" returned null. Cannot write scoped entity without a scope value. Ensure a scope is selected before creating scoped entities.`);return{...t,[e.field]:r}}function l(t,e){const r=t??{};if(!e||typeof e.field!="string")return r;const i=o(e.provider);if(!i)throw new Error(`[CRUD] Scope provider "${e.provider}" returned null. Cannot query scoped entity without a scope value. Ensure a scope is selected before querying scoped entities.`);const n={field:e.field,operator:p.EQ,value:i};return{...r,where:r.where?[...r.where,n]:[n]}}function s(t){return t?o(t.provider):null}export{s as getCurrentScopeValue,c as injectScope,l as injectScopeFilter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";function
|
|
1
|
+
"use client";function s(r){return!r||typeof r!="string"?!1:r.startsWith("https://")&&!r.startsWith("blob:")}function c(r){return!(!r||typeof r!="object"||!s(r.fullUrl)||r.thumbUrl&&!s(r.thumbUrl))}function u(r,o=""){const t=[];if(r==null)return t;if(typeof r=="string")return r.startsWith("blob:")&&t.push(o||"root"),t;if(typeof r!="object")return t;if(Array.isArray(r))r.forEach((e,n)=>{const l=o?`${o}[${n}]`:`[${n}]`;t.push(...u(e,l))});else{const e=r;if(e.fullUrl!==void 0&&e.thumbUrl!==void 0){const n=e.fullUrl,l=e.thumbUrl,f=typeof n=="string"&&!s(n),b=l&&typeof l=="string"&&!s(l);if(f||b){const i=o||"root";t.push(`${i}.fullUrl or ${i}.thumbUrl contains blob URL`)}}else Object.entries(e).forEach(([n,l])=>{const f=o?`${o}.${n}`:n;t.push(...u(l,f))})}return t}function h(r){return u(r).length>0}export{u as checkForBlobUrls,h as hasBlobUrls,s as isStorageUrl,c as validatePicture};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const r="dndev-workflow-";function c(t,o){try{const e=`${r}${t}`;localStorage.setItem(e,JSON.stringify(o))}catch{}}function n(t){try{const o=`${r}${t}`,e=localStorage.getItem(o);return e?JSON.parse(e):null}catch{return null}}function a(t){try{const o=`${r}${t}`;localStorage.removeItem(o)}catch{}}export{a as clearWorkflowState,n as loadWorkflowState,c as saveWorkflowState};
|
package/dist/workflows/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineWorkflow as
|
|
1
|
+
import{defineWorkflow as e}from"./defineWorkflow";import{useEntityWorkflow as f}from"./useEntityWorkflow";import{saveWorkflowState as a,loadWorkflowState as k,clearWorkflowState as w}from"./WorkflowPersistence";export{w as clearWorkflowState,e as defineWorkflow,k as loadWorkflowState,a as saveWorkflowState,f as useEntityWorkflow};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{useState as
|
|
1
|
+
"use client";import{useState as b,useCallback as p,useMemo as I,useEffect as G,useRef as L}from"react";import{evaluateCondition as M}from"@donotdev/core";import{saveWorkflowState as K,loadWorkflowState as j,clearWorkflowState as O}from"./WorkflowPersistence";function B(e,D={}){const{onComplete:k,onStepChange:u,defaultValues:y}=D,w=L(k);w.current=k;const[l,v]=b(()=>{if(e.persist){const t=j(e.persistKey??e.id);if(t)return t.stepData}if(y){const t={};for(const r of e.steps)if(r.entity&&r.fields){const o={};for(const i of r.fields)i in y&&(o[i]=y[i]);Object.keys(o).length>0&&(t[r.id]=o)}return t}return{}}),[s,m]=b(()=>{if(e.persist){const t=j(e.persistKey??e.id);if(t)return t.currentStepIndex}return 0}),[x,h]=b(!1),d=I(()=>{const t={};for(const[r,o]of Object.entries(l))for(const[i,a]of Object.entries(o))t[i]=a,t[`${r}.${i}`]=a;return t},[l]),n=I(()=>e.steps.filter(t=>t.conditions?.visible?M(t.conditions.visible,d):!0),[e.steps,d]),W=n[s]??n[0],S=s===0,c=s===n.length-1;G(()=>{e.persist&&K(e.persistKey??e.id,{currentStepIndex:s,stepData:l,savedAt:Date.now()})},[e.persist,e.persistKey,e.id,s,l]);const F=p(async t=>{const r=n[s];if(!r)return!1;if(t&&v(i=>({...i,[r.id]:{...i[r.id],...t}})),r.validation?.validate){const i={...d,...t??{}};if(r.validation.validate(i)!==!0)return!1}if(r.after){const i={};await r.after({data:t??l[r.id]??{},allData:{...d,...t??{}},next:{prefill:a=>{const f=n[s+1];f&&(i[f.id]=a)}}}),Object.keys(i).length>0&&v(a=>{const f={...a};for(const[C,$]of Object.entries(i))f[C]={...f[C],...$};return f})}if(c){h(!0);try{const i={...d,...t??{}},a=w.current??e.onComplete;return a&&await a(i),e.persist&&O(e.persistKey??e.id),!0}catch{return!1}finally{h(!1)}}const o=s+1;return m(o),u?.(s,o),!0},[n,s,d,l,c,e,u]),N=p(()=>{if(S)return;const t=s-1;m(t),u?.(s,t)},[s,S,u]),A=p(()=>{if(!n[s]?.allowSkip||c)return;const r=s+1;m(r),u?.(s,r)},[n,s,c,u]),E=p(t=>{t>=0&&t<n.length&&(u?.(s,t),m(t))},[n.length,s,u]),P=p(()=>{e.persist&&K(e.persistKey??e.id,{currentStepIndex:s,stepData:l,savedAt:Date.now()})},[e,s,l]),R=p(()=>{O(e.persistKey??e.id)},[e]),V=p((t,r)=>{v(o=>({...o,[t]:{...o[t],...r}}))},[]);return{currentStep:W,currentStepIndex:s,visibleSteps:n,stepData:l,allData:d,goNext:F,goPrevious:N,skipStep:A,goToStep:E,isFirst:S,isLast:c,isSubmitting:x,canGoNext:!x,persistNow:P,clearPersisted:R,prefillStep:V}}export{B as useEntityWorkflow};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# CRUD Components Reference
|
|
2
|
+
|
|
3
|
+
**Import:** `@donotdev/crud`
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Hooks
|
|
8
|
+
|
|
9
|
+
### useCrud
|
|
10
|
+
Single document CRUD actions (add, update, delete, get, subscribe).
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
const { add, update, delete: remove, get, data, loading, error } = useCrud(carEntity);
|
|
14
|
+
await add({ name: 'Tesla', year: 2024 });
|
|
15
|
+
await remove('doc-id');
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### useCrudList
|
|
19
|
+
Paginated list with automatic loading. For data tables.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const { items, loading, refresh } = useCrudList(productEntity);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### useCrudCardList
|
|
26
|
+
Card-based list with infinite scroll.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
const { items, loading, refresh } = useCrudCardList(articleEntity);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Form Components
|
|
35
|
+
|
|
36
|
+
### EntityFormRenderer
|
|
37
|
+
Auto-render full entity form from definition.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<EntityFormRenderer
|
|
41
|
+
entity={productEntity}
|
|
42
|
+
operation="create"
|
|
43
|
+
onSubmit={handleSubmit}
|
|
44
|
+
/>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### FormFieldRenderer
|
|
48
|
+
Render single field. Used inside custom forms.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<FormFieldRenderer field={field} control={form.control} />
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### EntityList
|
|
55
|
+
Data table for entity collection.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<EntityList entity={userEntity} onRowClick={(user) => edit(user.id)} />
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### EntityCardList
|
|
62
|
+
Card grid for entity collection.
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<EntityCardList entity={productEntity} renderCard={(item) => <ProductCard {...item} />} />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Form Building Blocks
|
|
71
|
+
|
|
72
|
+
For custom forms, use these low-level utilities:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { useEntityForm, getFieldsForOperation, validateEntity } from '@donotdev/crud';
|
|
76
|
+
|
|
77
|
+
const { register, handleSubmit, fields, formState } = useEntityForm(productEntity, {
|
|
78
|
+
operation: 'create'
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Export | Purpose |
|
|
83
|
+
|--------|---------|
|
|
84
|
+
| `useEntityForm` | React Hook Form wrapper for entities |
|
|
85
|
+
| `useEntityField` | Single field hook |
|
|
86
|
+
| `getFieldsForOperation` | Get editable fields for create/edit |
|
|
87
|
+
| `validateEntity` | Validate data against entity schema |
|
|
88
|
+
| `isFieldEditable` | Check if field is editable |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Field Types (Built-in)
|
|
93
|
+
|
|
94
|
+
Field components are auto-rendered by `FormFieldRenderer`. You don't import them directly.
|
|
95
|
+
|
|
96
|
+
### Text Inputs
|
|
97
|
+
- `text` - Single-line text input
|
|
98
|
+
- `email` - Email input with validation
|
|
99
|
+
- `tel` - Phone number input
|
|
100
|
+
- `url` - URL input
|
|
101
|
+
- `color` - Color picker
|
|
102
|
+
- `password` - Password input (masked)
|
|
103
|
+
- `textarea` - Multi-line text input
|
|
104
|
+
- `richtext` - Rich text editor
|
|
105
|
+
|
|
106
|
+
### Numbers
|
|
107
|
+
- `number` - Numeric input
|
|
108
|
+
- `range` - Slider input
|
|
109
|
+
- `rating` - Star rating input (1-5, configurable max)
|
|
110
|
+
|
|
111
|
+
**Rating + comment (e.g. reviews):** Use two fields on the same entity - `rating` (type `rating`) for stars and `comment` (type `textarea`) for the text. The form renders them as separate rows; no composite field type needed.
|
|
112
|
+
|
|
113
|
+
### Boolean
|
|
114
|
+
- `checkbox` - Checkbox input
|
|
115
|
+
- `boolean` - Alias for checkbox
|
|
116
|
+
- `switch` - Toggle switch
|
|
117
|
+
|
|
118
|
+
### Dates & Time
|
|
119
|
+
- `date` - Date picker
|
|
120
|
+
- `datetime-local` - Date and time picker
|
|
121
|
+
- `time` - Time picker
|
|
122
|
+
- `week` - Week picker
|
|
123
|
+
- `month` - Month picker
|
|
124
|
+
- `timestamp` - Timestamp (Firestore Timestamp)
|
|
125
|
+
|
|
126
|
+
### Selection
|
|
127
|
+
- `select` - Dropdown select
|
|
128
|
+
- `combobox` - Searchable dropdown
|
|
129
|
+
- `multiselect` - Multiple selection dropdown
|
|
130
|
+
- `radio` - Radio button group
|
|
131
|
+
|
|
132
|
+
### Files & Media
|
|
133
|
+
- `file` - Single file upload
|
|
134
|
+
- `files` - Multiple file uploads
|
|
135
|
+
- `document` - Document upload (PDF, etc.)
|
|
136
|
+
- `documents` - Multiple document uploads
|
|
137
|
+
- `image` - Single image upload
|
|
138
|
+
- `images` - Multiple image uploads
|
|
139
|
+
|
|
140
|
+
### Complex Types
|
|
141
|
+
- `geopoint` - Geographic coordinates (lat/lng)
|
|
142
|
+
- `address` - Address input with autocomplete
|
|
143
|
+
- `map` - Map picker
|
|
144
|
+
- `array` - Array of text inputs
|
|
145
|
+
|
|
146
|
+
### Special
|
|
147
|
+
- `avatar` - Avatar image upload
|
|
148
|
+
- `badge` - Badge display
|
|
149
|
+
- `hidden` - Hidden field (not displayed)
|
|
150
|
+
- `submit` - Submit button (uncontrolled)
|
|
151
|
+
- `reset` - Reset button (uncontrolled)
|
|
152
|
+
|
|
153
|
+
To add custom field types:
|
|
154
|
+
```tsx
|
|
155
|
+
import { useController, registerFieldType } from '@donotdev/crud';
|
|
156
|
+
import type { ControlledFieldProps } from '@donotdev/crud';
|
|
157
|
+
|
|
158
|
+
// Custom controlled component MUST use framework's useController (not react-hook-form's)
|
|
159
|
+
function ScoreField({
|
|
160
|
+
fieldConfig,
|
|
161
|
+
control,
|
|
162
|
+
errors,
|
|
163
|
+
t,
|
|
164
|
+
onChange
|
|
165
|
+
}: ControlledFieldProps) {
|
|
166
|
+
// REQUIRED: Use framework's useController - ensures type compatibility
|
|
167
|
+
const { field, fieldState } = useController({
|
|
168
|
+
name: fieldConfig.name,
|
|
169
|
+
control: control,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div>
|
|
174
|
+
<label>{t(fieldConfig.label)}</label>
|
|
175
|
+
{/* Use field.value and field.onChange */}
|
|
176
|
+
<input
|
|
177
|
+
type="number"
|
|
178
|
+
value={field.value ?? 0}
|
|
179
|
+
onChange={(e) => {
|
|
180
|
+
const value = Number(e.target.value);
|
|
181
|
+
field.onChange(value);
|
|
182
|
+
onChange?.(value);
|
|
183
|
+
}}
|
|
184
|
+
min={0}
|
|
185
|
+
max={10}
|
|
186
|
+
/>
|
|
187
|
+
{fieldState?.error && (
|
|
188
|
+
<span className="error">{fieldState.error.message}</span>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
registerFieldType({
|
|
195
|
+
type: 'score',
|
|
196
|
+
controlledComponent: ScoreField,
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Important:**
|
|
201
|
+
- Custom controlled components receive `control` prop, NOT `field` prop
|
|
202
|
+
- You must use **framework's `useController`** (from `@donotdev/crud`), NOT `react-hook-form`'s useController
|
|
203
|
+
- This ensures type compatibility - no type assertions needed
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Service & Store
|
|
208
|
+
|
|
209
|
+
Direct access (rarely needed):
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import { getCrudService, useCrudStore } from '@donotdev/crud';
|
|
213
|
+
|
|
214
|
+
const service = getCrudService();
|
|
215
|
+
await service.query('products', { where: [['active', '==', true]] });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Collection Utilities
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
import { loadDeterministicRange, upsertDeterministic, appendToCollection } from '@donotdev/crud';
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
| Utility | Purpose |
|
|
227
|
+
|---------|---------|
|
|
228
|
+
| `loadDeterministicRange` | Paginated loading with deterministic IDs |
|
|
229
|
+
| `upsertDeterministic` | Insert or update with deterministic ID |
|
|
230
|
+
| `appendToCollection` | Add to collection end |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
**JSDoc in IDE** - Hover over any import for full props and examples.
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# CRUD Operations
|
|
2
|
+
|
|
3
|
+
> **API details:** `lookup_symbol("symbolName")` via MCP for full props, return types, and examples from JSDoc.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Define Entity (SSOT)
|
|
8
|
+
|
|
9
|
+
The entity definition drives everything - forms, lists, validation, access, search, sort, backend.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { defineEntity } from '@donotdev/core';
|
|
13
|
+
|
|
14
|
+
export const productEntity = defineEntity({
|
|
15
|
+
name: 'Product',
|
|
16
|
+
collection: 'products',
|
|
17
|
+
search: { fields: ['name', 'description'] },
|
|
18
|
+
defaultSort: { field: 'createdAt', direction: 'desc' },
|
|
19
|
+
fields: {
|
|
20
|
+
name: { name: 'name', label: 'name', type: 'text', visibility: 'guest', validation: { required: true } },
|
|
21
|
+
price: { name: 'price', label: 'price', type: 'price', visibility: 'guest', validation: { required: true } },
|
|
22
|
+
image: { name: 'image', label: 'image', type: 'image', visibility: 'guest' },
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Auto-added fields:** `id`, `createdAt`, `updatedAt`, `createdById`, `updatedById`, `status`.
|
|
28
|
+
|
|
29
|
+
See `lookup_symbol("defineEntity")` for all options (scope, access, ownership, uniqueKeys, validation).
|
|
30
|
+
|
|
31
|
+
### Status Field (auto-added)
|
|
32
|
+
|
|
33
|
+
The `status` field is auto-added by `defineEntity()` with sensible defaults:
|
|
34
|
+
|
|
35
|
+
| Property | Default | Can Override? |
|
|
36
|
+
|----------|---------|---------------|
|
|
37
|
+
| `type` | `'select'` | Yes |
|
|
38
|
+
| `visibility` | `'admin'` | Yes |
|
|
39
|
+
| `editable` | `'admin'` | Yes |
|
|
40
|
+
| `required` | `true` | Yes |
|
|
41
|
+
|
|
42
|
+
**Default options:** `draft`, `available`, `deleted` - always present, cannot be removed.
|
|
43
|
+
**Default value:** `available`.
|
|
44
|
+
|
|
45
|
+
If you don't define `status` in your fields at all, the full default is added automatically. If you define it, your properties are merged on top of defaults:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
fields: {
|
|
49
|
+
// Minimal: just add custom options (merged after defaults)
|
|
50
|
+
status: {
|
|
51
|
+
validation: {
|
|
52
|
+
options: [
|
|
53
|
+
{ value: 'shipped', label: 'Shipped' },
|
|
54
|
+
{ value: 'returned', label: 'Returned' },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
// Result: [draft, available, deleted, shipped, returned]
|
|
59
|
+
|
|
60
|
+
// Full override: all properties respected
|
|
61
|
+
status: {
|
|
62
|
+
visibility: 'super',
|
|
63
|
+
editable: 'super',
|
|
64
|
+
validation: { options: [...] },
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> `draft` and `deleted` items are hidden from non-admin users (server-side filtering).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 2. Provider Setup (once)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// config/providers.ts
|
|
77
|
+
import { configureProviders } from '@donotdev/core';
|
|
78
|
+
import { FirestoreAdapter } from '@donotdev/firebase'; // or SupabaseCrudAdapter
|
|
79
|
+
|
|
80
|
+
configureProviders({ crud: new FirestoreAdapter() });
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Import in `App.tsx`: `import './config/providers';`
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 3. Backend Functions
|
|
88
|
+
|
|
89
|
+
**Firebase:**
|
|
90
|
+
```typescript
|
|
91
|
+
// functions/src/index.ts
|
|
92
|
+
import { initializeApp } from 'firebase-admin/app';
|
|
93
|
+
import { createCrudFunctions } from '@donotdev/functions/firebase';
|
|
94
|
+
import * as entities from 'entities';
|
|
95
|
+
|
|
96
|
+
initializeApp();
|
|
97
|
+
export const crud = createCrudFunctions(entities);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Supabase:**
|
|
101
|
+
```typescript
|
|
102
|
+
// supabase/functions/crud/index.ts
|
|
103
|
+
import * as entities from '../_shared/entities.ts';
|
|
104
|
+
import { createSupabaseCrudFunctions } from '@donotdev/functions/supabase';
|
|
105
|
+
|
|
106
|
+
const { serve } = createSupabaseCrudFunctions(entities);
|
|
107
|
+
Deno.serve(serve);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
See the Firebase or Supabase guideline for full function setup.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 4. Use Components
|
|
115
|
+
|
|
116
|
+
Drop components on a page. Entity drives the UI - no configuration needed.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
<EntityList entity={productEntity} userRole={user?.role} />
|
|
120
|
+
<EntityCardList entity={productEntity} basePath="/shop" cols={[1, 2, 3, 4]} />
|
|
121
|
+
<EntityFormRenderer entity={productEntity} operation="create" onSubmit={handleSubmit} cancelPath="/products" />
|
|
122
|
+
<EntityDisplayRenderer entity={productEntity} id={id} />
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use `lookup_symbol("ComponentName")` for full props.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 5. Visibility & Access
|
|
130
|
+
|
|
131
|
+
### Field Visibility (who sees what)
|
|
132
|
+
|
|
133
|
+
| Level | Who |
|
|
134
|
+
|-------|-----|
|
|
135
|
+
| `'guest'` | Everyone |
|
|
136
|
+
| `'user'` | Authenticated |
|
|
137
|
+
| `'admin'` | Admins |
|
|
138
|
+
| `'super'` | Super admins |
|
|
139
|
+
| `'technical'` | Admins only, read-only in forms |
|
|
140
|
+
| `'owner'` | Stakeholders only (see `ownership`) |
|
|
141
|
+
| `'hidden'` | Never |
|
|
142
|
+
|
|
143
|
+
### Entity Access (who can CRUD)
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
access: { create: 'admin', read: 'guest', update: 'admin', delete: 'admin' }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Ownership
|
|
150
|
+
|
|
151
|
+
For marketplace patterns. See `lookup_symbol("defineEntity")` -> `ownership`.
|
|
152
|
+
|
|
153
|
+
**Firebase:** Enforced by generated secure Firebase Functions (server-side).
|
|
154
|
+
**Supabase:** Enforced via RLS policies - generated by `dndev setup`.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 6. Multi-Tenancy (Scope)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
registerScopeProvider('company', () => companyStore.getState().currentCompanyId);
|
|
162
|
+
|
|
163
|
+
export const clientEntity = defineEntity({
|
|
164
|
+
scope: { field: 'companyId', provider: 'company' },
|
|
165
|
+
// ... (companyId auto-added, all CRUD ops auto-scoped)
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
See `lookup_symbol("registerScopeProvider")`.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 7. Data Fetching & Pagination
|
|
174
|
+
|
|
175
|
+
### Default: Auto Mode (zero config)
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
<EntityList entity={productEntity} />
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
That's it. The framework handles pagination automatically:
|
|
182
|
+
|
|
183
|
+
1. **First fetch** - loads up to 1000 items client-side (instant search, sort, filter in the browser)
|
|
184
|
+
2. **If total > 1000** - auto-switches to server pagination (fetches one page at a time via cursor)
|
|
185
|
+
|
|
186
|
+
You never need to configure pagination mode. It just works.
|
|
187
|
+
|
|
188
|
+
### Forcing a Mode (rare)
|
|
189
|
+
|
|
190
|
+
| Mode | Behavior | When to force |
|
|
191
|
+
|------|----------|---------------|
|
|
192
|
+
| `pagination='auto'` (default) | Client-side up to 1000, auto-switches to server if more | Never - this is the default |
|
|
193
|
+
| `pagination='client'` | Always client-side, fetches all | You know the dataset is small and want instant filters |
|
|
194
|
+
| `pagination='server'` | Always server-side, cursor pagination | You know the dataset is huge and want minimal fetch |
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
// Force server pagination with custom page size
|
|
198
|
+
<EntityList entity={productEntity} pagination="server" pageSize={50} />
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### What Changes When Auto-Switches to Server
|
|
202
|
+
|
|
203
|
+
| Feature | Client mode (< 1000) | Server mode (> 1000) |
|
|
204
|
+
|---------|----------------------|----------------------|
|
|
205
|
+
| Search | Instant (in-memory) | Re-fetches per query |
|
|
206
|
+
| Sort | Instant | Re-fetches |
|
|
207
|
+
| Filters | Instant | Re-fetches |
|
|
208
|
+
| Page navigation | Instant | Fetches next page |
|
|
209
|
+
|
|
210
|
+
Server mode fetches only `pageSize` items per request - not the whole collection.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## What's Available
|
|
215
|
+
|
|
216
|
+
### Components (`@donotdev/ui`)
|
|
217
|
+
|
|
218
|
+
| Component | One-liner |
|
|
219
|
+
|-----------|-----------|
|
|
220
|
+
| `EntityList` | Table list with search, filters, pagination, routing |
|
|
221
|
+
| `EntityCardList` | Card grid with search, filters, favorites, routing |
|
|
222
|
+
| `EntityFormRenderer` | Auto-generated form from entity (create/edit) |
|
|
223
|
+
| `EntityDisplayRenderer` | Read-only detail view, auto-fetches by ID |
|
|
224
|
+
| `EntityRecommendations` | "You may also like" related items |
|
|
225
|
+
|
|
226
|
+
### Templates (`@donotdev/templates`)
|
|
227
|
+
|
|
228
|
+
| Template | One-liner |
|
|
229
|
+
|----------|-----------|
|
|
230
|
+
| `ProductCardListTemplate` | Shop card grid with price, image, sale badges |
|
|
231
|
+
| `CarCardListTemplate` | Automotive listing with mileage, year |
|
|
232
|
+
| `CarDetailTemplate` | Car detail with gallery, specs, price |
|
|
233
|
+
| `InquiryFormTemplate` | Contact form (Customer + Inquiry, GDPR) |
|
|
234
|
+
| `InquiryAdminTemplate` | Admin dashboard for inquiries |
|
|
235
|
+
| `HomeTemplate` | Landing page |
|
|
236
|
+
| `LoginTemplate` | Auth login page |
|
|
237
|
+
| `DashboardTemplate` | Admin dashboard |
|
|
238
|
+
| `CheckoutTemplate` | Stripe checkout |
|
|
239
|
+
| `BillingSuccessTemplate` | Post-checkout success |
|
|
240
|
+
| `UserSubscriptionTemplate` | Manage subscription |
|
|
241
|
+
|
|
242
|
+
### Hooks (`@donotdev/crud`)
|
|
243
|
+
|
|
244
|
+
| Hook | One-liner |
|
|
245
|
+
|------|-----------|
|
|
246
|
+
| `useCrud` | CRUD actions: `add`, `update`, `delete`, `get`, `set`, `query` |
|
|
247
|
+
| `useCrudList` | Real-time list with search, filters, sort applied |
|
|
248
|
+
| `useCrudCardList` | Same as `useCrudList`, optimized for public card views |
|
|
249
|
+
| `useCrudFilters` | Filter state per collection, auto-consumed by list hooks |
|
|
250
|
+
| `useEntityForm` | Form state for custom form layouts |
|
|
251
|
+
| `useEntityField` | Single-field control for custom form layouts |
|
|
252
|
+
| `useEntityFavorites` | Local favorites (localStorage) |
|
|
253
|
+
| `useRelatedItems` | Fetch related items by reference field |
|
|
254
|
+
| `useFileUpload` | File upload with progress tracking |
|
|
255
|
+
| `useUnsavedChangesWarning` | Warn before leaving with unsaved changes |
|
|
256
|
+
|
|
257
|
+
### Utilities (`@donotdev/crud`)
|
|
258
|
+
|
|
259
|
+
| Utility | One-liner |
|
|
260
|
+
|---------|-----------|
|
|
261
|
+
| `formatValue` | Format any field value for display |
|
|
262
|
+
| `applyFilters` / `applySearch` / `applySort` | Processing pipeline (what `useCrudList` does internally) |
|
|
263
|
+
| `registerFieldType` | Register custom field type |
|
|
264
|
+
| `validateEntity` | Validate entity data against schemas |
|
|
265
|
+
| `isFieldEditable` / `getFieldsForOperation` | Field visibility helpers |
|
|
266
|
+
|
|
267
|
+
### Routing (`@donotdev/ui`)
|
|
268
|
+
|
|
269
|
+
| Symbol | One-liner |
|
|
270
|
+
|--------|-----------|
|
|
271
|
+
| `useNavigate` | Programmatic navigation (Vite/Next.js auto-detected) |
|
|
272
|
+
| `useParams` | Read route params |
|
|
273
|
+
| `Link` | Declarative navigation |
|
|
274
|
+
| `AuthGuard` | Protect routes by auth + role |
|
|
275
|
+
|
|
276
|
+
**Convention:** `/${collection}` (list) - `/${collection}/new` (create) - `/${collection}/:id` (detail/edit)
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Built-in Field Types
|
|
281
|
+
|
|
282
|
+
| Category | Types |
|
|
283
|
+
|----------|-------|
|
|
284
|
+
| **Text** | `text`, `email`, `tel`, `url`, `color`, `password`, `textarea`, `richtext` |
|
|
285
|
+
| **Number** | `number`, `currency`, `price`, `range`, `year` |
|
|
286
|
+
| **Boolean** | `checkbox`, `boolean`, `switch` |
|
|
287
|
+
| **Date/Time** | `date`, `datetime-local`, `time`, `week`, `month`, `timestamp` |
|
|
288
|
+
| **Selection** | `select`, `combobox`, `multiselect`, `radio` |
|
|
289
|
+
| **Files** | `file`, `files`, `document`, `documents`, `image`, `images` |
|
|
290
|
+
| **Complex** | `geopoint`, `address`, `map`, `array` |
|
|
291
|
+
| **Special** | `avatar`, `badge`, `hidden`, `submit`, `reset` |
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Custom Field Types (escape hatch)
|
|
296
|
+
|
|
297
|
+
When built-in types don't fit, register your own with `registerFieldType`. One call wires form component + display formatter + filter.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { registerFieldType, useController } from '@donotdev/crud';
|
|
301
|
+
import type { ControlledFieldProps } from '@donotdev/crud';
|
|
302
|
+
|
|
303
|
+
function StatusTierField({ fieldConfig, control, t }: ControlledFieldProps) {
|
|
304
|
+
const { field, fieldState } = useController({ name: fieldConfig.name, control });
|
|
305
|
+
return (
|
|
306
|
+
<select value={field.value ?? ''} onChange={(e) => field.onChange(e.target.value)}>
|
|
307
|
+
<option value="basic">Basic</option>
|
|
308
|
+
<option value="premium">Premium</option>
|
|
309
|
+
</select>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
registerFieldType({
|
|
314
|
+
type: 'statusTier',
|
|
315
|
+
controlledComponent: StatusTierField,
|
|
316
|
+
displayFormatter: (value) => value ? String(value).charAt(0).toUpperCase() + String(value).slice(1) : '-',
|
|
317
|
+
filterable: true,
|
|
318
|
+
filterType: 'select',
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Then use in entity: `tier: { name: 'tier', label: 'tier', type: 'statusTier', visibility: 'guest' }`
|
|
323
|
+
|
|
324
|
+
See `lookup_symbol("registerFieldType")`, `lookup_symbol("ControlledFieldProps")`.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## i18n
|
|
329
|
+
|
|
330
|
+
**Namespace:** `entity-${entity.name.toLowerCase()}` (customizable via `namespace`).
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{ "fields": { "name": "Product Name", "price": "Price" }, "name": "Product" }
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Status labels: entity namespace -> `crud` namespace fallback.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
**Define entity -> register provider -> register functions -> drop components. Entity is the SSOT - everything flows from it.**
|