@open-mercato/cli 0.4.7-main-1768da2e43 → 0.4.7
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/build.mjs +8 -1
- package/dist/agentic/claude-code/CLAUDE.md.template +1 -0
- package/dist/agentic/claude-code/hooks/entity-migration-check.ts +121 -0
- package/dist/agentic/claude-code/mcp.json.example +13 -0
- package/dist/agentic/claude-code/settings.json +16 -0
- package/dist/agentic/codex/enforcement-rules.md +20 -0
- package/dist/agentic/codex/mcp.json.example +12 -0
- package/dist/agentic/cursor/hooks/entity-migration-check.mjs +69 -0
- package/dist/agentic/cursor/hooks.json +10 -0
- package/dist/agentic/cursor/mcp.json.example +13 -0
- package/dist/agentic/cursor/rules/entity-guard.mdc +29 -0
- package/dist/agentic/cursor/rules/generated-guard.mdc +12 -0
- package/dist/agentic/cursor/rules/open-mercato.mdc +34 -0
- package/dist/agentic/shared/AGENTS.md.template +180 -0
- package/dist/agentic/shared/ai/lessons.md +4 -0
- package/dist/agentic/shared/ai/skills/backend-ui-design/SKILL.md +197 -0
- package/dist/agentic/shared/ai/skills/backend-ui-design/references/ui-components.md +233 -0
- package/dist/agentic/shared/ai/skills/code-review/SKILL.md +144 -0
- package/dist/agentic/shared/ai/skills/code-review/references/review-checklist.md +77 -0
- package/dist/agentic/shared/ai/skills/data-model-design/SKILL.md +512 -0
- package/dist/agentic/shared/ai/skills/data-model-design/references/mikro-orm-cheatsheet.md +146 -0
- package/dist/agentic/shared/ai/skills/eject-and-customize/SKILL.md +318 -0
- package/dist/agentic/shared/ai/skills/integration-builder/SKILL.md +722 -0
- package/dist/agentic/shared/ai/skills/integration-builder/references/adapter-contracts.md +762 -0
- package/dist/agentic/shared/ai/skills/module-scaffold/SKILL.md +614 -0
- package/dist/agentic/shared/ai/skills/module-scaffold/references/naming-conventions.md +61 -0
- package/dist/agentic/shared/ai/skills/spec-writing/SKILL.md +76 -0
- package/dist/agentic/shared/ai/skills/spec-writing/references/spec-checklist.md +50 -0
- package/dist/agentic/shared/ai/skills/spec-writing/references/spec-template.md +86 -0
- package/dist/agentic/shared/ai/skills/system-extension/SKILL.md +858 -0
- package/dist/agentic/shared/ai/skills/system-extension/references/extension-contracts.md +271 -0
- package/dist/agentic/shared/ai/skills/troubleshooter/SKILL.md +421 -0
- package/dist/agentic/shared/ai/skills/troubleshooter/references/diagnostic-commands.md +101 -0
- package/dist/agentic/shared/ai/specs/README.md +26 -0
- package/dist/agentic/shared/ai/specs/SPEC-000-template.md +35 -0
- package/dist/lib/agentic-init.js +54 -0
- package/dist/lib/agentic-init.js.map +7 -0
- package/dist/lib/agentic-setup.js +209 -0
- package/dist/lib/agentic-setup.js.map +7 -0
- package/dist/lib/generators/module-registry.js +3 -3
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/testing/integration.js +8 -2
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/mercato.js +5 -0
- package/dist/mercato.js.map +2 -2
- package/package.json +7 -4
- package/src/lib/agentic-init.ts +69 -0
- package/src/lib/agentic-setup.ts +277 -0
- package/src/lib/generators/module-registry.ts +3 -3
- package/src/lib/testing/integration.ts +8 -2
- package/src/mercato.ts +7 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-ui-design
|
|
3
|
+
description: Design and implement consistent backend/backoffice interfaces using @open-mercato/ui. Use when building admin pages, CRUD interfaces, data tables, forms, detail pages, or any backoffice UI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Backend UI Design
|
|
7
|
+
|
|
8
|
+
Guide for creating consistent, production-grade backend interfaces using the `@open-mercato/ui` component library. All implementations must use existing components for visual and behavioral consistency.
|
|
9
|
+
|
|
10
|
+
For complete component API reference, see `references/ui-components.md`.
|
|
11
|
+
|
|
12
|
+
## Design Principles
|
|
13
|
+
|
|
14
|
+
1. **Consistency First**: Every page should feel like part of the same application.
|
|
15
|
+
2. **Component Reuse**: Never create custom implementations when a shared component exists.
|
|
16
|
+
3. **Data Density**: Admin users need information-rich interfaces. Optimize for scanning.
|
|
17
|
+
4. **Keyboard Navigation**: `Cmd/Ctrl+Enter` for primary actions, `Escape` to cancel.
|
|
18
|
+
5. **Clear Hierarchy**: Page → Section → Content. Use `PageHeader`, `PageBody`, consistent spacing.
|
|
19
|
+
|
|
20
|
+
## Required Component Library
|
|
21
|
+
|
|
22
|
+
ALWAYS import from `@open-mercato/ui`.
|
|
23
|
+
|
|
24
|
+
### Core Layout
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { Page, PageHeader, PageBody } from '@open-mercato/ui/backend/Page'
|
|
28
|
+
|
|
29
|
+
<Page>
|
|
30
|
+
<PageHeader>{/* Title, actions, breadcrumbs */}</PageHeader>
|
|
31
|
+
<PageBody>{/* Main content */}</PageBody>
|
|
32
|
+
</Page>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Data Display (Lists)
|
|
36
|
+
|
|
37
|
+
Use `DataTable` for ALL tabular data. Never implement custom tables.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
41
|
+
import type { FilterDef } from '@open-mercato/ui/backend/FilterBar'
|
|
42
|
+
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
43
|
+
import { TruncatedCell } from '@open-mercato/ui/backend/TruncatedCell'
|
|
44
|
+
import { BooleanIcon, EnumBadge } from '@open-mercato/ui/backend/ValueIcons'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Column patterns:
|
|
48
|
+
- Text: `TruncatedCell` with `meta.maxWidth`
|
|
49
|
+
- Boolean: `BooleanIcon`
|
|
50
|
+
- Status/enum: `EnumBadge` with severity presets
|
|
51
|
+
- Actions: `RowActions` for context menus
|
|
52
|
+
|
|
53
|
+
### Forms
|
|
54
|
+
|
|
55
|
+
Use `CrudForm` for ALL forms. Never build from scratch.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Field types: `text`, `textarea`, `number`, `email`, `password`, `select`, `multiselect`, `combobox`, `checkbox`, `switch`, `date`, `datetime`, `custom`.
|
|
62
|
+
|
|
63
|
+
### Form Headers & Footers
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { FormHeader, FormFooter, FormActionButtons, ActionsDropdown } from '@open-mercato/ui/backend/forms'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **`FormHeader mode="edit"`** — compact header for CrudForm pages
|
|
70
|
+
- **`FormHeader mode="detail"`** — large header for view/detail pages with entity type label, title, status badge, and Actions dropdown
|
|
71
|
+
- **`FormFooter`** — footer wrapping `FormActionButtons`
|
|
72
|
+
- **`ActionsDropdown`** — groups additional context actions (Convert, Send, Print). Delete is never inside the dropdown.
|
|
73
|
+
|
|
74
|
+
### Dialogs
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
|
|
78
|
+
|
|
79
|
+
// Dialog forms MUST use embedded={true}
|
|
80
|
+
<CrudForm fields={fields} onSubmit={handleSubmit} embedded={true} submitLabel="Save" />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Detail Pages
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { DetailFieldsSection, LoadingMessage, ErrorMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'
|
|
87
|
+
import { NotesSection } from '@open-mercato/ui/backend/detail/NotesSection'
|
|
88
|
+
import { TagsSection } from '@open-mercato/ui/backend/detail/TagsSection'
|
|
89
|
+
import { CustomDataSection } from '@open-mercato/ui/backend/detail/CustomDataSection'
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Notifications
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
96
|
+
|
|
97
|
+
flash('Record saved successfully', 'success')
|
|
98
|
+
flash('Failed to save record', 'error')
|
|
99
|
+
flash('This action cannot be undone', 'warning')
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
NEVER use `alert()`, `console.log()`, or custom toast implementations.
|
|
103
|
+
|
|
104
|
+
### Loading & Error States
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
108
|
+
import { DataLoader } from '@open-mercato/ui/primitives/DataLoader'
|
|
109
|
+
import { Notice } from '@open-mercato/ui/primitives/Notice'
|
|
110
|
+
import { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'
|
|
111
|
+
import { EmptyState } from '@open-mercato/ui/backend/EmptyState'
|
|
112
|
+
import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Primitives
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
119
|
+
import { Input } from '@open-mercato/ui/primitives/input'
|
|
120
|
+
import { Label } from '@open-mercato/ui/primitives/label'
|
|
121
|
+
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
122
|
+
import { Switch } from '@open-mercato/ui/primitives/switch'
|
|
123
|
+
import { SimpleTooltip } from '@open-mercato/ui/primitives/tooltip'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## API Integration
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
130
|
+
import { createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
131
|
+
import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
132
|
+
|
|
133
|
+
const handleCreate = async (values: FormValues) => {
|
|
134
|
+
const result = await createCrud<ResponseType>('module/resource', values)
|
|
135
|
+
if (result.ok) {
|
|
136
|
+
flash('Created successfully', 'success')
|
|
137
|
+
router.push(`/backend/module/${result.result.id}`)
|
|
138
|
+
}
|
|
139
|
+
return result
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Custom Fields Integration
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
import { useCustomFieldDefinitions } from '@open-mercato/ui/backend/utils/customFieldDefs'
|
|
147
|
+
import { buildCustomFieldFormFields } from '@open-mercato/ui/backend/utils/customFieldForms'
|
|
148
|
+
import { buildCustomFieldColumns } from '@open-mercato/ui/backend/utils/customFieldColumns'
|
|
149
|
+
import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Implementation Checklist
|
|
153
|
+
|
|
154
|
+
- [ ] Forms use `CrudForm` (not custom)
|
|
155
|
+
- [ ] Tables use `DataTable` (not custom)
|
|
156
|
+
- [ ] Notifications use `flash()` (not alert/toast)
|
|
157
|
+
- [ ] Dialog forms have `embedded={true}`
|
|
158
|
+
- [ ] Keyboard: `Cmd/Ctrl+Enter` (submit), `Escape` (cancel)
|
|
159
|
+
- [ ] Loading states use `LoadingMessage` or `DataLoader`
|
|
160
|
+
- [ ] Error states use `ErrorMessage`, `ErrorNotice`, or `Notice variant="error"`
|
|
161
|
+
- [ ] Empty states use `EmptyState`
|
|
162
|
+
- [ ] Column truncation uses `meta.truncate` and `meta.maxWidth`
|
|
163
|
+
- [ ] Boolean values use `BooleanIcon`
|
|
164
|
+
- [ ] Status/enum values use `EnumBadge`
|
|
165
|
+
- [ ] Row actions use `RowActions` with stable `id` values
|
|
166
|
+
- [ ] API calls use `apiCall`/`apiCallOrThrow` (not raw `fetch`)
|
|
167
|
+
|
|
168
|
+
## Anti-Patterns
|
|
169
|
+
|
|
170
|
+
1. Custom form implementations — use `CrudForm`
|
|
171
|
+
2. Manual table markup — use `DataTable`
|
|
172
|
+
3. Custom toast/notification — use `flash()`
|
|
173
|
+
4. Inline styles — use Tailwind classes
|
|
174
|
+
5. Hardcoded colors — use theme variables
|
|
175
|
+
6. Missing loading states — every async operation needs feedback
|
|
176
|
+
7. Missing error handling — every failure needs messaging
|
|
177
|
+
8. Missing keyboard shortcuts — all dialogs need `Cmd+Enter` and `Escape`
|
|
178
|
+
9. Custom truncation — use `TruncatedCell` with `meta.maxWidth`
|
|
179
|
+
10. Direct `fetch()` — use `apiCall`/`apiCallOrThrow`
|
|
180
|
+
|
|
181
|
+
## Visual Guidelines
|
|
182
|
+
|
|
183
|
+
### Spacing
|
|
184
|
+
- `p-4` for cards, `p-6` for page sections
|
|
185
|
+
- `gap-4` or `gap-6` for flex/grid layouts
|
|
186
|
+
- `space-y-4` or `space-y-6` for vertical rhythm
|
|
187
|
+
|
|
188
|
+
### Colors
|
|
189
|
+
- Use semantic colors from theme (no hardcoded hex)
|
|
190
|
+
- Destructive: `variant="destructive"` on buttons
|
|
191
|
+
- Status badges: `useSeverityPreset()`
|
|
192
|
+
|
|
193
|
+
### Layout Patterns
|
|
194
|
+
- **List pages**: FilterBar + DataTable + Pagination
|
|
195
|
+
- **Detail pages**: Header + Tabs/Sections + Related data
|
|
196
|
+
- **Create/Edit**: Full-page CrudForm or Dialog with embedded CrudForm
|
|
197
|
+
- **Settings**: Grouped sections with inline editing
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# UI Components Reference
|
|
2
|
+
|
|
3
|
+
Complete reference of all available UI components in `@open-mercato/ui`.
|
|
4
|
+
|
|
5
|
+
## Package Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
@open-mercato/ui/
|
|
9
|
+
├── primitives/ # Base UI components (shadcn-style)
|
|
10
|
+
├── backend/ # Full-featured admin components
|
|
11
|
+
├── frontend/ # Public-facing components
|
|
12
|
+
└── theme/ # Theme and provider setup
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## PRIMITIVES (`@open-mercato/ui/primitives/*`)
|
|
16
|
+
|
|
17
|
+
| Component | Import Path | Purpose |
|
|
18
|
+
|-----------|-------------|---------|
|
|
19
|
+
| **Button** | `@open-mercato/ui/primitives/button` | Core button with variants (default, outline, ghost, destructive) |
|
|
20
|
+
| **Input** | `@open-mercato/ui/primitives/input` | Text input field |
|
|
21
|
+
| **Label** | `@open-mercato/ui/primitives/label` | Form label component |
|
|
22
|
+
| **Textarea** | `@open-mercato/ui/primitives/textarea` | Multi-line text input |
|
|
23
|
+
| **Dialog** | `@open-mercato/ui/primitives/dialog` | Modal dialog (Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter) |
|
|
24
|
+
| **Tooltip** | `@open-mercato/ui/primitives/tooltip` | Hover tooltip + `SimpleTooltip` utility component |
|
|
25
|
+
| **Table** | `@open-mercato/ui/primitives/table` | Table structure (Table, TableHeader, TableBody, TableRow, TableHead, TableCell) |
|
|
26
|
+
| **Alert** | `@open-mercato/ui/primitives/alert` | Alert boxes (Alert, AlertTitle, AlertDescription) with variants |
|
|
27
|
+
| **Badge** | `@open-mercato/ui/primitives/badge` | Small badge/tag labels with variants |
|
|
28
|
+
| **Separator** | `@open-mercato/ui/primitives/separator` | Visual divider line |
|
|
29
|
+
| **Switch** | `@open-mercato/ui/primitives/switch` | Toggle switch control |
|
|
30
|
+
| **Spinner** | `@open-mercato/ui/primitives/spinner` | Loading spinner animation |
|
|
31
|
+
| **Notice** | `@open-mercato/ui/primitives/Notice` | Contextual notice/hint with variants (`error`, `info`, `warning`) and optional `compact` mode |
|
|
32
|
+
| **ErrorNotice** | `@open-mercato/ui/primitives/ErrorNotice` | Convenience wrapper around `<Notice variant="error">` with default title/message |
|
|
33
|
+
| **DataLoader** | `@open-mercato/ui/primitives/DataLoader` | Loading state wrapper with spinner and optional skeleton |
|
|
34
|
+
|
|
35
|
+
## BACKEND COMPONENTS (`@open-mercato/ui/backend/*`)
|
|
36
|
+
|
|
37
|
+
### Core Layout & Navigation
|
|
38
|
+
|
|
39
|
+
| Component | Import Path | Purpose | Key Props |
|
|
40
|
+
|-----------|-------------|---------|-----------|
|
|
41
|
+
| **AppShell** | `@open-mercato/ui/backend/AppShell` | Main application shell with sidebar, header, breadcrumbs | `navigation`, `user`, `children` |
|
|
42
|
+
| **Page, PageHeader, PageBody** | `@open-mercato/ui/backend/Page` | Page layout containers | - |
|
|
43
|
+
| **UserMenu** | `@open-mercato/ui/backend/UserMenu` | User profile dropdown with logout | `user`, `onLogout` |
|
|
44
|
+
| **FlashMessages, flash** | `@open-mercato/ui/backend/FlashMessages` | Toast notifications. Use `flash(message, type)` programmatically | Type: 'success' \| 'error' \| 'warning' \| 'info' |
|
|
45
|
+
|
|
46
|
+
### Data Display & Tables
|
|
47
|
+
|
|
48
|
+
| Component | Import Path | Purpose | Key Props |
|
|
49
|
+
|-----------|-------------|---------|-----------|
|
|
50
|
+
| **DataTable** | `@open-mercato/ui/backend/DataTable` | Feature-rich table with sorting, filtering, pagination, export, perspectives | `columns`, `data`, `filters`, `pagination`, `perspective`, `onRowClick` |
|
|
51
|
+
| **TruncatedCell** | `@open-mercato/ui/backend/TruncatedCell` | Table cell with text truncation and tooltip | `value`, `maxWidth` |
|
|
52
|
+
| **EmptyState** | `@open-mercato/ui/backend/EmptyState` | Empty state placeholder | `title`, `description`, `action`, `icon` |
|
|
53
|
+
| **RowActions** | `@open-mercato/ui/backend/RowActions` | Context menu for row actions | `items: {label, href?, onSelect?, destructive?}[]` |
|
|
54
|
+
| **FilterBar** | `@open-mercato/ui/backend/FilterBar` | Search and filter UI bar | `filters`, `values`, `onApply`, `onClear` |
|
|
55
|
+
| **ValueIcons** | `@open-mercato/ui/backend/ValueIcons` | `BooleanIcon`, `EnumBadge`, `useSeverityPreset()` | - |
|
|
56
|
+
|
|
57
|
+
### Forms
|
|
58
|
+
|
|
59
|
+
| Component | Import Path | Purpose | Key Props |
|
|
60
|
+
|-----------|-------------|---------|-----------|
|
|
61
|
+
| **CrudForm** | `@open-mercato/ui/backend/CrudForm` | Complete CRUD form with field registry, groups, custom fields, validation | `fields`, `groups`, `initialValues`, `onSubmit`, `schema`, `embedded`, `extraActions` |
|
|
62
|
+
| **FormHeader** | `@open-mercato/ui/backend/forms` | Unified page header with `edit` mode (compact, for CrudForm) and `detail` mode (large title, entity type label, status badge, Actions dropdown) | `mode`, `backHref`, `title`, `actions`, `menuActions`, `onDelete`, `statusBadge` |
|
|
63
|
+
| **FormFooter** | `@open-mercato/ui/backend/forms` | Form footer wrapping FormActionButtons with embedded/dialog layout awareness | `actions`, `embedded`, `className` |
|
|
64
|
+
| **FormActionButtons** | `@open-mercato/ui/backend/forms` | Atomic button bar: [extraActions] [Delete] [Cancel] [Save]. Shared by header and footer. | `showDelete`, `onDelete`, `cancelHref`, `submit` |
|
|
65
|
+
| **ActionsDropdown** | `@open-mercato/ui/backend/forms` | Dropdown menu for additional context actions (Convert, Send, Print). Only visible when items are provided. Delete is never inside the dropdown. | `items: ActionItem[]`, `label`, `size` |
|
|
66
|
+
| **JsonBuilder** | `@open-mercato/ui/backend/JsonBuilder` | Interactive JSON editor with "Raw JSON" and "Builder" tabs | `value`, `onChange`, `disabled` |
|
|
67
|
+
| **JsonDisplay** | `@open-mercato/ui/backend/JsonDisplay` | Read-only JSON viewer with expand/collapse | `data`, `title`, `maxInitialDepth`, `showCopy` |
|
|
68
|
+
|
|
69
|
+
### Input Components (`@open-mercato/ui/backend/inputs/*`)
|
|
70
|
+
|
|
71
|
+
| Component | Import Path | Purpose | Key Props |
|
|
72
|
+
|-----------|-------------|---------|-----------|
|
|
73
|
+
| **TagsInput** | `@open-mercato/ui/backend/inputs/TagsInput` | Multi-tag input with suggestions | `value`, `onChange`, `placeholder`, `suggestions` |
|
|
74
|
+
| **ComboboxInput** | `@open-mercato/ui/backend/inputs/ComboboxInput` | Searchable dropdown (single value) | `value`, `onChange`, `options`, `placeholder` |
|
|
75
|
+
| **PhoneNumberField** | `@open-mercato/ui/backend/inputs/PhoneNumberField` | Phone input with formatting | `value`, `onChange`, `checkDuplicate` |
|
|
76
|
+
| **LookupSelect** | `@open-mercato/ui/backend/inputs/LookupSelect` | Async lookup/search select | `value`, `onChange`, `onSearch`, `renderItem` |
|
|
77
|
+
| **SwitchableMarkdownInput** | `@open-mercato/ui/backend/inputs/SwitchableMarkdownInput` | Text/markdown toggle input | `value`, `onChange`, `placeholder` |
|
|
78
|
+
|
|
79
|
+
### Detail Page Components (`@open-mercato/ui/backend/detail/*`)
|
|
80
|
+
|
|
81
|
+
| Component | Import Path | Purpose | Key Props |
|
|
82
|
+
|-----------|-------------|---------|-----------|
|
|
83
|
+
| **DetailFieldsSection** | `@open-mercato/ui/backend/detail/DetailFieldsSection` | Entity field display with inline editing | `fields`, `entity`, `onUpdate` |
|
|
84
|
+
| **InlineTextEditor** | `@open-mercato/ui/backend/detail/InlineEditors` | Click-to-edit text field | `value`, `onSave`, `label` |
|
|
85
|
+
| **InlineMultilineEditor** | `@open-mercato/ui/backend/detail/InlineEditors` | Click-to-edit textarea | `value`, `onSave`, `label` |
|
|
86
|
+
| **InlineSelectEditor** | `@open-mercato/ui/backend/detail/InlineEditors` | Click-to-edit select | `value`, `onSave`, `options`, `label` |
|
|
87
|
+
| **NotesSection** | `@open-mercato/ui/backend/detail/NotesSection` | Notes/comments section with markdown | `notes`, `onAdd`, `onUpdate`, `onDelete` |
|
|
88
|
+
| **TagsSection** | `@open-mercato/ui/backend/detail/TagsSection` | Tag management section | `tags`, `onAdd`, `onRemove`, `suggestions` |
|
|
89
|
+
| **CustomDataSection** | `@open-mercato/ui/backend/detail/CustomDataSection` | Custom fields display | `data`, `fieldDefinitions` |
|
|
90
|
+
| **LoadingMessage** | `@open-mercato/ui/backend/detail` | Loading state with spinner | `message` |
|
|
91
|
+
| **ErrorMessage** | `@open-mercato/ui/backend/detail` | Error alert with action | `title`, `description`, `action` |
|
|
92
|
+
| **TabEmptyState** | `@open-mercato/ui/backend/detail` | Empty state for tabs | `message`, `action` |
|
|
93
|
+
|
|
94
|
+
## BACKEND UTILITIES (`@open-mercato/ui/backend/utils/*`)
|
|
95
|
+
|
|
96
|
+
| Utility | Import Path | Purpose | Key Exports |
|
|
97
|
+
|---------|-------------|---------|-------------|
|
|
98
|
+
| **apiCall** | `@open-mercato/ui/backend/utils/apiCall` | HTTP client with auth | `apiCall`, `apiCallOrThrow`, `readApiResultOrThrow` |
|
|
99
|
+
| **api** | `@open-mercato/ui/backend/utils/api` | Low-level API utils | `apiFetch` |
|
|
100
|
+
| **crud** | `@open-mercato/ui/backend/utils/crud` | CRUD operation helpers | `createCrud`, `updateCrud`, `deleteCrud` |
|
|
101
|
+
| **serverErrors** | `@open-mercato/ui/backend/utils/serverErrors` | Error mapping | `mapCrudServerErrorToFormErrors`, `createCrudFormError`, `raiseCrudError` |
|
|
102
|
+
| **customFieldValues** | `@open-mercato/ui/backend/utils/customFieldValues` | Custom field value helpers | `collectCustomFieldValues`, `normalizeCustomFieldSubmitValue` |
|
|
103
|
+
| **customFieldDefs** | `@open-mercato/ui/backend/utils/customFieldDefs` | Field definition fetching | `useCustomFieldDefinitions` |
|
|
104
|
+
| **customFieldForms** | `@open-mercato/ui/backend/utils/customFieldForms` | Form field generation | `buildCustomFieldFormFields` |
|
|
105
|
+
| **customFieldColumns** | `@open-mercato/ui/backend/utils/customFieldColumns` | Column builders | `buildCustomFieldColumns` |
|
|
106
|
+
| **customFieldFilters** | `@open-mercato/ui/backend/utils/customFieldFilters` | Filter definitions | `useCustomFieldFilters` |
|
|
107
|
+
|
|
108
|
+
## Common Usage Patterns
|
|
109
|
+
|
|
110
|
+
### Flash Messages
|
|
111
|
+
```tsx
|
|
112
|
+
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
113
|
+
|
|
114
|
+
flash('Record saved successfully', 'success')
|
|
115
|
+
flash('Failed to save record', 'error')
|
|
116
|
+
// Types: 'success' | 'error' | 'warning' | 'info'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### DataTable with Filters
|
|
120
|
+
```tsx
|
|
121
|
+
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
122
|
+
import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
|
|
123
|
+
|
|
124
|
+
const filters: FilterDef[] = [
|
|
125
|
+
{ id: 'search', type: 'text', label: 'Search', placeholder: 'Search...' },
|
|
126
|
+
{ id: 'status', type: 'select', label: 'Status', options: [...] },
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
<DataTable
|
|
130
|
+
title="Items"
|
|
131
|
+
columns={columns}
|
|
132
|
+
data={data}
|
|
133
|
+
filters={filters}
|
|
134
|
+
filterValues={filterValues}
|
|
135
|
+
onFiltersApply={handleFiltersApply}
|
|
136
|
+
onFiltersClear={handleFiltersClear}
|
|
137
|
+
pagination={{ page, pageSize, total, totalPages, onPageChange }}
|
|
138
|
+
onRowClick={(row) => router.push(`/items/${row.id}`)}
|
|
139
|
+
/>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### CrudForm
|
|
143
|
+
```tsx
|
|
144
|
+
import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
|
|
145
|
+
|
|
146
|
+
const fields: CrudField[] = [
|
|
147
|
+
{ id: 'name', label: 'Name', type: 'text', required: true },
|
|
148
|
+
{ id: 'description', label: 'Description', type: 'textarea' },
|
|
149
|
+
{ id: 'status', label: 'Status', type: 'select', options: [...] },
|
|
150
|
+
{ id: 'config', label: 'Config', type: 'custom', component: (props) => <JsonBuilder {...props} /> },
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
const groups: CrudFormGroup[] = [
|
|
154
|
+
{ id: 'basic', title: 'Basic Info', column: 1, fields: ['name', 'description'] },
|
|
155
|
+
{ id: 'settings', title: 'Settings', column: 1, fields: ['status', 'config'] },
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
<CrudForm
|
|
159
|
+
title="Create Item"
|
|
160
|
+
fields={fields}
|
|
161
|
+
groups={groups}
|
|
162
|
+
initialValues={{}}
|
|
163
|
+
onSubmit={handleSubmit}
|
|
164
|
+
submitLabel="Save"
|
|
165
|
+
embedded={true} // For use inside dialogs
|
|
166
|
+
extraActions={<Button onClick={handleDelete}>Delete</Button>}
|
|
167
|
+
/>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Dialog with Form
|
|
171
|
+
```tsx
|
|
172
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
|
|
173
|
+
import { CrudForm } from '@open-mercato/ui/backend/CrudForm'
|
|
174
|
+
|
|
175
|
+
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
176
|
+
<DialogContent className="sm:max-w-2xl [&_.grid]:!grid-cols-1">
|
|
177
|
+
<DialogHeader>
|
|
178
|
+
<DialogTitle>Edit Item</DialogTitle>
|
|
179
|
+
</DialogHeader>
|
|
180
|
+
<CrudForm
|
|
181
|
+
fields={fields}
|
|
182
|
+
groups={groups}
|
|
183
|
+
initialValues={initialValues}
|
|
184
|
+
onSubmit={handleSubmit}
|
|
185
|
+
embedded={true}
|
|
186
|
+
submitLabel="Save"
|
|
187
|
+
/>
|
|
188
|
+
</DialogContent>
|
|
189
|
+
</Dialog>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Detail Page with Inline Editing
|
|
193
|
+
```tsx
|
|
194
|
+
import { DetailFieldsSection, LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
195
|
+
|
|
196
|
+
if (isLoading) return <LoadingMessage message="Loading..." />
|
|
197
|
+
if (error) return <ErrorMessage title="Error" description={error.message} />
|
|
198
|
+
|
|
199
|
+
<DetailFieldsSection
|
|
200
|
+
fields={[
|
|
201
|
+
{ id: 'name', label: 'Name', type: 'text' },
|
|
202
|
+
{ id: 'status', label: 'Status', type: 'select', options: [...] },
|
|
203
|
+
]}
|
|
204
|
+
entity={entity}
|
|
205
|
+
onUpdate={handleUpdate}
|
|
206
|
+
/>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### API Calls
|
|
210
|
+
```tsx
|
|
211
|
+
import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
212
|
+
import { createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
213
|
+
|
|
214
|
+
// Generic API call
|
|
215
|
+
const result = await apiCall<ResponseType>('/api/endpoint', { method: 'POST', body: JSON.stringify(data) })
|
|
216
|
+
if (result.ok) {
|
|
217
|
+
// result.result contains the parsed JSON response
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// CRUD operations
|
|
221
|
+
const created = await createCrud<ItemType>('module/items', payload)
|
|
222
|
+
const updated = await updateCrud<ItemType>('module/items', id, payload)
|
|
223
|
+
const deleted = await deleteCrud('module/items', id)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Important Notes
|
|
227
|
+
|
|
228
|
+
1. **Always use `flash()` for notifications** - Don't use `alert()` or custom toast implementations
|
|
229
|
+
2. **Use `CrudForm` for forms** - Provides consistent validation, field rendering, and keyboard shortcuts
|
|
230
|
+
3. **Use `DataTable` for lists** - Includes filtering, sorting, pagination, export, and perspectives
|
|
231
|
+
4. **Use `JsonBuilder` for JSON editing** - Provides both raw JSON and visual builder modes
|
|
232
|
+
5. **Dialog forms need `embedded={true}`** - And add `[&_.grid]:!grid-cols-1` to DialogContent for single-column layout
|
|
233
|
+
6. **Support Cmd/Ctrl+Enter and Escape** - All dialogs should support these keyboard shortcuts
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: Review code changes for architecture, security, conventions, and quality compliance. Use when reviewing pull requests, code changes, or auditing code quality.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Review
|
|
7
|
+
|
|
8
|
+
Review code changes against Open Mercato architecture rules, security requirements, and quality standards.
|
|
9
|
+
|
|
10
|
+
## Review Workflow
|
|
11
|
+
|
|
12
|
+
1. **Scope**: Identify changed files. Classify by layer (entity, API route, validator, backend page, subscriber, worker, command, widget).
|
|
13
|
+
2. **Gather context**: Read `AGENTS.md` for module conventions. Check `.ai/specs/` for active specs. Read `.ai/lessons.md` for known pitfalls.
|
|
14
|
+
3. **CI/CD verification gate (MANDATORY)**: Run the checks below. Every gate MUST pass. See **CI/CD Gate** section.
|
|
15
|
+
4. **Run checklist**: Apply rules from `references/review-checklist.md`. Flag violations with severity, file, and fix suggestion.
|
|
16
|
+
5. **Test coverage**: Verify changed behavior is covered by tests. Flag missing coverage.
|
|
17
|
+
6. **Cross-module impact**: If the change touches events, extensions, or widgets, verify consumers handle the contract correctly.
|
|
18
|
+
7. **Output**: Produce the review report.
|
|
19
|
+
|
|
20
|
+
## CI/CD Verification Gate (MANDATORY)
|
|
21
|
+
|
|
22
|
+
**NEVER claim code is "ready to merge" without running these checks.** If any step fails, it MUST be fixed before the review can pass.
|
|
23
|
+
|
|
24
|
+
| # | Command | What it checks | If it fails |
|
|
25
|
+
|---|---------|----------------|-------------|
|
|
26
|
+
| 1 | `yarn generate` | Module registries are up to date | Run it — it generates missing files |
|
|
27
|
+
| 2 | `yarn typecheck` | TypeScript types are correct | Fix type errors |
|
|
28
|
+
| 3 | `yarn test` | All unit tests pass | Fix failing tests |
|
|
29
|
+
| 4 | `yarn build` | The app builds successfully | Fix build errors |
|
|
30
|
+
|
|
31
|
+
**Rules**:
|
|
32
|
+
- Steps 2 and 3 can run in parallel.
|
|
33
|
+
- Every failure is a **Critical** finding — even if it appears unrelated to the current changes.
|
|
34
|
+
- The review output MUST include actual pass/fail results. Do not assume — run and report.
|
|
35
|
+
|
|
36
|
+
## Output Format
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
# Code Review: {change description}
|
|
40
|
+
|
|
41
|
+
## Summary
|
|
42
|
+
{1-3 sentences: what the change does, overall assessment}
|
|
43
|
+
|
|
44
|
+
## CI/CD Verification
|
|
45
|
+
|
|
46
|
+
| Gate | Status | Notes |
|
|
47
|
+
|------|--------|-------|
|
|
48
|
+
| `yarn generate` | PASS/FAIL | |
|
|
49
|
+
| `yarn typecheck` | PASS/FAIL | |
|
|
50
|
+
| `yarn test` | PASS/FAIL | |
|
|
51
|
+
| `yarn build` | PASS/FAIL | |
|
|
52
|
+
|
|
53
|
+
## Findings
|
|
54
|
+
|
|
55
|
+
### Critical
|
|
56
|
+
{Security, data integrity, tenant isolation violations}
|
|
57
|
+
|
|
58
|
+
### High
|
|
59
|
+
{Architecture violations, missing required exports}
|
|
60
|
+
|
|
61
|
+
### Medium
|
|
62
|
+
{Convention violations, suboptimal patterns}
|
|
63
|
+
|
|
64
|
+
### Low
|
|
65
|
+
{Suggestions, minor improvements}
|
|
66
|
+
|
|
67
|
+
## Checklist
|
|
68
|
+
{From references/review-checklist.md — mark [x] passing, [ ] failing with explanation}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Omit empty severity sections.
|
|
72
|
+
|
|
73
|
+
## Severity Classification
|
|
74
|
+
|
|
75
|
+
| Severity | Criteria | Action |
|
|
76
|
+
|----------|----------|--------|
|
|
77
|
+
| **Critical** | Security vulnerability, cross-tenant leak, data corruption, missing auth | MUST fix before merge |
|
|
78
|
+
| **High** | Architecture violation, missing required export, broken module contract | MUST fix before merge |
|
|
79
|
+
| **Medium** | Convention violation, suboptimal pattern, missing best practice | Should fix |
|
|
80
|
+
| **Low** | Style suggestion, minor improvement | Nice to have |
|
|
81
|
+
|
|
82
|
+
## Quick Rule Reference
|
|
83
|
+
|
|
84
|
+
### Architecture
|
|
85
|
+
|
|
86
|
+
- **NO direct ORM relationships between modules** — use FK IDs, fetch separately
|
|
87
|
+
- **Always filter by `organization_id`** for tenant-scoped entities
|
|
88
|
+
- **Use DI (Awilix)** to inject services — never `new` directly
|
|
89
|
+
- **NO direct module-to-module calls** for side effects — use events
|
|
90
|
+
- **Cross-module data**: use extension entities + `data/extensions.ts`
|
|
91
|
+
|
|
92
|
+
### Security
|
|
93
|
+
|
|
94
|
+
- **Validate all inputs with zod** in `data/validators.ts`
|
|
95
|
+
- **Use `findWithDecryption`** instead of raw `em.find`/`em.findOne`
|
|
96
|
+
- **Hash passwords with bcryptjs (cost >= 10)** — never log credentials
|
|
97
|
+
- **Every endpoint MUST declare auth guards** (`requireAuth`, `requireRoles`, `requireFeatures`)
|
|
98
|
+
|
|
99
|
+
### Data Integrity
|
|
100
|
+
|
|
101
|
+
- **Never hand-write migrations** — update entities, run `yarn db:generate`
|
|
102
|
+
- **Validate migration scope** — autogenerated doesn't mean correct
|
|
103
|
+
- **Workers/subscribers MUST be idempotent**
|
|
104
|
+
- **Commands MUST be undoable** — include before/after snapshots
|
|
105
|
+
|
|
106
|
+
### Naming
|
|
107
|
+
|
|
108
|
+
- Modules: **plural, snake_case** (folders and `id`)
|
|
109
|
+
- JS/TS identifiers: **camelCase**
|
|
110
|
+
- Database: **snake_case**, table names plural
|
|
111
|
+
- Standard columns: `id`, `created_at`, `updated_at`, `deleted_at`, `is_active`, `organization_id`
|
|
112
|
+
|
|
113
|
+
### UI & HTTP
|
|
114
|
+
|
|
115
|
+
- Forms: `CrudForm` — never custom
|
|
116
|
+
- Tables: `DataTable` — never manual markup
|
|
117
|
+
- Notifications: `flash()` — never `alert()` or custom toast
|
|
118
|
+
- API calls: `apiCall`/`apiCallOrThrow` — never raw `fetch`
|
|
119
|
+
- Dialogs: `Cmd/Ctrl+Enter` (submit), `Escape` (cancel)
|
|
120
|
+
- `pageSize` MUST be <= 100
|
|
121
|
+
|
|
122
|
+
### Code Quality
|
|
123
|
+
|
|
124
|
+
- No `any` types — use zod + `z.infer`
|
|
125
|
+
- No empty `catch` blocks
|
|
126
|
+
- No one-letter variable names
|
|
127
|
+
- Boolean parsing: use `parseBooleanToken`/`parseBooleanWithDefault`
|
|
128
|
+
- Don't add docstrings/comments to code you didn't change
|
|
129
|
+
|
|
130
|
+
## Review Heuristics
|
|
131
|
+
|
|
132
|
+
1. **New files**: Check if `yarn generate` is needed. Verify auto-discovery paths.
|
|
133
|
+
2. **Entity changes**: Check if `yarn db:generate` is needed. Look for missing tenant columns.
|
|
134
|
+
3. **Migration sanity**: Inspect SQL content. Reject unrelated schema churn.
|
|
135
|
+
4. **New API routes**: Verify `openApi` export, auth guards, zod validation, tenant filtering.
|
|
136
|
+
5. **Event emitters**: Verify event is declared in `events.ts` with `as const`.
|
|
137
|
+
6. **Commands**: Verify undoable, before/after snapshots.
|
|
138
|
+
7. **UI changes**: Verify `CrudForm`/`DataTable`, `flash()`, keyboard shortcuts, loading/error states.
|
|
139
|
+
8. **Test coverage**: Verify unit/integration tests cover new behavior.
|
|
140
|
+
|
|
141
|
+
## Reference Materials
|
|
142
|
+
|
|
143
|
+
- [Review Checklist](references/review-checklist.md)
|
|
144
|
+
- [AGENTS.md](../../../AGENTS.md)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Code Review Checklist
|
|
2
|
+
|
|
3
|
+
## 1. Architecture & Module Independence
|
|
4
|
+
|
|
5
|
+
- [ ] No ORM relationships between modules — FK IDs only
|
|
6
|
+
- [ ] No direct module-to-module function calls for side effects
|
|
7
|
+
- [ ] DI (Awilix) used for service wiring
|
|
8
|
+
- [ ] No cross-tenant data exposure
|
|
9
|
+
- [ ] Code in correct location (`src/modules/<id>/`)
|
|
10
|
+
|
|
11
|
+
## 2. Security
|
|
12
|
+
|
|
13
|
+
- [ ] All inputs validated with zod in `data/validators.ts`
|
|
14
|
+
- [ ] No `any` types
|
|
15
|
+
- [ ] Auth guards on all endpoints (`requireAuth`, `requireRoles`, `requireFeatures`)
|
|
16
|
+
- [ ] Passwords hashed with bcryptjs (cost >= 10)
|
|
17
|
+
- [ ] No credentials logged or in error messages
|
|
18
|
+
- [ ] `findWithDecryption` used instead of raw `em.find`/`em.findOne`
|
|
19
|
+
- [ ] Tenant isolation: queries filter by `organization_id`
|
|
20
|
+
|
|
21
|
+
## 3. Data Integrity & ORM
|
|
22
|
+
|
|
23
|
+
- [ ] No hand-written migrations — entities updated, `yarn db:generate` run
|
|
24
|
+
- [ ] Migration scope matches PR intent (no unrelated schema churn)
|
|
25
|
+
- [ ] UUID primary keys with standard columns (`id`, `created_at`, `updated_at`)
|
|
26
|
+
- [ ] Soft delete via `deleted_at` where applicable
|
|
27
|
+
- [ ] Atomic transactions for multi-step writes
|
|
28
|
+
|
|
29
|
+
## 4. API Routes
|
|
30
|
+
|
|
31
|
+
- [ ] `openApi` exported for documentation
|
|
32
|
+
- [ ] `metadata` exported with auth guards
|
|
33
|
+
- [ ] Zod validation on request body
|
|
34
|
+
- [ ] Tenant scoping in queries
|
|
35
|
+
- [ ] `apiCall` used instead of raw `fetch`
|
|
36
|
+
- [ ] `pageSize <= 100`
|
|
37
|
+
|
|
38
|
+
## 5. Events & Commands
|
|
39
|
+
|
|
40
|
+
- [ ] Events declared in `events.ts` with `createModuleEvents` and `as const`
|
|
41
|
+
- [ ] Subscribers export `metadata` with `{ event, persistent?, id? }`
|
|
42
|
+
- [ ] Workers export `metadata` with `{ queue, id?, concurrency? }`
|
|
43
|
+
- [ ] All mutations implemented as commands with undo logic
|
|
44
|
+
- [ ] Side effects outside `withAtomicFlush`
|
|
45
|
+
|
|
46
|
+
## 6. UI & Backend Pages
|
|
47
|
+
|
|
48
|
+
- [ ] Forms use `CrudForm` (not custom)
|
|
49
|
+
- [ ] Tables use `DataTable` (not custom)
|
|
50
|
+
- [ ] Notifications use `flash()` (not alert/toast)
|
|
51
|
+
- [ ] Dialog forms have `embedded={true}`
|
|
52
|
+
- [ ] Keyboard: `Cmd/Ctrl+Enter` submit, `Escape` cancel
|
|
53
|
+
- [ ] Loading states: `LoadingMessage` or `DataLoader`
|
|
54
|
+
- [ ] Error states: `ErrorMessage` or `ErrorNotice`
|
|
55
|
+
- [ ] Empty states: `EmptyState`
|
|
56
|
+
- [ ] `RowActions` items have stable `id` values
|
|
57
|
+
- [ ] i18n: `useT()` client-side — no hardcoded strings
|
|
58
|
+
|
|
59
|
+
## 7. Naming Conventions
|
|
60
|
+
|
|
61
|
+
- [ ] Modules: plural, snake_case
|
|
62
|
+
- [ ] JS/TS identifiers: camelCase
|
|
63
|
+
- [ ] DB tables/columns: snake_case, plural table names
|
|
64
|
+
- [ ] Feature naming: `<module>.<action>` (e.g. `inventory.view`)
|
|
65
|
+
- [ ] Event naming: `module.entity.action` (singular entity, past tense)
|
|
66
|
+
|
|
67
|
+
## 8. Anti-Patterns
|
|
68
|
+
|
|
69
|
+
- [ ] No cross-module ORM links
|
|
70
|
+
- [ ] No plural entity/command/event naming
|
|
71
|
+
- [ ] No direct `fetch()` calls
|
|
72
|
+
- [ ] No custom toast/notification implementations
|
|
73
|
+
- [ ] No inline styles (use Tailwind)
|
|
74
|
+
- [ ] No hardcoded colors (use theme)
|
|
75
|
+
- [ ] No empty `catch` blocks
|
|
76
|
+
- [ ] No `any` types
|
|
77
|
+
- [ ] No missing loading/error states
|