@tailor-platform/erp-kit 0.0.1 → 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 (231) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +196 -28
  4. package/dist/cli.js +914 -0
  5. package/package.json +67 -8
  6. package/schemas/app-compose/actors.yml +34 -0
  7. package/schemas/app-compose/business-flow.yml +50 -0
  8. package/schemas/app-compose/requirements.yml +33 -0
  9. package/schemas/app-compose/resolver.yml +47 -0
  10. package/schemas/app-compose/screen.yml +81 -0
  11. package/schemas/app-compose/story.yml +67 -0
  12. package/schemas/module/command.yml +52 -0
  13. package/schemas/module/feature.yml +58 -0
  14. package/schemas/module/model.yml +70 -0
  15. package/schemas/module/module.yml +50 -0
  16. package/skills/1-module-docs/SKILL.md +111 -0
  17. package/skills/1-module-docs/references/structure.md +22 -0
  18. package/skills/2-module-feature-breakdown/SKILL.md +72 -0
  19. package/skills/2-module-feature-breakdown/references/commands.md +48 -0
  20. package/skills/2-module-feature-breakdown/references/models.md +29 -0
  21. package/skills/2-module-feature-breakdown/references/structure.md +22 -0
  22. package/skills/3-module-doc-review/SKILL.md +236 -0
  23. package/skills/3-module-doc-review/references/commands.md +54 -0
  24. package/skills/3-module-doc-review/references/models.md +29 -0
  25. package/skills/3-module-doc-review/references/testing.md +37 -0
  26. package/skills/4-module-tdd-implementation/SKILL.md +74 -0
  27. package/skills/4-module-tdd-implementation/references/commands.md +45 -0
  28. package/skills/4-module-tdd-implementation/references/db-relations.md +69 -0
  29. package/skills/4-module-tdd-implementation/references/errors.md +7 -0
  30. package/skills/4-module-tdd-implementation/references/exports.md +8 -0
  31. package/skills/4-module-tdd-implementation/references/models.md +30 -0
  32. package/skills/4-module-tdd-implementation/references/structure.md +22 -0
  33. package/skills/4-module-tdd-implementation/references/testing.md +37 -0
  34. package/skills/5-module-implementation-review/SKILL.md +408 -0
  35. package/skills/5-module-implementation-review/references/commands.md +45 -0
  36. package/skills/5-module-implementation-review/references/errors.md +7 -0
  37. package/skills/5-module-implementation-review/references/exports.md +8 -0
  38. package/skills/5-module-implementation-review/references/models.md +30 -0
  39. package/skills/5-module-implementation-review/references/testing.md +29 -0
  40. package/skills/app-compose-1-requirement-analysis/SKILL.md +89 -0
  41. package/skills/app-compose-1-requirement-analysis/references/structure.md +27 -0
  42. package/skills/app-compose-2-requirements-breakdown/SKILL.md +95 -0
  43. package/skills/app-compose-2-requirements-breakdown/references/screen-detailview.md +106 -0
  44. package/skills/app-compose-2-requirements-breakdown/references/screen-form.md +139 -0
  45. package/skills/app-compose-2-requirements-breakdown/references/screen-listview.md +153 -0
  46. package/skills/app-compose-2-requirements-breakdown/references/structure.md +27 -0
  47. package/skills/app-compose-3-doc-review/SKILL.md +116 -0
  48. package/skills/app-compose-3-doc-review/references/structure.md +27 -0
  49. package/skills/app-compose-4-design-mock/SKILL.md +256 -0
  50. package/skills/app-compose-4-design-mock/references/component.md +50 -0
  51. package/skills/app-compose-4-design-mock/references/screen-detailview.md +106 -0
  52. package/skills/app-compose-4-design-mock/references/screen-form.md +139 -0
  53. package/skills/app-compose-4-design-mock/references/screen-listview.md +153 -0
  54. package/skills/app-compose-4-design-mock/references/structure.md +27 -0
  55. package/skills/app-compose-5-design-mock-review/SKILL.md +290 -0
  56. package/skills/app-compose-5-design-mock-review/references/component.md +50 -0
  57. package/skills/app-compose-5-design-mock-review/references/screen-detailview.md +106 -0
  58. package/skills/app-compose-5-design-mock-review/references/screen-form.md +139 -0
  59. package/skills/app-compose-5-design-mock-review/references/screen-listview.md +153 -0
  60. package/skills/app-compose-6-implementation-spec/SKILL.md +127 -0
  61. package/skills/app-compose-6-implementation-spec/references/auth.md +72 -0
  62. package/skills/app-compose-6-implementation-spec/references/structure.md +27 -0
  63. package/skills/mock-scenario/SKILL.md +118 -0
  64. package/src/app.ts +1 -0
  65. package/src/cli.ts +120 -0
  66. package/src/commands/check.test.ts +30 -0
  67. package/src/commands/check.ts +66 -0
  68. package/src/commands/init.test.ts +88 -0
  69. package/src/commands/init.ts +120 -0
  70. package/src/commands/mock/index.ts +53 -0
  71. package/src/commands/mock/start.ts +179 -0
  72. package/src/commands/mock/validate.test.ts +185 -0
  73. package/src/commands/mock/validate.ts +198 -0
  74. package/src/commands/scaffold.test.ts +76 -0
  75. package/src/commands/scaffold.ts +119 -0
  76. package/src/commands/sync-check.test.ts +125 -0
  77. package/src/commands/sync-check.ts +182 -0
  78. package/src/integration.test.ts +63 -0
  79. package/src/mdschema.ts +48 -0
  80. package/src/mockServer.ts +55 -0
  81. package/src/module.ts +86 -0
  82. package/src/modules/accounting/.gitkeep +0 -0
  83. package/src/modules/coa-management/.gitkeep +0 -0
  84. package/src/modules/inventory/.gitkeep +0 -0
  85. package/src/modules/manufacturing/.gitkeep +0 -0
  86. package/src/modules/primitives/README.md +39 -0
  87. package/src/modules/primitives/command/activateCategory.test.ts +75 -0
  88. package/src/modules/primitives/command/activateCategory.ts +50 -0
  89. package/src/modules/primitives/command/activateCurrency.test.ts +70 -0
  90. package/src/modules/primitives/command/activateCurrency.ts +50 -0
  91. package/src/modules/primitives/command/activateUnit.test.ts +53 -0
  92. package/src/modules/primitives/command/activateUnit.ts +50 -0
  93. package/src/modules/primitives/command/convertAmount.test.ts +275 -0
  94. package/src/modules/primitives/command/convertAmount.ts +126 -0
  95. package/src/modules/primitives/command/convertQuantity.test.ts +219 -0
  96. package/src/modules/primitives/command/convertQuantity.ts +73 -0
  97. package/src/modules/primitives/command/createCategory.test.ts +126 -0
  98. package/src/modules/primitives/command/createCategory.ts +89 -0
  99. package/src/modules/primitives/command/createCurrency.test.ts +191 -0
  100. package/src/modules/primitives/command/createCurrency.ts +77 -0
  101. package/src/modules/primitives/command/createExchangeRate.test.ts +216 -0
  102. package/src/modules/primitives/command/createExchangeRate.ts +91 -0
  103. package/src/modules/primitives/command/createUnit.test.ts +214 -0
  104. package/src/modules/primitives/command/createUnit.ts +88 -0
  105. package/src/modules/primitives/command/deactivateCategory.test.ts +97 -0
  106. package/src/modules/primitives/command/deactivateCategory.ts +62 -0
  107. package/src/modules/primitives/command/deactivateCurrency.test.ts +85 -0
  108. package/src/modules/primitives/command/deactivateCurrency.ts +55 -0
  109. package/src/modules/primitives/command/deactivateUnit.test.ts +78 -0
  110. package/src/modules/primitives/command/deactivateUnit.ts +62 -0
  111. package/src/modules/primitives/command/setBaseCurrency.test.ts +98 -0
  112. package/src/modules/primitives/command/setBaseCurrency.ts +74 -0
  113. package/src/modules/primitives/command/setReferenceUnit.test.ts +108 -0
  114. package/src/modules/primitives/command/setReferenceUnit.ts +84 -0
  115. package/src/modules/primitives/db/currency.ts +30 -0
  116. package/src/modules/primitives/db/exchangeRate.ts +28 -0
  117. package/src/modules/primitives/db/unit.ts +32 -0
  118. package/src/modules/primitives/db/uomCategory.ts +32 -0
  119. package/src/modules/primitives/docs/commands/ActivateCategory.md +34 -0
  120. package/src/modules/primitives/docs/commands/ActivateCurrency.md +33 -0
  121. package/src/modules/primitives/docs/commands/ActivateUnit.md +34 -0
  122. package/src/modules/primitives/docs/commands/ConvertAmount.md +50 -0
  123. package/src/modules/primitives/docs/commands/ConvertQuantity.md +43 -0
  124. package/src/modules/primitives/docs/commands/CreateCategory.md +44 -0
  125. package/src/modules/primitives/docs/commands/CreateCurrency.md +47 -0
  126. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +48 -0
  127. package/src/modules/primitives/docs/commands/CreateUnit.md +48 -0
  128. package/src/modules/primitives/docs/commands/DeactivateCategory.md +38 -0
  129. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +38 -0
  130. package/src/modules/primitives/docs/commands/DeactivateUnit.md +38 -0
  131. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +39 -0
  132. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +43 -0
  133. package/src/modules/primitives/docs/features/currency-definitions.md +55 -0
  134. package/src/modules/primitives/docs/features/exchange-rates.md +61 -0
  135. package/src/modules/primitives/docs/features/unit-conversion.md +66 -0
  136. package/src/modules/primitives/docs/features/uom-categories.md +52 -0
  137. package/src/modules/primitives/docs/models/Currency.md +45 -0
  138. package/src/modules/primitives/docs/models/ExchangeRate.md +33 -0
  139. package/src/modules/primitives/docs/models/Unit.md +46 -0
  140. package/src/modules/primitives/docs/models/UoMCategory.md +44 -0
  141. package/src/modules/primitives/generated/kysely-tailordb.ts +95 -0
  142. package/src/modules/primitives/index.ts +40 -0
  143. package/src/modules/primitives/lib/errors.ts +138 -0
  144. package/src/modules/primitives/lib/types.ts +20 -0
  145. package/src/modules/primitives/module.ts +66 -0
  146. package/src/modules/primitives/permissions.ts +18 -0
  147. package/src/modules/primitives/tailor.config.ts +11 -0
  148. package/src/modules/primitives/testing/fixtures.ts +161 -0
  149. package/src/modules/product-management/.gitkeep +0 -0
  150. package/src/modules/purchase/.gitkeep +0 -0
  151. package/src/modules/sales/.gitkeep +0 -0
  152. package/src/modules/shared/createContext.test.ts +39 -0
  153. package/src/modules/shared/createContext.ts +15 -0
  154. package/src/modules/shared/defineCommand.test.ts +42 -0
  155. package/src/modules/shared/defineCommand.ts +19 -0
  156. package/src/modules/shared/definePermissions.test.ts +146 -0
  157. package/src/modules/shared/definePermissions.ts +94 -0
  158. package/src/modules/shared/entityTypes.ts +15 -0
  159. package/src/modules/shared/errors.ts +22 -0
  160. package/src/modules/shared/index.ts +1 -0
  161. package/src/modules/shared/internal.ts +13 -0
  162. package/src/modules/shared/requirePermission.test.ts +47 -0
  163. package/src/modules/shared/requirePermission.ts +8 -0
  164. package/src/modules/shared/types.ts +4 -0
  165. package/src/modules/supplier-management/.gitkeep +0 -0
  166. package/src/modules/supplier-portal/.gitkeep +0 -0
  167. package/src/modules/testing/index.ts +120 -0
  168. package/src/modules/user-management/README.md +38 -0
  169. package/src/modules/user-management/command/activateUser.test.ts +112 -0
  170. package/src/modules/user-management/command/activateUser.ts +67 -0
  171. package/src/modules/user-management/command/assignPermissionToRole.test.ts +119 -0
  172. package/src/modules/user-management/command/assignPermissionToRole.ts +87 -0
  173. package/src/modules/user-management/command/assignRoleToUser.test.ts +162 -0
  174. package/src/modules/user-management/command/assignRoleToUser.ts +93 -0
  175. package/src/modules/user-management/command/createPermission.test.ts +143 -0
  176. package/src/modules/user-management/command/createPermission.ts +66 -0
  177. package/src/modules/user-management/command/createRole.test.ts +115 -0
  178. package/src/modules/user-management/command/createRole.ts +52 -0
  179. package/src/modules/user-management/command/createUser.test.ts +198 -0
  180. package/src/modules/user-management/command/createUser.ts +85 -0
  181. package/src/modules/user-management/command/deactivateUser.test.ts +112 -0
  182. package/src/modules/user-management/command/deactivateUser.ts +67 -0
  183. package/src/modules/user-management/command/logAuditEvent.test.ts +179 -0
  184. package/src/modules/user-management/command/logAuditEvent.ts +59 -0
  185. package/src/modules/user-management/command/reactivateUser.test.ts +115 -0
  186. package/src/modules/user-management/command/reactivateUser.ts +67 -0
  187. package/src/modules/user-management/command/revokePermissionFromRole.test.ts +112 -0
  188. package/src/modules/user-management/command/revokePermissionFromRole.ts +81 -0
  189. package/src/modules/user-management/command/revokeRoleFromUser.test.ts +112 -0
  190. package/src/modules/user-management/command/revokeRoleFromUser.ts +81 -0
  191. package/src/modules/user-management/db/auditEvent.ts +47 -0
  192. package/src/modules/user-management/db/permission.ts +31 -0
  193. package/src/modules/user-management/db/role.ts +28 -0
  194. package/src/modules/user-management/db/rolePermission.ts +44 -0
  195. package/src/modules/user-management/db/user.ts +38 -0
  196. package/src/modules/user-management/db/userRole.ts +44 -0
  197. package/src/modules/user-management/docs/commands/ActivateUser.md +36 -0
  198. package/src/modules/user-management/docs/commands/AssignPermissionToRole.md +39 -0
  199. package/src/modules/user-management/docs/commands/AssignRoleToUser.md +43 -0
  200. package/src/modules/user-management/docs/commands/CreatePermission.md +35 -0
  201. package/src/modules/user-management/docs/commands/CreateRole.md +35 -0
  202. package/src/modules/user-management/docs/commands/CreateUser.md +41 -0
  203. package/src/modules/user-management/docs/commands/DeactivateUser.md +38 -0
  204. package/src/modules/user-management/docs/commands/LogAuditEvent.md +37 -0
  205. package/src/modules/user-management/docs/commands/ReactivateUser.md +37 -0
  206. package/src/modules/user-management/docs/commands/RevokePermissionFromRole.md +40 -0
  207. package/src/modules/user-management/docs/commands/RevokeRoleFromUser.md +40 -0
  208. package/src/modules/user-management/docs/features/audit-trail.md +80 -0
  209. package/src/modules/user-management/docs/features/role-based-access-control.md +76 -0
  210. package/src/modules/user-management/docs/features/user-account-management.md +64 -0
  211. package/src/modules/user-management/docs/models/AuditEvent.md +34 -0
  212. package/src/modules/user-management/docs/models/Permission.md +31 -0
  213. package/src/modules/user-management/docs/models/Role.md +31 -0
  214. package/src/modules/user-management/docs/models/RolePermission.md +33 -0
  215. package/src/modules/user-management/docs/models/User.md +47 -0
  216. package/src/modules/user-management/docs/models/UserRole.md +34 -0
  217. package/src/modules/user-management/docs/plans/2026-01-30-flattened-permissions-design.md +52 -0
  218. package/src/modules/user-management/executor/recomputeOnRolePermissionChange.ts +61 -0
  219. package/src/modules/user-management/generated/enums.ts +24 -0
  220. package/src/modules/user-management/generated/kysely-tailordb.ts +112 -0
  221. package/src/modules/user-management/index.ts +32 -0
  222. package/src/modules/user-management/lib/errors.ts +81 -0
  223. package/src/modules/user-management/lib/recomputeUserPermissions.ts +53 -0
  224. package/src/modules/user-management/lib/types.ts +31 -0
  225. package/src/modules/user-management/module.ts +77 -0
  226. package/src/modules/user-management/permissions.ts +15 -0
  227. package/src/modules/user-management/tailor.config.ts +11 -0
  228. package/src/modules/user-management/testing/fixtures.ts +98 -0
  229. package/src/schemas.ts +25 -0
  230. package/src/testing.ts +10 -0
  231. package/src/util.ts +3 -0
@@ -0,0 +1,256 @@
1
+ ---
2
+ name: app-compose-4-design-mock
3
+ description: Create design mockups from screen specifications using .pen files. Use after completing documentation review with app-compose-3-doc-review. Generates visual UI mockups with shadcn components.
4
+ ---
5
+
6
+ # Design Mockup Workflow
7
+
8
+ Create visual UI mockups from Tier 3 screen documentation using .pen design files and shadcn components.
9
+
10
+ ## Prerequisites
11
+
12
+ Tier 3 documentation must exist:
13
+
14
+ - `docs/screen/*.md` (screen specifications)
15
+ - Design system source file (contains reusable shadcn components)
16
+
17
+ ## Workflow Phases
18
+
19
+ ```
20
+ SETUP → ANALYZE → DESIGN → VALIDATE
21
+ ```
22
+
23
+ ### Phase 1: Setup
24
+
25
+ Ensure the design system file exists at the application's `docs/design.pen`.
26
+
27
+ This provides access to 87+ shadcn reusable components including:
28
+
29
+ - **Layout**: Sidebar, Card, Table, Data Table
30
+ - **Form**: Input, Textarea, Select, Checkbox, Radio
31
+ - **Navigation**: Breadcrumb, Pagination, Tabs
32
+ - **Feedback**: Badge, Alert, Dialog
33
+ - **Actions**: Button (Default, Secondary, Outline, Ghost, Destructive)
34
+
35
+ ### Phase 2: Analyze
36
+
37
+ 1. **Read** all screen documentation in `docs/screen/*.md`
38
+ 2. **Identify** screen types: ListView, DetailView, Form
39
+ 3. **Map** UI requirements to available components
40
+ 4. **Plan** layout hierarchy based on sidebar navigation
41
+
42
+ ### Phase 3: Design
43
+
44
+ Create mockups using Pencil MCP tools.
45
+
46
+ #### 3.1 Get Editor State
47
+
48
+ ```
49
+ mcp__pencil__get_editor_state(include_schema: true)
50
+ mcp__pencil__open_document(filePathOrTemplate: "docs/design.pen")
51
+ ```
52
+
53
+ #### 3.2 Discover Components
54
+
55
+ Search for reusable components:
56
+
57
+ ```
58
+ mcp__pencil__batch_get(
59
+ filePath: "docs/design.pen",
60
+ patterns: [{ "reusable": true }],
61
+ searchDepth: 3
62
+ )
63
+ ```
64
+
65
+ Key component patterns to search:
66
+
67
+ | Component Type | Search Pattern |
68
+ | -------------- | ----------------------- |
69
+ | Sidebar | `{ "name": "Sidebar" }` |
70
+ | Card | `{ "name": "Card" }` |
71
+ | Table | `{ "name": "Table" }` |
72
+ | Input | `{ "name": "Input" }` |
73
+ | Button | `{ "name": "Button" }` |
74
+ | Badge | `{ "name": "Badge" }` |
75
+
76
+ #### 3.3 Create Screen Frames
77
+
78
+ Use `batch_design` to create screens. Standard pattern:
79
+
80
+ ```javascript
81
+ // Create screen frame
82
+ screen = I("document", {
83
+ type: "frame",
84
+ name: "{screen-name}",
85
+ x: { x },
86
+ y: { y },
87
+ width: 1280,
88
+ height: 800,
89
+ fill: "$--background",
90
+ layout: "horizontal",
91
+ });
92
+
93
+ // Add sidebar
94
+ sidebar = I(screen, {
95
+ type: "ref",
96
+ ref: "{sidebar-component-id}",
97
+ height: "fill_container",
98
+ });
99
+ U(sidebar + "/{title-id}", { content: "{app-name}" });
100
+
101
+ // Add main content area
102
+ mainContent = I(screen, {
103
+ type: "frame",
104
+ layout: "vertical",
105
+ gap: 24,
106
+ padding: 32,
107
+ width: "fill_container",
108
+ height: "fill_container",
109
+ });
110
+ ```
111
+
112
+ #### 3.4 Layout Organization
113
+
114
+ Arrange screens by navigation hierarchy:
115
+
116
+ | Row | Y Position | Screens |
117
+ | --- | ---------- | ------------------------- |
118
+ | 0 | 0 | Dashboard (Home) |
119
+ | 1 | 1000 | Primary feature screens |
120
+ | 2 | 2000 | Secondary feature screens |
121
+ | 3 | 3000 | Tertiary feature screens |
122
+ | N | N\*1000 | Settings/Admin screens |
123
+
124
+ Flow within rows: List → Detail → Create/Edit Form
125
+
126
+ #### 3.5 Component Patterns by Screen Type
127
+
128
+ **ListView Pattern:**
129
+
130
+ ```javascript
131
+ // Header with title and action button
132
+ header = I(mainContent, {
133
+ type: "frame",
134
+ justifyContent: "space_between",
135
+ alignItems: "center",
136
+ width: "fill_container",
137
+ });
138
+ title = I(header, {
139
+ type: "text",
140
+ content: "{page-title}",
141
+ fontSize: 24,
142
+ fontWeight: "600",
143
+ fill: "$--foreground",
144
+ });
145
+ actionBtn = I(header, { type: "ref", ref: "{button-component-id}" });
146
+
147
+ // Filter row
148
+ filterRow = I(mainContent, { type: "frame", gap: 12, alignItems: "center" });
149
+
150
+ // Data table in card
151
+ tableCard = I(mainContent, {
152
+ type: "ref",
153
+ ref: "{card-component-id}",
154
+ width: "fill_container",
155
+ });
156
+ ```
157
+
158
+ **Form Pattern:**
159
+
160
+ ```javascript
161
+ // Card with header
162
+ formCard = I(mainContent, {
163
+ type: "ref",
164
+ ref: "{card-component-id}",
165
+ width: "fill_container",
166
+ });
167
+ cardTitle = I(formCard + "/{header-slot-id}", {
168
+ type: "text",
169
+ content: "{form-title}",
170
+ fontSize: 18,
171
+ fontWeight: "600",
172
+ });
173
+
174
+ // Form fields in content slot
175
+ formContent = I(formCard + "/{content-slot-id}", {
176
+ type: "frame",
177
+ layout: "vertical",
178
+ gap: 16,
179
+ width: "fill_container",
180
+ });
181
+ inputField = I(formContent, {
182
+ type: "ref",
183
+ ref: "{input-component-id}",
184
+ width: "fill_container",
185
+ });
186
+ U(inputField + "/{label-id}", { content: "{field-label}" });
187
+
188
+ // Action buttons in footer slot
189
+ btnRow = I(formCard + "/{footer-slot-id}", {
190
+ type: "frame",
191
+ gap: 12,
192
+ justifyContent: "flex_end",
193
+ });
194
+ ```
195
+
196
+ **DetailView Pattern:**
197
+
198
+ ```javascript
199
+ // Breadcrumb navigation
200
+ bcRow = I(mainContent, { type: "frame", gap: 4, alignItems: "center" });
201
+
202
+ // Info cards
203
+ infoCard = I(mainContent, {
204
+ type: "ref",
205
+ ref: "{card-component-id}",
206
+ width: "fill_container",
207
+ });
208
+
209
+ // Action section
210
+ actionRow = I(mainContent, { type: "frame", gap: 12 });
211
+ ```
212
+
213
+ ### Phase 4: Validate
214
+
215
+ Take screenshots to verify each screen:
216
+
217
+ ```
218
+ mcp__pencil__get_screenshot(filePath: "docs/design.pen", nodeId: "{frame-id}")
219
+ ```
220
+
221
+ Check for:
222
+
223
+ - Consistent sidebar navigation across screens
224
+ - Proper alignment and spacing
225
+ - Correct component usage
226
+ - Readable text and labels
227
+
228
+ ## Tips
229
+
230
+ - Always check component structure with `batch_get` before using `U()` updates
231
+ - Use `find_empty_space_on_canvas` for positioning new screens
232
+ - Sidebar navigation should highlight the current screen's menu item
233
+ - Keep operations under 25 per `batch_design` call
234
+ - Use consistent user profile in sidebar footer across all screens
235
+
236
+ ## Validation Commands
237
+
238
+ ```
239
+ # Check layout structure
240
+ mcp__pencil__snapshot_layout(filePath: "docs/design.pen", maxDepth: 0)
241
+
242
+ # Verify screen screenshot
243
+ mcp__pencil__get_screenshot(filePath: "docs/design.pen", nodeId: "{frame-id}")
244
+ ```
245
+
246
+ ## Next Step
247
+
248
+ After completing design mockups, use `/app-compose-5-design-mock-review` to review and validate the mockups against screen specifications.
249
+
250
+ ## References
251
+
252
+ - [Application structure](references/structure.md)
253
+ - [ListView screen](references/screen-listview.md)
254
+ - [Form screen](references/screen-form.md)
255
+ - [DetailView screen](references/screen-detailview.md)
256
+ - [Page components](references/component.md)
@@ -0,0 +1,50 @@
1
+ # Page Components
2
+
3
+ Page-specific components are placed in a `components/` directory, separated from page.tsx.
4
+
5
+ ```
6
+ {page-name}/
7
+ ├── components/
8
+ │ └── *.tsx
9
+ └── page.tsx
10
+ ```
11
+
12
+ ## Fragment Collocation
13
+
14
+ Components define and export their own GraphQL Fragment for the data they display. The parent page imports the Fragment and includes it in the query.
15
+
16
+ Use `graphql`, `FragmentOf`, and `readFragment` from `@/graphql`.
17
+
18
+ ```tsx
19
+ // components/user-card.tsx
20
+ import { graphql, type FragmentOf, readFragment } from "@/graphql";
21
+
22
+ export const UserCardFragment = graphql(`
23
+ fragment UserCard on User {
24
+ id
25
+ name
26
+ email
27
+ }
28
+ `);
29
+
30
+ export const UserCard = ({ user }: { user: FragmentOf<typeof UserCardFragment> }) => {
31
+ const data = readFragment(UserCardFragment, user);
32
+ return <div>{data.name}</div>;
33
+ };
34
+ ```
35
+
36
+ ```tsx
37
+ // page.tsx
38
+ import { UserCard, UserCardFragment } from "./components/user-card";
39
+
40
+ const UserQuery = graphql(
41
+ `
42
+ query User($id: ID!) {
43
+ user(id: $id) {
44
+ ...UserCard
45
+ }
46
+ }
47
+ `,
48
+ [UserCardFragment],
49
+ );
50
+ ```
@@ -0,0 +1,106 @@
1
+ # DetailView Screen Implementation
2
+
3
+ Implementation pattern for screens with `Screen Type: DetailView`.
4
+ Assumes `page.md` and `component.md` rules.
5
+
6
+ ## File Structure
7
+
8
+ ```
9
+ {screen-path}/[id]/
10
+ ├── components/
11
+ │ ├── {screen-name}-detail.tsx # Main content (left column)
12
+ │ └── {screen-name}-actions.tsx # Action sidebar (right column)
13
+ ├── edit/
14
+ │ ├── components/
15
+ │ │ └── edit-{screen-name}-form.tsx
16
+ │ └── page.tsx
17
+ └── page.tsx
18
+ ```
19
+
20
+ ## Layout
21
+
22
+ - Two-column layout: main content on the left, actions on the right.
23
+
24
+ ```tsx
25
+ const ResourcePage = () => {
26
+ const { id } = useParams();
27
+ const [{ data, error, fetching }, reexecuteQuery] = useQuery({
28
+ query: ResourceQuery,
29
+ variables: { id: id! },
30
+ });
31
+
32
+ if (fetching) return <Loading />;
33
+ if (error || !data?.resource) return <ErrorFallback ... />;
34
+
35
+ return (
36
+ <Layout columns={2} title="Resource Detail">
37
+ <Layout.Column>
38
+ <ResourceDetail resource={data.resource} />
39
+ </Layout.Column>
40
+ <Layout.Column>
41
+ <ResourceActions resource={data.resource} />
42
+ </Layout.Column>
43
+ </Layout>
44
+ );
45
+ };
46
+ ```
47
+
48
+ ## Left Column: Detail Component
49
+
50
+ Stack `DescriptionCard` and related tables vertically with `space-y-6`.
51
+
52
+ - `DescriptionCard` (`@tailor-platform/app-shell`): renders key-value fields declaratively.
53
+ - Complex content (tables, timelines): wrap in `<div className="rounded-lg border bg-card p-6">`.
54
+
55
+ ### DescriptionCard
56
+
57
+ ```tsx
58
+ <DescriptionCard
59
+ data={resource}
60
+ title="Overview"
61
+ columns={3}
62
+ fields={[
63
+ { key: "name", label: "Name", meta: { copyable: true } },
64
+ {
65
+ key: "status",
66
+ label: "Status",
67
+ type: "badge",
68
+ meta: { badgeVariantMap: { ACTIVE: "success", PENDING: "warning" } },
69
+ },
70
+ { type: "divider" },
71
+ {
72
+ key: "createdAt",
73
+ label: "Created At",
74
+ type: "date",
75
+ meta: { dateFormat: "medium" },
76
+ },
77
+ ]}
78
+ />
79
+ ```
80
+
81
+ Field types: `"text"` (default), `"badge"`, `"money"`, `"date"`, `"link"`, `"address"`, `"reference"`, `"divider"`
82
+
83
+ ## Right Column: Actions Component
84
+
85
+ Wrap in a `Card` component. Use `Button variant="ghost"` for each action item.
86
+
87
+ ```tsx
88
+ <Card>
89
+ <CardHeader>
90
+ <CardTitle>Actions</CardTitle>
91
+ </CardHeader>
92
+ <CardContent className="space-y-2">
93
+ <Button variant="ghost" className="w-full justify-start gap-2" asChild>
94
+ <Link to="edit">✎ Edit</Link>
95
+ </Button>
96
+ <Button variant="ghost" className="w-full justify-start gap-2" onClick={handler}>
97
+ ✓ Approve
98
+ </Button>
99
+ </CardContent>
100
+ </Card>
101
+ ```
102
+
103
+ - Navigation: `<Button variant="ghost" asChild><Link to="...">`
104
+ - Mutation: `<Button variant="ghost" onClick={handler}>` with custom resolvers (see `backend/resolvers.md`)
105
+ - Conditional: show/hide based on status
106
+ - Multiple cards: stack with `<div className="space-y-6">`
@@ -0,0 +1,139 @@
1
+ # Form Screen Implementation
2
+
3
+ Implementation pattern for screens with `Screen Type: Form`.
4
+ Assumes `page.md` and `component.md` rules.
5
+
6
+ ## File Structure
7
+
8
+ ```
9
+ {screen-path}/
10
+ ├── components/
11
+ │ └── {screen-name}-form.tsx # Form component with validation
12
+ └── page.tsx
13
+ ```
14
+
15
+ ## Page Component (page.tsx)
16
+
17
+ Form pages delegate mutation logic to the form component.
18
+
19
+ ```tsx
20
+ const ScreenNamePage = () => (
21
+ <Layout columns={1} title="Screen Title">
22
+ <Layout.Column>
23
+ <ScreenNameForm />
24
+ </Layout.Column>
25
+ </Layout>
26
+ );
27
+ ```
28
+
29
+ For edit forms that need existing data, co-locate data fetching in the page component:
30
+
31
+ ```tsx
32
+ const EditPage = () => {
33
+ const { id } = useParams();
34
+ const [{ data, error, fetching }] = useQuery({
35
+ query: ResourceQuery,
36
+ variables: { id: id! },
37
+ });
38
+
39
+ if (fetching) return <Loading />;
40
+ if (error || !data?.resource) return <ErrorFallback ... />;
41
+
42
+ return (
43
+ <Layout columns={1} title="Edit Resource">
44
+ <Layout.Column>
45
+ <EditResourceForm resource={data.resource} />
46
+ </Layout.Column>
47
+ </Layout>
48
+ );
49
+ };
50
+ ```
51
+
52
+ ## Form Component (components/{screen-name}-form.tsx)
53
+
54
+ ### Technology Stack
55
+
56
+ - `react-hook-form` — form state management
57
+ - `zod` + `@hookform/resolvers/zod` — validation
58
+ - `useMutation` (urql) — GraphQL mutation
59
+ - `useNavigate` (@tailor-platform/app-shell) — post-submit navigation
60
+
61
+ ### Pattern
62
+
63
+ ```tsx
64
+ const formSchema = z.object({
65
+ title: z.string().min(1, "Title is required"),
66
+ description: z.string().optional(),
67
+ });
68
+
69
+ type FormValues = z.infer<typeof formSchema>;
70
+
71
+ export const ScreenNameForm = () => {
72
+ const navigate = useNavigate();
73
+ const [, createResource] = useMutation(CreateMutation);
74
+
75
+ const form = useForm<FormValues>({
76
+ resolver: zodResolver(formSchema),
77
+ defaultValues: { title: "", description: "" },
78
+ });
79
+
80
+ const onSubmit = (values: FormValues) => {
81
+ void createResource({ input: values }).then((result) => {
82
+ if (!result.error) {
83
+ void navigate("..");
84
+ }
85
+ });
86
+ };
87
+
88
+ return (
89
+ <Form {...form}>
90
+ <form onSubmit={(e) => void form.handleSubmit(onSubmit)(e)} className="max-w-md space-y-4">
91
+ <FormField
92
+ control={form.control}
93
+ name="title"
94
+ render={({ field }) => (
95
+ <FormItem>
96
+ <FormLabel>Title</FormLabel>
97
+ <FormControl>
98
+ <Input placeholder="Enter title" {...field} />
99
+ </FormControl>
100
+ <FormMessage />
101
+ </FormItem>
102
+ )}
103
+ />
104
+ <div className="flex gap-2">
105
+ <Button type="submit">Create</Button>
106
+ <Button type="button" variant="outline" onClick={() => void navigate("..")}>
107
+ Cancel
108
+ </Button>
109
+ </div>
110
+ </form>
111
+ </Form>
112
+ );
113
+ };
114
+ ```
115
+
116
+ ## Field Type Mapping
117
+
118
+ | Field Type | Component | Zod Schema |
119
+ | ---------- | ------------------------------ | ------------------------------- |
120
+ | Text | `<Input />` | `z.string()` |
121
+ | Textarea | `<textarea className="..." />` | `z.string()` |
122
+ | Dropdown | `<Select />` | `z.string()` or `z.enum([...])` |
123
+ | Date | `<Input type="date" />` | `z.string()` (ISO format) |
124
+ | Number | `<Input type="number" />` | `z.coerce.number()` |
125
+ | Email | `<Input type="email" />` | `z.string().email()` |
126
+ | Checkbox | `<Checkbox />` | `z.boolean()` |
127
+ | Radio | `<RadioGroup />` | `z.enum([...])` |
128
+
129
+ ## Validation Mapping
130
+
131
+ - **Required: Yes** → `.min(1, "Field is required")` (string) / `.positive()` (number)
132
+ - **Required: No** → `.optional()`
133
+
134
+ ## Key Points
135
+
136
+ - Set `defaultValues` for all fields (empty string, false, etc.)
137
+ - Navigate to `".."` after successful mutation
138
+ - Cancel button must use `type="button"` to prevent form submit
139
+ - For edit forms, accept fragment data as props and pre-fill `defaultValues`
@@ -0,0 +1,153 @@
1
+ # ListView Screen Implementation
2
+
3
+ Implementation pattern for screens with `Screen Type: ListView`.
4
+ Assumes `page.md` and `component.md` rules.
5
+
6
+ ## File Structure
7
+
8
+ ```
9
+ {screen-path}/
10
+ ├── components/
11
+ │ └── {screen-name}-table.tsx # Table component with fragments
12
+ └── page.tsx
13
+ ```
14
+
15
+ ## Page Component (page.tsx)
16
+
17
+ Data fetching and `Layout` must be co-located in the same page component.
18
+ Do NOT split into an inner Content component — `Layout` requires `Layout.Column` as direct children.
19
+
20
+ ```tsx
21
+ const ResourcesQuery = graphql(
22
+ `
23
+ query Resources {
24
+ resources {
25
+ ...ResourceTable
26
+ }
27
+ }
28
+ `,
29
+ [ResourceTableFragment],
30
+ );
31
+
32
+ const ResourcesPage = () => {
33
+ const [{ data, error, fetching }, reexecuteQuery] = useQuery({
34
+ query: ResourcesQuery,
35
+ });
36
+
37
+ if (fetching) return <Loading />;
38
+
39
+ if (error || !data) {
40
+ return (
41
+ <ErrorFallback
42
+ title="Failed to load resources"
43
+ message="An error occurred while fetching the list."
44
+ onReset={() => reexecuteQuery({ requestPolicy: "network-only" })}
45
+ />
46
+ );
47
+ }
48
+
49
+ return (
50
+ <Layout
51
+ columns={1}
52
+ title="Resources"
53
+ actions={[
54
+ <Button key="create" asChild>
55
+ <Link to="create">Create</Link>
56
+ </Button>,
57
+ ]}
58
+ >
59
+ <Layout.Column>
60
+ <ResourceTable data={data.resources} />
61
+ </Layout.Column>
62
+ </Layout>
63
+ );
64
+ };
65
+ ```
66
+
67
+ ## Table Component (components/{screen-name}-table.tsx)
68
+
69
+ ### Fragment Collocation
70
+
71
+ Define a row fragment and a table fragment wrapping the Connection type.
72
+
73
+ ```tsx
74
+ const ResourceRowFragment = graphql(`
75
+ fragment ResourceRow on Resource {
76
+ id
77
+ title
78
+ status
79
+ createdAt
80
+ }
81
+ `);
82
+
83
+ export const ResourceTableFragment = graphql(
84
+ `
85
+ fragment ResourceTable on ResourceConnection {
86
+ edges {
87
+ node {
88
+ ...ResourceRow
89
+ }
90
+ }
91
+ }
92
+ `,
93
+ [ResourceRowFragment],
94
+ );
95
+ ```
96
+
97
+ ### Row Component
98
+
99
+ ```tsx
100
+ const ResourceRow = ({ resource: resourceFragment }: ResourceRowProps) => {
101
+ const resource = readFragment(ResourceRowFragment, resourceFragment);
102
+ return (
103
+ <TableRow>
104
+ <TableCell>{resource.title}</TableCell>
105
+ <TableCell>
106
+ <Badge variant={resource.status === "ACTIVE" ? "default" : "secondary"}>
107
+ {resource.status}
108
+ </Badge>
109
+ </TableCell>
110
+ <TableCell>
111
+ <Button variant="ghost" size="sm" asChild>
112
+ <Link to={resource.id}>View</Link>
113
+ </Button>
114
+ </TableCell>
115
+ </TableRow>
116
+ );
117
+ };
118
+ ```
119
+
120
+ ### Empty State
121
+
122
+ ```tsx
123
+ if (connection.edges.length === 0) {
124
+ return (
125
+ <EmptyState
126
+ title="No resources"
127
+ message="Get started by creating a new resource."
128
+ action={
129
+ <Button asChild>
130
+ <Link to="create">Create</Link>
131
+ </Button>
132
+ }
133
+ />
134
+ );
135
+ }
136
+ ```
137
+
138
+ ## Column Property Mapping
139
+
140
+ | Property | Implementation |
141
+ | --------------- | ----------------------------------------------------------- |
142
+ | Hideable: Yes | Column visibility state to toggle show/hide |
143
+ | Sortable: Yes | Sort icon on `<TableHead>` + onClick to toggle query vars |
144
+ | Filterable: Yes | Filter UI above table (`<Select />`) |
145
+ | Searchable: Yes | Search input above table (`<Input placeholder="Search" />`) |
146
+
147
+ ## Key Points
148
+
149
+ - Use `<Badge />` for status columns
150
+ - Format dates with `toLocaleDateString()`
151
+ - Use `<EmptyState />` with Create action for empty lists
152
+ - Add a View button per row to navigate to the detail page
153
+ - Iterate data using Connection type `edges.node` pattern