@donotdev/cli 0.0.9 → 0.0.11
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/dependencies-matrix.json +24 -7
- package/dist/bin/commands/build.js +2 -2
- package/dist/bin/commands/bump.js +578 -94
- package/dist/bin/commands/cacheout.js +2 -2
- package/dist/bin/commands/create-app.js +3 -3
- package/dist/bin/commands/create-project.js +3 -3
- package/dist/bin/commands/deploy.js +3 -3
- package/dist/bin/commands/dev.js +2 -2
- package/dist/bin/commands/emu.js +2 -2
- package/dist/bin/commands/format.js +2 -2
- package/dist/bin/commands/lint.js +2 -2
- package/dist/bin/commands/preview.js +2 -2
- package/dist/bin/commands/sync-secrets.js +2 -2
- package/dist/index.js +3 -3
- package/package.json +2 -2
- package/templates/root-consumer/.claude/agents/architect.md.example +313 -0
- package/templates/root-consumer/.claude/agents/builder.md.example +329 -0
- package/templates/root-consumer/.claude/agents/coder.md.example +87 -0
- package/templates/root-consumer/.claude/agents/extractor.md.example +235 -0
- package/templates/root-consumer/.claude/agents/polisher.md.example +359 -0
- package/templates/root-consumer/.claude/agents/prompt-engineer.md.example +85 -0
- package/templates/root-consumer/.claude/commands/brainstorm.md.example +133 -0
- package/templates/root-consumer/.claude/commands/build.md.example +109 -0
- package/templates/root-consumer/.claude/commands/design.md.example +136 -0
- package/templates/root-consumer/.claude/commands/polish.md.example +145 -0
- package/templates/root-consumer/.cursor/mcp.json.example +8 -0
- package/templates/root-consumer/.mcp.json.example +8 -0
- package/templates/root-consumer/CLAUDE.md.example +146 -0
- package/templates/root-consumer/guides/dndev/AGENT_START_HERE.md.example +15 -12
- package/templates/root-consumer/guides/dndev/COMPONENT_API.md.example +195 -0
- package/templates/root-consumer/guides/dndev/INDEX.md.example +3 -1
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +157 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# DoNotDev Framework Guides
|
|
2
2
|
|
|
3
|
-
**For AI Agents:** See [AGENT_START_HERE.md](./AGENT_START_HERE.md)
|
|
3
|
+
**For AI Agents:** See [AGENT_START_HERE.md](./AGENT_START_HERE.md) ← **START HERE**
|
|
4
|
+
|
|
5
|
+
**AI System:** Commands in `.claude/commands/`, agents in `.claude/agents/` (synced via `dndev bump`)
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
| **Display Utilities** (`formatValue`, `DisplayFieldRenderer`, `EntityFilters`, `translateFieldLabel`) | `@donotdev/crud` |
|
|
13
13
|
| **Form Components** (`FormFieldRenderer`, `Controlled*Field`, `UploadProvider`) | `@donotdev/crud` |
|
|
14
14
|
| **Routing-Aware Components** (`EntityFormRenderer`, `EntityList`, `EntityCardList`, `EntityDisplayRenderer`) | `@donotdev/ui` |
|
|
15
|
-
| **Specialized Templates** (`ProductCardListTemplate`, `CarCardListTemplate`) | `@donotdev/templates` |
|
|
15
|
+
| **Specialized Templates** (`ProductCardListTemplate`, `CarCardListTemplate`, `InquiryFormTemplate`, `InquiryAdminTemplate`) | `@donotdev/templates` |
|
|
16
16
|
| **Routing** (`useNavigate`, `Link`, `useParams`) | `@donotdev/ui` |
|
|
17
17
|
| **Entity Definition** (`defineEntity`) | `@donotdev/core` |
|
|
18
18
|
|
|
@@ -147,8 +147,49 @@ registerScopeProvider('tenant', () => tenantStore.getState().currentTenantId);
|
|
|
147
147
|
| `'user'` | Authenticated |
|
|
148
148
|
| `'admin'` | Admins |
|
|
149
149
|
| `'super'` | Super admins |
|
|
150
|
+
| `'technical'` | Admins only (read-only in forms; e.g. scope field) |
|
|
151
|
+
| `'owner'` | Only when the current user is a stakeholder (see **Stakeholder Access** below) |
|
|
150
152
|
| `'hidden'` | Never |
|
|
151
153
|
|
|
154
|
+
### Stakeholder Access (ownership)
|
|
155
|
+
|
|
156
|
+
For marketplace-style entities: documents **public when available**, **private to stakeholders** (e.g. partner, customer) when booked.
|
|
157
|
+
|
|
158
|
+
**1. Add `ownership` to the entity:**
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
ownership: {
|
|
162
|
+
ownerFields: ['providerId', 'customerId'], // Document fields whose value is a user id
|
|
163
|
+
publicCondition: [ // When can anyone read? (AND together)
|
|
164
|
+
{ field: 'status', op: '==', value: 'available' },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**2. Use `visibility: 'owner'`** for fields only stakeholders should see (e.g. internal notes, customer contact after booking):
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
internalNotes: {
|
|
173
|
+
name: 'internalNotes',
|
|
174
|
+
label: 'Internal notes',
|
|
175
|
+
type: 'textarea',
|
|
176
|
+
visibility: 'owner', // Returned only when request.auth.uid matches one of ownerFields
|
|
177
|
+
editable: true,
|
|
178
|
+
validation: { required: false },
|
|
179
|
+
},
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**3. Firestore rules (read and update):** Use the framework helper, then paste the condition into your hand-written `firestore.rules`:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { generateFirestoreRuleCondition } from '@donotdev/core';
|
|
186
|
+
|
|
187
|
+
const condition = generateFirestoreRuleCondition(scheduleEntity.ownership);
|
|
188
|
+
// Paste into firestore.rules: allow read, update: if <condition>;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Behavior:** `listCard_` returns only documents matching `publicCondition` (e.g. public slots). `list_` returns only documents where the current user is in one of `ownerFields` ("mine"). Same condition is used for **allow read** and **allow update** so owners can update (e.g. status to cancelled, notes). For multiple owner fields, prefer an `ownerIds` array and `array-contains` in rules and queries.
|
|
192
|
+
|
|
152
193
|
### Entity Access (who CAN DO)
|
|
153
194
|
```typescript
|
|
154
195
|
access: {
|
|
@@ -365,6 +406,85 @@ export default function ShopPage() {
|
|
|
365
406
|
}
|
|
366
407
|
```
|
|
367
408
|
|
|
409
|
+
#### Contact/Inquiry Form (Specialized Template)
|
|
410
|
+
|
|
411
|
+
For contact forms that create both a Customer and an Inquiry record:
|
|
412
|
+
|
|
413
|
+
```tsx
|
|
414
|
+
import { InquiryFormTemplate } from '@donotdev/templates';
|
|
415
|
+
import { customerEntity, inquiryEntity } from 'entities';
|
|
416
|
+
|
|
417
|
+
export default function ContactPage() {
|
|
418
|
+
return (
|
|
419
|
+
<PageContainer>
|
|
420
|
+
<InquiryFormTemplate
|
|
421
|
+
customerEntity={customerEntity}
|
|
422
|
+
inquiryEntity={inquiryEntity}
|
|
423
|
+
// Optional: contextId="car123" - links inquiry to a car/product
|
|
424
|
+
// Optional: contextName="BMW X5 2024" - shown in message placeholder
|
|
425
|
+
// Optional: contextDetails="BMW X5 2024 • 50,000 km • €45,000" - detailed placeholder
|
|
426
|
+
// Optional: messageField="message" - defaults to 'message'
|
|
427
|
+
// Optional: contextField="carId" - defaults to 'carId'
|
|
428
|
+
// Optional: customerFields={['firstName', 'lastName', 'email', 'phone']} - fields to show
|
|
429
|
+
// Optional: onSuccess={() => navigate('/thank-you')}
|
|
430
|
+
/>
|
|
431
|
+
</PageContainer>
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**What it does:**
|
|
437
|
+
- Creates a Customer record (with `findOrCreate` logic via `uniqueKeys` - prevents duplicates by email/phone)
|
|
438
|
+
- Creates an Inquiry record linked to the customer
|
|
439
|
+
- Handles GDPR consent tracking
|
|
440
|
+
- Auto-fills message with context details if provided
|
|
441
|
+
- Shows success state after submission
|
|
442
|
+
|
|
443
|
+
**Requirements:**
|
|
444
|
+
- Customer entity must have `uniqueKeys` configured (e.g., `{ fields: ['email'], findOrCreate: true }`)
|
|
445
|
+
- Inquiry entity should have `customerId` field (type: `reference`)
|
|
446
|
+
- Both entities should have `status` field (defaults to `'draft'`)
|
|
447
|
+
|
|
448
|
+
#### Inquiry Admin Dashboard (Specialized Template)
|
|
449
|
+
|
|
450
|
+
For admin pages to review and respond to inquiries with one-click actions:
|
|
451
|
+
|
|
452
|
+
```tsx
|
|
453
|
+
import { InquiryAdminTemplate } from '@donotdev/templates';
|
|
454
|
+
import { customerEntity, inquiryEntity } from 'entities';
|
|
455
|
+
|
|
456
|
+
export default function InquiriesAdminPage() {
|
|
457
|
+
return (
|
|
458
|
+
<PageContainer>
|
|
459
|
+
<InquiryAdminTemplate
|
|
460
|
+
customerEntity={customerEntity}
|
|
461
|
+
inquiryEntity={inquiryEntity}
|
|
462
|
+
// Optional: customerBasePath="/customers" - defaults to '/customers'
|
|
463
|
+
/>
|
|
464
|
+
</PageContainer>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**What it does:**
|
|
470
|
+
- Shows all inquiries in a responsive card grid (1 column mobile, 2 columns desktop)
|
|
471
|
+
- Displays inquiry message preview (first 150 chars)
|
|
472
|
+
- Shows customer info inline (name, email, phone)
|
|
473
|
+
- One-click actions:
|
|
474
|
+
- **Email** - Opens `mailto:` link (includes subject if `carId` present)
|
|
475
|
+
- **Call** - Opens `tel:` link
|
|
476
|
+
- **View Customer** - Navigates to customer detail page
|
|
477
|
+
- **Mark Responded** - Updates inquiry status to `'responded'` (only shown if not already responded)
|
|
478
|
+
- Status badges with icons (✓ for responded, ⏰ for pending)
|
|
479
|
+
- Auto-sorted by priority: available → draft → responded → deleted (then by date, newest first)
|
|
480
|
+
|
|
481
|
+
**Features:**
|
|
482
|
+
- Fetches all inquiries and customers in parallel
|
|
483
|
+
- Efficient customer lookup via Map (no N+1 queries)
|
|
484
|
+
- Loading state with spinner
|
|
485
|
+
- Empty state message
|
|
486
|
+
- Responsive grid layout
|
|
487
|
+
|
|
368
488
|
---
|
|
369
489
|
|
|
370
490
|
## 5. Component Props
|
|
@@ -765,6 +885,42 @@ export const productEntity = defineEntity({
|
|
|
765
885
|
|
|
766
886
|
Field `label` → translation key in `fields.*`
|
|
767
887
|
|
|
888
|
+
### Status Field Translations
|
|
889
|
+
|
|
890
|
+
The `status` field uses CRUD namespace translations with entity-specific overrides:
|
|
891
|
+
|
|
892
|
+
**Translation Fallback Order:**
|
|
893
|
+
1. **Entity namespace** (`entity-{name}`) - User overrides take priority
|
|
894
|
+
2. **CRUD namespace** (`crud`) - Framework defaults
|
|
895
|
+
|
|
896
|
+
**Framework Defaults** (in `crud` namespace):
|
|
897
|
+
- `crud:status.draft` → "Draft"
|
|
898
|
+
- `crud:status.available` → "Available"
|
|
899
|
+
- `crud:status.deleted` → "Deleted"
|
|
900
|
+
|
|
901
|
+
**Override in Entity Translation File:**
|
|
902
|
+
|
|
903
|
+
```json
|
|
904
|
+
// locales/entity-inquiry_en.json
|
|
905
|
+
{
|
|
906
|
+
"status": {
|
|
907
|
+
"draft": "New",
|
|
908
|
+
"available": "Read",
|
|
909
|
+
"deleted": "Closed"
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
**How it works:**
|
|
915
|
+
- Framework components (`EntityList`, `EntityFormRenderer`, etc.) automatically use `[entity.namespace, 'crud']` translation namespaces
|
|
916
|
+
- Status labels are translated via `translateLabel()` which tries entity namespace first, then falls back to `crud`
|
|
917
|
+
- No `dndev` namespace fallback - CRUD is optional and doesn't pollute core framework translations
|
|
918
|
+
|
|
919
|
+
**Example:** For an `inquiry` entity:
|
|
920
|
+
- If `entity-inquiry_en.json` has `"status.draft": "New"` → displays "New"
|
|
921
|
+
- If not found → falls back to `crud:status.draft` → displays "Draft"
|
|
922
|
+
- Never falls back to `dndev` namespace
|
|
923
|
+
|
|
768
924
|
---
|
|
769
925
|
|
|
770
926
|
## 11. Display Utilities
|