@donotdev/crud 0.1.0 → 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.
Files changed (157) hide show
  1. package/dist/CrudService.d.ts +9 -0
  2. package/dist/CrudService.d.ts.map +1 -1
  3. package/dist/CrudService.js +2 -2
  4. package/dist/CrudStore.d.ts.map +1 -1
  5. package/dist/CrudStore.js +1 -1
  6. package/dist/FieldRegistry.d.ts +2 -12
  7. package/dist/FieldRegistry.d.ts.map +1 -1
  8. package/dist/FieldRegistry.js +1 -1
  9. package/dist/components/CrudButton.d.ts +1 -0
  10. package/dist/components/CrudButton.d.ts.map +1 -1
  11. package/dist/components/CrudCard.d.ts +1 -1
  12. package/dist/components/CrudCard.d.ts.map +1 -1
  13. package/dist/components/CrudCard.js +1 -1
  14. package/dist/components/DisplayFieldRenderer.d.ts +1 -0
  15. package/dist/components/DisplayFieldRenderer.d.ts.map +1 -1
  16. package/dist/components/DisplayFieldRenderer.js +1 -1
  17. package/dist/components/DisplayThumbnail.d.ts +1 -9
  18. package/dist/components/DisplayThumbnail.d.ts.map +1 -1
  19. package/dist/components/DisplayThumbnail.js +1 -1
  20. package/dist/components/EntityFilters.d.ts +1 -0
  21. package/dist/components/EntityFilters.d.ts.map +1 -1
  22. package/dist/components/FormFieldRenderer.d.ts +1 -0
  23. package/dist/components/FormFieldRenderer.d.ts.map +1 -1
  24. package/dist/components/FormFieldRenderer.js +1 -1
  25. package/dist/components/FormLayout.d.ts +1 -0
  26. package/dist/components/FormLayout.d.ts.map +1 -1
  27. package/dist/components/controlled/complex/ControlledAddressField.js +1 -1
  28. package/dist/components/controlled/complex/ControlledDateField.d.ts.map +1 -1
  29. package/dist/components/controlled/complex/ControlledDateField.js +1 -1
  30. package/dist/components/controlled/complex/ControlledGeoPointField.js +1 -1
  31. package/dist/components/controlled/complex/ControlledMapField.js +1 -1
  32. package/dist/components/controlled/complex/ControlledRichTextField.js +1 -1
  33. package/dist/components/controlled/complex/ControlledTimestampField.d.ts.map +1 -1
  34. package/dist/components/controlled/complex/ControlledTimestampField.js +1 -1
  35. package/dist/components/controlled/file/ControlledDocumentField.js +1 -1
  36. package/dist/components/controlled/file/ControlledFileField.js +1 -1
  37. package/dist/components/controlled/file/ControlledImageField.js +1 -1
  38. package/dist/components/controlled/file/ControlledMultiDocumentField.js +1 -1
  39. package/dist/components/controlled/file/ControlledMultiFileField.js +1 -1
  40. package/dist/components/controlled/file/ControlledMultiImageField.js +1 -1
  41. package/dist/components/controlled/input/ControlledCurrencyField.js +1 -1
  42. package/dist/components/controlled/input/ControlledDurationField.js +1 -1
  43. package/dist/components/controlled/input/ControlledNumberField.js +1 -1
  44. package/dist/components/controlled/input/ControlledPasswordField.js +1 -1
  45. package/dist/components/controlled/input/ControlledPhoneField.js +1 -1
  46. package/dist/components/controlled/input/ControlledPriceField.js +1 -1
  47. package/dist/components/controlled/input/ControlledRangeField.js +1 -1
  48. package/dist/components/controlled/input/ControlledRatingField.js +1 -1
  49. package/dist/components/controlled/input/ControlledSwitchField.js +1 -1
  50. package/dist/components/controlled/input/ControlledTextField.d.ts +5 -5
  51. package/dist/components/controlled/input/ControlledTextField.d.ts.map +1 -1
  52. package/dist/components/controlled/input/ControlledTextField.js +1 -1
  53. package/dist/components/controlled/input/ControlledTextareaField.js +1 -1
  54. package/dist/components/controlled/select/ControlledComboboxField.js +1 -1
  55. package/dist/components/controlled/select/ControlledDropdownField.d.ts +2 -4
  56. package/dist/components/controlled/select/ControlledDropdownField.d.ts.map +1 -1
  57. package/dist/components/controlled/select/ControlledDropdownField.js +1 -1
  58. package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts +2 -4
  59. package/dist/components/controlled/select/ControlledMultiDropdownField.d.ts.map +1 -1
  60. package/dist/components/controlled/select/ControlledMultiDropdownField.js +1 -1
  61. package/dist/components/controlled/select/ControlledRadioField.d.ts +2 -4
  62. package/dist/components/controlled/select/ControlledRadioField.d.ts.map +1 -1
  63. package/dist/components/controlled/select/ControlledRadioField.js +1 -1
  64. package/dist/components/controlled/select/ControlledReferenceField.js +1 -1
  65. package/dist/components/controlled/select/ControlledYearField.js +1 -1
  66. package/dist/components/fields/display/DateFieldDisplay.d.ts.map +1 -1
  67. package/dist/components/fields/display/DateFieldDisplay.js +1 -1
  68. package/dist/components/fields/display/NumberFieldDisplay.d.ts.map +1 -1
  69. package/dist/components/fields/display/NumberFieldDisplay.js +1 -1
  70. package/dist/components/fields/display/TextFieldDisplay.js +1 -1
  71. package/dist/components/fields/display/TimestampFieldDisplay.d.ts.map +1 -1
  72. package/dist/components/fields/display/TimestampFieldDisplay.js +1 -1
  73. package/dist/components/form/fields/CurrencyFieldComponent.d.ts.map +1 -1
  74. package/dist/components/form/fields/CurrencyFieldComponent.js +1 -1
  75. package/dist/components/form/fields/DateFieldComponent.d.ts +2 -2
  76. package/dist/components/form/fields/DateFieldComponent.d.ts.map +1 -1
  77. package/dist/components/form/fields/DateFieldComponent.js +1 -1
  78. package/dist/components/form/fields/DocumentFieldComponent.js +1 -1
  79. package/dist/components/form/fields/FileFieldComponent.js +1 -1
  80. package/dist/components/form/fields/ImageFieldComponent.d.ts.map +1 -1
  81. package/dist/components/form/fields/ImageFieldComponent.js +1 -1
  82. package/dist/components/form/fields/TextFieldComponent.d.ts +2 -2
  83. package/dist/components/form/fields/TextFieldComponent.d.ts.map +1 -1
  84. package/dist/components/form/fields/TextFieldComponent.js +1 -1
  85. package/dist/components/form/fields/TimestampFieldComponent.d.ts +2 -2
  86. package/dist/components/form/fields/TimestampFieldComponent.d.ts.map +1 -1
  87. package/dist/contexts/UploadContext.d.ts +1 -0
  88. package/dist/contexts/UploadContext.d.ts.map +1 -1
  89. package/dist/fieldTypeRegistry.store.d.ts.map +1 -1
  90. package/dist/fieldTypeRegistry.store.js +1 -1
  91. package/dist/fieldTypeRegistry.types.d.ts +4 -0
  92. package/dist/fieldTypeRegistry.types.d.ts.map +1 -1
  93. package/dist/forms/hooks/useEntityForm.d.ts.map +1 -1
  94. package/dist/forms/hooks/useEntityForm.js +1 -1
  95. package/dist/forms/types.d.ts +5 -0
  96. package/dist/forms/types.d.ts.map +1 -1
  97. package/dist/forms/utils/buildInitialValues.d.ts.map +1 -1
  98. package/dist/forms/utils/buildInitialValues.js +1 -1
  99. package/dist/forms/utils/getFieldsForOperation.d.ts.map +1 -1
  100. package/dist/forms/utils/getFieldsForOperation.js +1 -1
  101. package/dist/forms/utils/isFieldEditable.d.ts +7 -1
  102. package/dist/forms/utils/isFieldEditable.d.ts.map +1 -1
  103. package/dist/forms/utils/isFieldEditable.js +1 -1
  104. package/dist/hooks/index.d.ts +2 -0
  105. package/dist/hooks/index.d.ts.map +1 -1
  106. package/dist/hooks/index.js +1 -1
  107. package/dist/hooks/useCrudFilters.d.ts +2 -0
  108. package/dist/hooks/useCrudFilters.d.ts.map +1 -1
  109. package/dist/hooks/useCrudPageSize.d.ts +29 -0
  110. package/dist/hooks/useCrudPageSize.d.ts.map +1 -0
  111. package/dist/hooks/useCrudPageSize.js +1 -0
  112. package/dist/hooks/useEntityFavorites.d.ts +2 -0
  113. package/dist/hooks/useEntityFavorites.d.ts.map +1 -1
  114. package/dist/hooks/useFileUpload.d.ts.map +1 -1
  115. package/dist/hooks/useFileUpload.js +1 -1
  116. package/dist/hooks/useReferenceResolver.d.ts +1 -1
  117. package/dist/hooks/useReferenceResolver.d.ts.map +1 -1
  118. package/dist/hooks/useReferenceResolver.js +1 -1
  119. package/dist/hooks/useRelatedItems.d.ts +2 -0
  120. package/dist/hooks/useRelatedItems.d.ts.map +1 -1
  121. package/dist/index.d.ts +3 -3
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +1 -1
  124. package/dist/registerBuiltinFieldTypes.d.ts.map +1 -1
  125. package/dist/registerBuiltinFieldTypes.js +1 -1
  126. package/dist/stores/FormStore.d.ts +2 -0
  127. package/dist/stores/FormStore.d.ts.map +1 -1
  128. package/dist/stores/FormStore.js +1 -1
  129. package/dist/stores/UploadStore.d.ts +9 -3
  130. package/dist/stores/UploadStore.d.ts.map +1 -1
  131. package/dist/stores/UploadStore.js +1 -1
  132. package/dist/symbol-index.json +1 -0
  133. package/dist/tsconfig.tsbuildinfo +1 -1
  134. package/dist/types.d.ts +6 -0
  135. package/dist/types.d.ts.map +1 -1
  136. package/dist/useCrud.d.ts +1 -0
  137. package/dist/useCrud.d.ts.map +1 -1
  138. package/dist/useCrud.js +1 -1
  139. package/dist/useCrudCardList.d.ts +5 -3
  140. package/dist/useCrudCardList.d.ts.map +1 -1
  141. package/dist/useCrudList.d.ts +5 -3
  142. package/dist/useCrudList.d.ts.map +1 -1
  143. package/dist/utils/clientListProcessing.d.ts +21 -2
  144. package/dist/utils/clientListProcessing.d.ts.map +1 -1
  145. package/dist/utils/clientListProcessing.js +1 -1
  146. package/dist/utils/fileStorage.d.ts +2 -0
  147. package/dist/utils/fileStorage.d.ts.map +1 -1
  148. package/dist/utils/fileStorage.js +1 -1
  149. package/dist/utils/imageProcessing.d.ts.map +1 -1
  150. package/dist/utils/imageProcessing.js +1 -1
  151. package/dist/utils/imageStorage.d.ts +2 -0
  152. package/dist/utils/imageStorage.d.ts.map +1 -1
  153. package/dist/utils/imageStorage.js +1 -1
  154. package/guidelines/COMPONENTS.md +234 -0
  155. package/guidelines/CRUD.md +340 -0
  156. package/guidelines/GOTCHAS.md +46 -0
  157. package/package.json +7 -4
@@ -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.**
@@ -0,0 +1,46 @@
1
+ # Gotchas: @donotdev/crud
2
+
3
+ Common mistakes related to entity definition, forms, fields, and CRUD operations.
4
+
5
+ ---
6
+
7
+ ## CRUD [Phase 2, 3]
8
+
9
+ **Hidden fields are auto-added by `defineEntity()` - don't define them manually:**
10
+ - `id`, `createdAt`, `updatedAt`, `createdById`, `updatedById`
11
+
12
+ **Status field - auto-added, fully overridable.**
13
+ The `status` field is auto-added with defaults: `visibility: 'admin'`, `editable: 'admin'`, `type: 'select'`, `required: true`. All properties can be overridden. `validation.options` are **merged** (consumer options added after base: `draft`, `available`, `deleted`).
14
+
15
+ ```typescript
16
+ // Minimal: just extend options
17
+ status: { validation: { options: [{ value: 'shipped', label: 'Shipped' }] } }
18
+
19
+ // Override visibility, editable, label - all respected
20
+ status: { name: 'status', label: 'fields.status', visibility: 'admin', editable: 'super', validation: { options: [...] } }
21
+
22
+ // Hide from forms entirely
23
+ status: { visibility: 'technical' }
24
+ ```
25
+
26
+ **Scope field is auto-added** when `scope` is configured. Don't manually define the scope field (e.g., `companyId`).
27
+
28
+ **Custom form fields MUST use framework's `useController`:**
29
+
30
+ ```typescript
31
+ // WRONG
32
+ import { useController } from 'react-hook-form';
33
+
34
+ // CORRECT
35
+ import { useController } from '@donotdev/crud';
36
+ ```
37
+
38
+ **Form components receive `control` prop, not `field` prop.**
39
+
40
+ **Price field is structured:** `{ amount, currency, vatIncluded, discountPercent }`. Don't store computed discount amounts.
41
+
42
+ **File uploads are deferred** - files upload on form submit, not on selection. Images show optimistic blob URLs.
43
+
44
+ **Generated/computed fields are auto-excluded from forms.** Fields with `editable: 'generated'` (DB-generated columns) or `editable: 'computed'` (UI-derived values) are silently filtered out by `getFieldsForOperation`. They won't appear in create or edit forms. Use `'generated'` for PostgreSQL `GENERATED ALWAYS` columns. Use `'computed'` for fields derived from other form values (still included in write payloads).
45
+
46
+ **Entity namespace defaults to `entity-{name}`** (lowercase). Translation files must match: `locales/entity-product_en.json`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donotdev/crud",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -20,6 +20,7 @@
20
20
  ],
21
21
  "files": [
22
22
  "dist",
23
+ "guidelines",
23
24
  "package.json",
24
25
  "README.md",
25
26
  "LICENSE.md"
@@ -47,14 +48,16 @@
47
48
  "dev": "tsc --noEmit --watch --listFiles false --listEmittedFiles false",
48
49
  "clean": "rimraf dist tsconfig.tsbuildinfo",
49
50
  "lint": "eslint src/",
50
- "type-check": "bunx tsc --noEmit"
51
+ "type-check": "bunx tsc --noEmit",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest"
51
54
  },
52
55
  "dependencies": {
53
56
  "@dnd-kit/core": "^6.3.1",
54
57
  "@dnd-kit/sortable": "^10.0.0",
55
58
  "@dnd-kit/utilities": "^3.2.2",
56
- "@donotdev/components": "^0.1.0",
57
- "@donotdev/core": "^0.1.0",
59
+ "@donotdev/components": "^0.1.1",
60
+ "@donotdev/core": "^0.1.1",
58
61
  "@hookform/resolvers": "^5.2.2",
59
62
  "dompurify": "^3.3.2",
60
63
  "react-easy-crop": "^5.5.6"