@nebulit/embuilder 0.1.47 → 0.1.48
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/package.json +1 -1
- package/templates/backend/AGENTS.md +56 -54
- package/templates/backend/prompt.md +460 -91
- package/templates/frontend/prompt.md +5 -2
- package/templates/prompt.md +2 -1
package/package.json
CHANGED
|
@@ -104,20 +104,22 @@ import { ContextEvents } from '../../events/ContextEvents';
|
|
|
104
104
|
- Use proper `initialState` function (not empty object `{}`) in `DeciderSpecification.for()`
|
|
105
105
|
- Do NOT add explicit type arguments - let TypeScript infer (avoids TS2558)
|
|
106
106
|
- Switch statements: always use explicit `break` to prevent fallthrough bugs
|
|
107
|
+
- you must not change the signature of evolve - it has (state:State, event:Event)
|
|
108
|
+
- you must not change the signature of decide - it has (state:State, command:Command) - all data to verify the command must be passed via state.
|
|
107
109
|
|
|
108
110
|
**Structure**:
|
|
109
111
|
```typescript
|
|
110
112
|
type State = { /* track relevant state */ };
|
|
111
113
|
const initialState = (): State => ({ /* defaults */ });
|
|
112
114
|
const decide = (state: State, cmd: Command): Event[] => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
if (invalidCondition) throw 'error.code';
|
|
116
|
+
return [createEvent(cmd)];
|
|
115
117
|
};
|
|
116
118
|
const evolve = (state: State, event: ContextEvents): State => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
switch (event.type) {
|
|
120
|
+
case 'EventType': return { ...state, field: newValue };
|
|
121
|
+
default: return state;
|
|
122
|
+
}
|
|
121
123
|
};
|
|
122
124
|
```
|
|
123
125
|
|
|
@@ -133,14 +135,14 @@ const evolve = (state: State, event: ContextEvents): State => {
|
|
|
133
135
|
**Projection Structure**:
|
|
134
136
|
```typescript
|
|
135
137
|
export const ProjectionName = postgreSQLRawSQLProjection({
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
canHandle: ['Event1', 'Event2'],
|
|
139
|
+
evolve: (event: Event1 | Event2) => {
|
|
140
|
+
switch (event.type) {
|
|
141
|
+
case 'Event1': return sql(db(table).insert({...}).onConflict().merge());
|
|
142
|
+
case 'Event2': return sql(db(table).where({...}).delete());
|
|
143
|
+
default: return [];
|
|
144
|
+
}
|
|
142
145
|
}
|
|
143
|
-
}
|
|
144
146
|
});
|
|
145
147
|
```
|
|
146
148
|
|
|
@@ -158,22 +160,22 @@ import { createServiceClient } from '../../common/supabaseClient';
|
|
|
158
160
|
const config = { schedule: '*/30 * * * * *', endpoint: "work_queue_table" };
|
|
159
161
|
|
|
160
162
|
export const processor = {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
163
|
+
start: () => {
|
|
164
|
+
cron.schedule(config.schedule, async () => {
|
|
165
|
+
const client = createServiceClient();
|
|
166
|
+
const result = await client.from(config.endpoint)
|
|
167
|
+
.select("*").eq('must_process', true).limit(1);
|
|
168
|
+
|
|
169
|
+
if (result.error) return;
|
|
170
|
+
for (const item of result.data ?? []) {
|
|
171
|
+
try {
|
|
172
|
+
await handleCommand(streamId, command, { userId: 'system', ...metadata });
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error('Processing error:', error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
177
179
|
};
|
|
178
180
|
```
|
|
179
181
|
|
|
@@ -189,9 +191,9 @@ export const processor = {
|
|
|
189
191
|
```typescript
|
|
190
192
|
type State = { isActive: boolean };
|
|
191
193
|
const decide = (state: State, cmd: Command): Event[] => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
if (state.isActive) throw "already.active"; // Activate guard
|
|
195
|
+
if (!state.isActive) throw "not.active"; // Deactivate guard
|
|
196
|
+
return [event];
|
|
195
197
|
};
|
|
196
198
|
```
|
|
197
199
|
|
|
@@ -201,13 +203,13 @@ const decide = (state: State, cmd: Command): Event[] => {
|
|
|
201
203
|
```typescript
|
|
202
204
|
type State = { trackedIds: Set<string> };
|
|
203
205
|
const evolve = (state: State, event: ContextEvents): State => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
206
|
+
switch (event.type) {
|
|
207
|
+
case 'ItemAdded': return { trackedIds: new Set([...state.trackedIds, event.data.id]) };
|
|
208
|
+
case 'ItemRemoved':
|
|
209
|
+
const newSet = new Set(state.trackedIds);
|
|
210
|
+
newSet.delete(event.data.id);
|
|
211
|
+
return { trackedIds: newSet };
|
|
212
|
+
}
|
|
211
213
|
};
|
|
212
214
|
```
|
|
213
215
|
|
|
@@ -217,10 +219,10 @@ const evolve = (state: State, event: ContextEvents): State => {
|
|
|
217
219
|
```typescript
|
|
218
220
|
type State = { submitted: boolean; reverted: boolean; approved: boolean; declined: boolean };
|
|
219
221
|
const decide = (state: State, cmd: Command): Event[] => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
222
|
+
if (state.submitted) throw 'cannot submit twice';
|
|
223
|
+
if (!state.submitted) throw 'not_submitted';
|
|
224
|
+
if (state.reverted) throw 'already_reverted';
|
|
225
|
+
return [event];
|
|
224
226
|
};
|
|
225
227
|
```
|
|
226
228
|
|
|
@@ -275,27 +277,27 @@ All endpoints must:
|
|
|
275
277
|
### STATE_CHANGE Tests
|
|
276
278
|
```typescript
|
|
277
279
|
DeciderSpecification.for(decide, evolve, initialState)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
280
|
+
.given([PrerequisiteEvent1, PrerequisiteEvent2])
|
|
281
|
+
.when(Command({ field: 'value' }))
|
|
282
|
+
.then([ExpectedEvent({ field: 'value' })]);
|
|
281
283
|
```
|
|
282
284
|
|
|
283
285
|
### STATE_VIEW Tests
|
|
284
286
|
```typescript
|
|
285
287
|
PostgreSQLProjectionSpec.for(ProjectionName)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
288
|
+
.given([Event1, Event2])
|
|
289
|
+
.when([]) // Always empty
|
|
290
|
+
.then(async (state) => {
|
|
291
|
+
const result = await state.query();
|
|
292
|
+
assert.equal(result[0].field, expectedValue);
|
|
293
|
+
});
|
|
292
294
|
```
|
|
293
295
|
|
|
294
296
|
### Error Case Tests
|
|
295
297
|
```typescript
|
|
296
298
|
.given([EventCreated])
|
|
297
|
-
.when(Command)
|
|
298
|
-
.shouldFail(); // Expects error
|
|
299
|
+
.when(Command)
|
|
300
|
+
.shouldFail(); // Expects error
|
|
299
301
|
```
|
|
300
302
|
|
|
301
303
|
### Test Coverage
|
|
@@ -1,140 +1,509 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Frontend Agent Prompt - UI Component Generation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
The structure defined in the Project-Skills is relevant.
|
|
3
|
+
Build React + TypeScript UI components from slice JSON definitions using established patterns.
|
|
6
4
|
|
|
7
5
|
## Your Task
|
|
8
6
|
|
|
9
|
-
0. Do not read the entire code base. Focus on the tasks in this description.
|
|
10
|
-
1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" and assigned to "
|
|
7
|
+
0. Do not read the entire code base. read /frontend/AGENTS.md. Focus on the tasks in this description.
|
|
8
|
+
1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" and assigned to "ui_worker" is a task.
|
|
11
9
|
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
|
12
|
-
3. Make sure you are on the right branch "feature/<slicename>",
|
|
13
|
-
5. Pick the **highest priority** task assigned to
|
|
10
|
+
3. Make sure you are on the right branch "feature/<slicename>", the branch should exist.
|
|
11
|
+
5. Pick the **highest priority** task assigned to ui_worker. This becomes your PRD. Set the status "InProgress", add a started_date ( including date and time ) in the index.json. If no slice has status planned, reply with:
|
|
14
12
|
<promise>NO_TASKS</promise> and stop. Do not work on other slices.
|
|
15
13
|
6. Pick the slice definition from the project root /.slices in <folder> defined in the prd. Never work on more than one slice per iteration.
|
|
16
|
-
7. A slice can define additional prompts as codegen/
|
|
17
|
-
7. Define the slice type. If the processors-array is not empty, it´s an automation slice. Load the matching skill (automation-slice, state-change-slice, state-view-slice)
|
|
14
|
+
7. A slice can define additional prompts as codegen/uiPrompt. any additional prompts defined in backend are hints for the implementation of the slice and have to be taken into account. If you use the additional prompt, add a line in progress.txt
|
|
18
15
|
8. Write a short progress one liner after each step to progress.txt
|
|
19
|
-
9. Analyze and Implement
|
|
20
|
-
knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition.
|
|
16
|
+
9. Analyze and Implement according to the Rest of the instructions in this file, make use of the skills in the skills directory, but also your previsously collected
|
|
17
|
+
knowledge. Make a list TODO list for what needs to be done. Also make sure to adjust the implementation according to the json definition.
|
|
21
18
|
10. The slice in the json is always true, the code follows what is defined in the json
|
|
22
|
-
11. the
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
15. if the slice is an automation slice, set status to 'Done'. Otherwise - Update the Slice in the index.json back to status 'Planned' and assign the 'ui-worker'
|
|
28
|
-
16. If checks pass, commit ALL changes with message: `feat: [Slice Name]`.
|
|
29
|
-
17. if the slice stauts is 'Done', merge back.
|
|
19
|
+
11. the slice is only 'Done' if APIs are implemented.
|
|
20
|
+
12. make sure to read the ui-prompt.md in /backend/src/slices/<slice>
|
|
21
|
+
13. Place the component where it belongs. If you can´t find a place, add a new page with /debug/<page> to showcase the component.
|
|
22
|
+
14. Run quality checks ( npm run build, tsc ) - Attention - it´s enough to run the tests for the slice. Do not run all tests.
|
|
23
|
+
16. Update the Slice in the index.json to status 'Done' and remove assignment
|
|
30
24
|
17. Append your progress to `progress.txt` after each step in the iteration.
|
|
31
|
-
18. append your new learnings to AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
|
|
32
|
-
19.
|
|
25
|
+
18. append your new learnings to frontend/AGENTS.md in a compressed form, reusable for future iterations. Only add learnings if they are not already there.
|
|
26
|
+
19. If checks pass, commit ALL changes with message: `feat: [Slice Name]` and merge back to main as FF merge ( update
|
|
27
|
+
first ). The tash is only 'Done' if the branch is merged back.
|
|
33
28
|
20. Finish the iteration.
|
|
34
29
|
|
|
35
|
-
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 0. Available Skills (Use These!)
|
|
33
|
+
|
|
34
|
+
This project has custom skills available in `.claude/skills/` that automate common tasks:
|
|
35
|
+
|
|
36
|
+
- **`/ui-build-slice-ui`**: Complete orchestrator - builds entire UI from slice definitions (types → API → hooks → components)
|
|
37
|
+
- **`/ui-analyze-slices`**: Analyze slice JSON files to identify grouping, dependencies, and implementation strategy
|
|
38
|
+
- **`/ui-read-ui-prompts`**: Find and parse UI prompts from slice definitions and `ui-prompt.md`
|
|
39
|
+
- **`/ui-generate-types`**: Generate TypeScript interfaces from slice field definitions
|
|
40
|
+
- **`/ui-generate-api`**: Generate API layer functions for queries (Supabase) and commands (POST)
|
|
41
|
+
- **`/ui-generate-hook`**: Generate React Query hooks for STATE_VIEW (queries) and STATE_CHANGE (mutations)
|
|
42
|
+
- **`/ui-scaffold-component`**: Scaffold React components (List, Dialog, Page) using Bulma CSS
|
|
43
|
+
|
|
44
|
+
**IMPORTANT**: When asked to build UI from slices, USE the `/ui-build-slice-ui` skill first! It will orchestrate all other skills in the correct order. Only use individual skills when you need to regenerate or modify a specific part.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 1. Slice Composition (CRITICAL)
|
|
49
|
+
|
|
50
|
+
### Slice Types
|
|
51
|
+
|
|
52
|
+
- **STATE_VIEW**: One read model table → Query hook → List component
|
|
53
|
+
- **STATE_CHANGE**: One command/POST endpoint → Mutation hook → Form/Dialog
|
|
54
|
+
|
|
55
|
+
### Grouping Rule
|
|
56
|
+
|
|
57
|
+
**Slices with same `groupId` → Implement together as one component**
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
// Multiple slices, same groupId → One component
|
|
61
|
+
Slice A: { groupId: "123", sliceType: "STATE_VIEW", title: "Events View" }
|
|
62
|
+
Slice B: { groupId: "123", sliceType: "STATE_CHANGE", title: "Create Event" }
|
|
63
|
+
Slice C: { groupId: "123", sliceType: "STATE_CHANGE", title: "Cancel Event" }
|
|
64
|
+
// → Combine into EventsPage component
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### UI Prompts Priority
|
|
68
|
+
|
|
69
|
+
1. Check `ui-prompt.md` (detailed specs) - Use `/ui-read-ui-prompts` skill
|
|
70
|
+
2. Check `codeGen.uiPrompts` array (high-level guidance) - Use `/ui-read-ui-prompts` skill
|
|
71
|
+
3. Follow this prompt (standard patterns)
|
|
72
|
+
4. Keep it simple if no guidance
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 2. Authentication & API Context
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// AuthContext: user, session, restaurantId
|
|
80
|
+
import { useAuth } from "@/contexts/AuthContext";
|
|
81
|
+
const { user, session } = useAuth();
|
|
82
|
+
|
|
83
|
+
// ApiContext: token, restaurantId, userId (auto-injected into headers)
|
|
84
|
+
import { useApiContext } from "@/hooks/useApiContext";
|
|
85
|
+
const ctx = useApiContext();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 3. Hooks Pattern
|
|
91
|
+
|
|
92
|
+
### Query Hook (STATE_VIEW)
|
|
36
93
|
|
|
37
|
-
|
|
94
|
+
```typescript
|
|
95
|
+
// src/hooks/api/useEvents.ts
|
|
96
|
+
import { useQuery } from "@tanstack/react-query";
|
|
97
|
+
import * as api from "@/lib/api";
|
|
38
98
|
|
|
99
|
+
export function useEvents() {
|
|
100
|
+
return useQuery({
|
|
101
|
+
queryKey: ["events"],
|
|
102
|
+
queryFn: () => api.fetchEvents(),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Mutation Hook (STATE_CHANGE)
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
export function useCreateEvent() {
|
|
111
|
+
const ctx = useApiContext();
|
|
112
|
+
const queryClient = useQueryClient();
|
|
113
|
+
|
|
114
|
+
return useMutation({
|
|
115
|
+
mutationFn: (params: api.CreateEventParams) => api.createEvent(params, ctx),
|
|
116
|
+
onSuccess: () => {
|
|
117
|
+
queryClient.invalidateQueries({ queryKey: ["events"] });
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
39
121
|
```
|
|
40
|
-
## [Date/Time] - [Slice]
|
|
41
122
|
|
|
42
|
-
- What was implemented
|
|
43
|
-
- Files changed
|
|
44
|
-
- **Learnings for future iterations:**
|
|
45
|
-
- Patterns discovered (e.g., "this codebase uses X for Y")
|
|
46
|
-
- Gotchas encountered (e.g., "don't forget to update Z when changing W")
|
|
47
|
-
- Useful context (e.g., "the evaluation panel is in component X")
|
|
48
123
|
---
|
|
124
|
+
|
|
125
|
+
## 4. API Layer (CQRS)
|
|
126
|
+
|
|
127
|
+
### Query (Read from Supabase)
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// src/lib/api.ts
|
|
131
|
+
export async function fetchEvents(): Promise<Event[]> {
|
|
132
|
+
const { data, error } = await supabase
|
|
133
|
+
.from("events_for_planning") // Read model table
|
|
134
|
+
.select("*");
|
|
135
|
+
if (error) throw new Error(error.message);
|
|
136
|
+
return data || [];
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Command (POST to Backend)
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
export async function createEvent(params: CreateEventParams, ctx: ApiContext) {
|
|
144
|
+
const response = await apiRequest(
|
|
145
|
+
commandEndpoints.createEvent, // /api/createevent
|
|
146
|
+
ctx,
|
|
147
|
+
{ method: "POST", body: { ...params } }
|
|
148
|
+
);
|
|
149
|
+
if (!response.ok) throw new Error(response.error);
|
|
150
|
+
return response.data;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Add endpoint in `src/lib/api-client.ts`**:
|
|
155
|
+
```typescript
|
|
156
|
+
export const commandEndpoints = {
|
|
157
|
+
createEvent: "/api/createevent",
|
|
158
|
+
// ...
|
|
159
|
+
};
|
|
49
160
|
```
|
|
50
161
|
|
|
51
|
-
|
|
52
|
-
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 5. Component Patterns
|
|
165
|
+
|
|
166
|
+
### List Component (STATE_VIEW)
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
170
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
171
|
+
import { useEvents } from "@/hooks/api/useEvents";
|
|
172
|
+
|
|
173
|
+
export function EventList() {
|
|
174
|
+
const { data: events = [], isLoading } = useEvents();
|
|
175
|
+
|
|
176
|
+
if (isLoading) {
|
|
177
|
+
return <Skeleton className="h-20 w-full" />;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
182
|
+
{events.map(event => (
|
|
183
|
+
<Card key={event.id}>
|
|
184
|
+
<CardHeader><CardTitle>{event.name}</CardTitle></CardHeader>
|
|
185
|
+
<CardContent>{event.date}</CardContent>
|
|
186
|
+
</Card>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Dialog Component (STATE_CHANGE)
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { useState } from "react";
|
|
197
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
|
198
|
+
import { Button } from "@/components/ui/button";
|
|
199
|
+
import { Input } from "@/components/ui/input";
|
|
200
|
+
import { Label } from "@/components/ui/label";
|
|
201
|
+
import { useCreateEvent } from "@/hooks/api/useEvents";
|
|
202
|
+
import { toast } from "sonner";
|
|
203
|
+
|
|
204
|
+
export function CreateEventDialog({ open, onOpenChange }) {
|
|
205
|
+
const createEvent = useCreateEvent();
|
|
206
|
+
const [form, setForm] = useState({ name: "", date: "" });
|
|
207
|
+
|
|
208
|
+
const handleSubmit = async () => {
|
|
209
|
+
if (!form.name) return toast.error("Name required");
|
|
210
|
+
try {
|
|
211
|
+
await createEvent.mutateAsync(form);
|
|
212
|
+
toast.success("Planned");
|
|
213
|
+
onOpenChange(false);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
toast.error(`Error: ${err.message}`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
221
|
+
<DialogContent>
|
|
222
|
+
<DialogHeader><DialogTitle>Create Event</DialogTitle></DialogHeader>
|
|
223
|
+
<div className="space-y-4">
|
|
224
|
+
<div>
|
|
225
|
+
<Label>Name</Label>
|
|
226
|
+
<Input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
|
|
227
|
+
</div>
|
|
228
|
+
<Button onClick={handleSubmit} disabled={createEvent.isPending}>
|
|
229
|
+
Create
|
|
230
|
+
</Button>
|
|
231
|
+
</div>
|
|
232
|
+
</DialogContent>
|
|
233
|
+
</Dialog>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Page Composition (Combine Slices)
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
export function EventsPage() {
|
|
242
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<div className="p-6">
|
|
246
|
+
<div className="flex justify-between mb-6">
|
|
247
|
+
<h1 className="text-2xl font-bold">Events</h1>
|
|
248
|
+
<Button onClick={() => setDialogOpen(true)}>Create Event</Button>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<EventList />
|
|
252
|
+
<CreateEventDialog open={dialogOpen} onOpenChange={setDialogOpen} />
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 6. Implementation Workflow
|
|
53
261
|
|
|
54
|
-
|
|
262
|
+
### Recommended Approach: Use Skills!
|
|
55
263
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
264
|
+
**For complete UI generation from slices:**
|
|
265
|
+
```bash
|
|
266
|
+
# Use the orchestrator skill - it handles everything
|
|
267
|
+
/ui-build-slice-ui <slice-file-paths>
|
|
268
|
+
```
|
|
59
269
|
|
|
270
|
+
This will automatically:
|
|
271
|
+
1. Analyze slices (`/ui-analyze-slices`)
|
|
272
|
+
2. Read UI prompts (`/ui-read-ui-prompts`)
|
|
273
|
+
3. Generate types (`/ui-generate-types`)
|
|
274
|
+
4. Generate API functions (`/ui-generate-api`)
|
|
275
|
+
5. Generate hooks (`/ui-generate-hook`)
|
|
276
|
+
6. Scaffold components (`/ui-scaffold-component`)
|
|
277
|
+
7. Verify integration
|
|
278
|
+
|
|
279
|
+
**For individual tasks:**
|
|
280
|
+
- **Analyze before coding**: `/ui-analyze-slices <slice-files>` - Understand grouping and dependencies
|
|
281
|
+
- **Read requirements**: `/ui-read-ui-prompts <slice-folder>` - Extract design specs and validation rules
|
|
282
|
+
- **Generate types only**: `/ui-generate-types <slice-file>` - Create TypeScript interfaces
|
|
283
|
+
- **Generate API only**: `/ui-generate-api <slice-file>` - Create Supabase queries or POST commands
|
|
284
|
+
- **Generate hook only**: `/ui-generate-hook <slice-file>` - Create React Query hooks
|
|
285
|
+
- **Generate component only**: `/ui-scaffold-component <slice-files>` - Create React components with Bulma CSS
|
|
286
|
+
|
|
287
|
+
### Manual Workflow (If not using skills)
|
|
288
|
+
|
|
289
|
+
### Step 0: Analyze Grouping
|
|
290
|
+
- Find all slices with same `groupId`
|
|
291
|
+
- Check `codeGen.uiPrompts` and `ui-prompt.md`
|
|
292
|
+
- Plan to implement together
|
|
293
|
+
|
|
294
|
+
### Step 1: Types
|
|
295
|
+
```typescript
|
|
296
|
+
// src/types/index.ts
|
|
297
|
+
export interface Event {
|
|
298
|
+
event_id: string;
|
|
299
|
+
name: string;
|
|
300
|
+
date: string;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export interface CreateEventParams {
|
|
304
|
+
name: string;
|
|
305
|
+
date: string;
|
|
306
|
+
}
|
|
60
307
|
```
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
308
|
+
|
|
309
|
+
### Step 2: API Functions
|
|
310
|
+
- Add endpoints to `src/lib/api-client.ts`
|
|
311
|
+
- Implement queries (Supabase) in `src/lib/api.ts`
|
|
312
|
+
- Implement commands (POST) in `src/lib/api.ts`
|
|
313
|
+
|
|
314
|
+
### Step 3: Hooks
|
|
315
|
+
- Create query/mutation hooks in `src/hooks/api/use<Entity>.ts`
|
|
316
|
+
- Export in `src/hooks/api/index.ts`
|
|
317
|
+
|
|
318
|
+
### Step 4: Components
|
|
319
|
+
- Build list component (STATE_VIEW)
|
|
320
|
+
- Build dialog/form components (STATE_CHANGE)
|
|
321
|
+
- Compose into page component
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 7. Field Type Mapping
|
|
326
|
+
|
|
327
|
+
| JSON Type | TypeScript | UI Component |
|
|
328
|
+
|-----------|-----------|--------------|
|
|
329
|
+
| `string` | `string` | `<Input type="text">` |
|
|
330
|
+
| `number` | `number` | `<Input type="number">` |
|
|
331
|
+
| `date` | `string` | `<Input type="date">` |
|
|
332
|
+
| `time` | `string` | `<Input type="time">` |
|
|
333
|
+
| `boolean` | `boolean` | `<Switch>` or `<Checkbox>` |
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 8. Common Patterns
|
|
338
|
+
|
|
339
|
+
**Loading**: `if (isLoading) return <Skeleton />;`
|
|
340
|
+
|
|
341
|
+
**Error Handling**:
|
|
342
|
+
```typescript
|
|
343
|
+
try {
|
|
344
|
+
await mutation.mutateAsync(params);
|
|
345
|
+
toast.success("Success");
|
|
346
|
+
} catch (err) {
|
|
347
|
+
toast.error(`Error: ${err.message}`);
|
|
348
|
+
}
|
|
65
349
|
```
|
|
66
350
|
|
|
67
|
-
|
|
351
|
+
**Form State**: `const [form, setForm] = useState({ field: "" });`
|
|
352
|
+
|
|
353
|
+
**Invalidation**: `queryClient.invalidateQueries({ queryKey: ["entity"] });`
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## 9. UI Components
|
|
358
|
+
|
|
359
|
+
**NOTE**: The skills in `.claude/skills/` are configured to use **Bulma CSS** for styling. If you're using the skills (especially `/scaffold-component`), components will be generated with Bulma classes.
|
|
360
|
+
|
|
361
|
+
### Bulma CSS (Used by Skills)
|
|
362
|
+
|
|
363
|
+
When using skills, components use Bulma classes:
|
|
364
|
+
- Layout: `container`, `section`, `columns`, `column`
|
|
365
|
+
- Components: `card`, `modal`, `button`, `notification`, `box`
|
|
366
|
+
- Forms: `field`, `control`, `label`, `input`, `select`, `textarea`
|
|
367
|
+
- Modifiers: `is-primary`, `is-active`, `is-fullwidth`, `has-text-centered`
|
|
368
|
+
|
|
369
|
+
### shadcn/ui (Alternative)
|
|
370
|
+
|
|
371
|
+
If manually implementing (not using skills), you can use shadcn/ui components:
|
|
372
|
+
|
|
373
|
+
Import from `@/components/ui/*`:
|
|
374
|
+
- `Button`, `Input`, `Label`, `Textarea`, `Select`
|
|
375
|
+
- `Card`, `CardHeader`, `CardTitle`, `CardContent`
|
|
376
|
+
- `Dialog`, `DialogContent`, `DialogHeader`, `DialogTitle`
|
|
377
|
+
- `Table`, `Tabs`, `Skeleton`
|
|
378
|
+
- `toast` from `sonner`
|
|
379
|
+
|
|
380
|
+
**Recommendation**: Use the skills with Bulma CSS for consistency with project configuration.
|
|
68
381
|
|
|
69
|
-
|
|
382
|
+
---
|
|
70
383
|
|
|
71
|
-
|
|
384
|
+
## Checklist
|
|
72
385
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- Dependencies between files
|
|
78
|
-
- Testing approaches for that area
|
|
79
|
-
- Configuration or environment requirements
|
|
386
|
+
**Pre-Implementation**:
|
|
387
|
+
- [ ] Check `groupId` - find all related slices
|
|
388
|
+
- [ ] Read `ui-prompt.md` and `codeGen.uiPrompts`
|
|
389
|
+
- [ ] Identify STATE_VIEW vs STATE_CHANGE slices
|
|
80
390
|
|
|
81
|
-
**
|
|
391
|
+
**Implementation**:
|
|
392
|
+
- [ ] Define types in `src/types/index.ts`
|
|
393
|
+
- [ ] Add endpoints to `src/lib/api-client.ts`
|
|
394
|
+
- [ ] Implement API functions in `src/lib/api.ts`
|
|
395
|
+
- [ ] Create hooks in `src/hooks/api/use<Entity>.ts`
|
|
396
|
+
- [ ] Build components in `src/components/<domain>/`
|
|
397
|
+
- [ ] Compose page combining all grouped slices
|
|
398
|
+
- [ ] Test loading, error handling, success flows
|
|
82
399
|
|
|
83
|
-
|
|
84
|
-
- "This module uses pattern Z for all API calls"
|
|
85
|
-
- "Tests require the dev server running on PORT 3000"
|
|
86
|
-
- "Field names must match the template exactly"
|
|
400
|
+
---
|
|
87
401
|
|
|
88
|
-
|
|
402
|
+
## Quick Reference
|
|
89
403
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
|
|
404
|
+
✅ **Same `groupId`** = Implement together
|
|
405
|
+
✅ **STATE_VIEW** = Read table → Query hook → List
|
|
406
|
+
✅ **STATE_CHANGE** = Command → Mutation hook → Form/Dialog
|
|
407
|
+
✅ **Keep modular** = Sub-components for each slice capability
|
|
408
|
+
✅ **Keep simple** = Follow existing patterns, don't over-engineer
|
|
95
409
|
|
|
96
|
-
|
|
410
|
+
---
|
|
97
411
|
|
|
98
|
-
##
|
|
412
|
+
## 10. Skills Usage Examples
|
|
99
413
|
|
|
100
|
-
|
|
101
|
-
- run 'npm run build'
|
|
102
|
-
- run 'npm run test'
|
|
103
|
-
- Do NOT commit broken code
|
|
104
|
-
- Keep changes focused and minimal
|
|
105
|
-
- Follow existing code patterns
|
|
414
|
+
### Example 1: Building Complete UI from Scratch
|
|
106
415
|
|
|
107
|
-
|
|
416
|
+
**User Request**: "Build the UI for the events management slices"
|
|
108
417
|
|
|
109
|
-
|
|
110
|
-
|
|
418
|
+
**Your Response**:
|
|
419
|
+
```bash
|
|
420
|
+
# First, analyze what we're working with
|
|
421
|
+
/ui-analyze-slices src/slices/events/*.json
|
|
111
422
|
|
|
112
|
-
|
|
423
|
+
# Then build everything at once
|
|
424
|
+
/ui-build-slice-ui src/slices/events/ViewEvents.json src/slices/events/CreateEvent.json
|
|
425
|
+
```
|
|
113
426
|
|
|
114
|
-
|
|
427
|
+
### Example 2: Just Need to Understand Slices
|
|
115
428
|
|
|
116
|
-
|
|
429
|
+
**User Request**: "What slices are in the orders group?"
|
|
117
430
|
|
|
118
|
-
|
|
431
|
+
**Your Response**:
|
|
432
|
+
```bash
|
|
433
|
+
# Use ui-analyze-slices to understand the grouping
|
|
434
|
+
/ui-analyze-slices src/slices/orders/*.json
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Example 3: Regenerating Just Types
|
|
119
438
|
|
|
120
|
-
|
|
439
|
+
**User Request**: "The Event interface is wrong, regenerate it from the slice"
|
|
121
440
|
|
|
122
|
-
|
|
123
|
-
|
|
441
|
+
**Your Response**:
|
|
442
|
+
```bash
|
|
443
|
+
# Use ui-generate-types to recreate just the TypeScript interfaces
|
|
444
|
+
/ui-generate-types src/slices/events/ViewEvents.json
|
|
445
|
+
```
|
|
124
446
|
|
|
125
|
-
|
|
126
|
-
<promise>NO_TASKS</promise>
|
|
447
|
+
### Example 4: Adding New API Endpoint
|
|
127
448
|
|
|
128
|
-
|
|
129
|
-
|
|
449
|
+
**User Request**: "Add the API function for the CancelEvent command"
|
|
450
|
+
|
|
451
|
+
**Your Response**:
|
|
452
|
+
```bash
|
|
453
|
+
# Use ui-generate-api to create the API layer
|
|
454
|
+
/ui-generate-api src/slices/events/CancelEvent.json
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### Example 5: Understanding UI Requirements
|
|
458
|
+
|
|
459
|
+
**User Request**: "What are the validation rules for the event form?"
|
|
460
|
+
|
|
461
|
+
**Your Response**:
|
|
462
|
+
```bash
|
|
463
|
+
# Use ui-read-ui-prompts to extract requirements
|
|
464
|
+
/ui-read-ui-prompts src/slices/events/
|
|
465
|
+
```
|
|
130
466
|
|
|
131
|
-
|
|
467
|
+
### Example 6: Fixing Component Styling
|
|
132
468
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
469
|
+
**User Request**: "The EventList component doesn't look right, regenerate it with proper Bulma styling"
|
|
470
|
+
|
|
471
|
+
**Your Response**:
|
|
472
|
+
```bash
|
|
473
|
+
# Use ui-scaffold-component to regenerate with Bulma CSS
|
|
474
|
+
/ui-scaffold-component src/slices/events/ViewEvents.json
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Decision Tree: Which Skill to Use?
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
Need to build complete UI from slices?
|
|
481
|
+
└─> /ui-build-slice-ui (orchestrates everything)
|
|
482
|
+
|
|
483
|
+
Just exploring/understanding?
|
|
484
|
+
└─> /ui-analyze-slices (analysis only)
|
|
485
|
+
|
|
486
|
+
Just need to understand UI requirements?
|
|
487
|
+
└─> /ui-read-ui-prompts (extract specs)
|
|
488
|
+
|
|
489
|
+
Need to fix/regenerate specific layer?
|
|
490
|
+
├─> Types wrong? → /ui-generate-types
|
|
491
|
+
├─> API functions wrong? → /ui-generate-api
|
|
492
|
+
├─> Hooks wrong? → /ui-generate-hook
|
|
493
|
+
└─> Components wrong? → /ui-scaffold-component
|
|
494
|
+
|
|
495
|
+
Don't know which skill to use?
|
|
496
|
+
└─> Start with /ui-analyze-slices, then use /ui-build-slice-ui
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
137
500
|
|
|
138
|
-
##
|
|
501
|
+
## Remember
|
|
139
502
|
|
|
140
|
-
|
|
503
|
+
**ALWAYS** prefer using the skills over manual implementation! They ensure:
|
|
504
|
+
- Consistency across the codebase
|
|
505
|
+
- Proper use of Bulma CSS
|
|
506
|
+
- Correct CQRS patterns
|
|
507
|
+
- Proper TypeScript types
|
|
508
|
+
- React Query best practices
|
|
509
|
+
- Adherence to UI prompts and validation rules
|
|
@@ -4,6 +4,8 @@ Build React + TypeScript UI components from slice JSON definitions using establi
|
|
|
4
4
|
|
|
5
5
|
## Your Task
|
|
6
6
|
|
|
7
|
+
If a slice is in status 'planned', Even if a slice seems to have been implemented, make sure to verify the implementation, check for new fields, additional custom prompts in the slice json. A "planned" slice can also be an update of an existing slice. If that is the case, match the implemenetation to the updated slice definition.
|
|
8
|
+
|
|
7
9
|
0. Do not read the entire code base. read /frontend/AGENTS.md. Focus on the tasks in this description.
|
|
8
10
|
1. Read the description at `.slices/index.json` (in the same directory as this file). Every item in status "planned" and assigned to "ui_worker" is a task.
|
|
9
11
|
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
|
@@ -18,8 +20,8 @@ Build React + TypeScript UI components from slice JSON definitions using establi
|
|
|
18
20
|
10. The slice in the json is always true, the code follows what is defined in the json
|
|
19
21
|
11. the slice is only 'Done' if APIs are implemented.
|
|
20
22
|
12. make sure to read the ui-prompt.md in /backend/src/slices/<slice>
|
|
21
|
-
13.
|
|
22
|
-
14. Run quality checks ( npm run build, tsc ) - Attention -
|
|
23
|
+
13. If a custom uiPrompt in the slice.json defines where the component should be mounted, this always has to be done. If no mount point is defined in uiPrompt, verify if there is natural place where to mount a component. If there is no natural place, create a new page, name it after the componenet and define a Route in App.tsx
|
|
24
|
+
14. Run quality checks ( npm run build, tsc ) - Attention - the slice is only done if the component is placed somewhere.
|
|
23
25
|
16. Update the Slice in the index.json to status 'Done' and remove assignment
|
|
24
26
|
17. If checks pass, commit ALL changes with message: `feat: [Slice Name]` and merge back to main as FF merge ( update
|
|
25
27
|
first )
|
|
@@ -42,6 +44,7 @@ This project has custom skills available in `.claude/skills/` that automate comm
|
|
|
42
44
|
- **`/ui-scaffold-component`**: Scaffold React components (List, Dialog, Page) using Bulma CSS
|
|
43
45
|
|
|
44
46
|
**IMPORTANT**: When asked to build UI from slices, USE the `/ui-build-slice-ui` skill first! It will orchestrate all other skills in the correct order. Only use individual skills when you need to regenerate or modify a specific part.
|
|
47
|
+
**IMPORTANT**: Unless stated in a custom prompt, components read directly from the tables provided, not from api-endpoints via HTTP
|
|
45
48
|
|
|
46
49
|
---
|
|
47
50
|
|
package/templates/prompt.md
CHANGED
|
@@ -9,4 +9,5 @@ You assign tasks to workers. Only one task can be assigned and in status planned
|
|
|
9
9
|
2. if the slice is in status planned and not assigned, assign it to backend_worker (property "assigned" ) and continue with backend/prompt.md. Ignore the rest of this file.
|
|
10
10
|
3. if the status is in status "InProgress" and assigned to backend_worker, updated started_time and continue with backend/prompt.md. Ignore the rest of this file.
|
|
11
11
|
4. if the status is "done" and assigned to "backend_worker", assign the task to "ui_worker" and move it back to status "planned". continue with frontend/prompt.md. Ignore the rest of this file.
|
|
12
|
-
|
|
12
|
+
5. if there is no task in status planned, return <promise>NO_TASKS</promise>
|
|
13
|
+
6. If one task is done. Finish your work, do not continue with the next slice.
|