docs : add implementation plans for admin clients and time entry multi-type select
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
# Time Entry Multi-Type Selection Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Allow selecting multiple task types in the TimeEntryDrawer, matching the existing multi-select pattern used in TaskDrawer.
|
||||
|
||||
**Architecture:** Replace the single-select `MalioSelect` dropdown for types with the checkbox-based colored badge multi-select already used in TaskDrawer. The backend (ManyToMany relation) and DTO (`types: string[]`) already support multiple types — only the frontend form state and template need updating.
|
||||
|
||||
**Tech Stack:** Vue 3, TypeScript
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1: Multi-Type Select in TimeEntryDrawer
|
||||
|
||||
### Task 1: Update TimeEntryDrawer to support multiple type selection
|
||||
|
||||
**Files:**
|
||||
- Modify: `frontend/components/time-tracking/TimeEntryDrawer.vue`
|
||||
|
||||
- [ ] **Step 1: Change form state from single typeId to typeIds array**
|
||||
|
||||
In the `form` reactive object (line 133-142), replace:
|
||||
|
||||
```typescript
|
||||
typeId: null as number | null,
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
typeIds: [] as number[],
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add toggleType function**
|
||||
|
||||
Add this function after the `durationLabel` computed (after line 165):
|
||||
|
||||
```typescript
|
||||
function toggleType(id: number) {
|
||||
const idx = form.typeIds.indexOf(id)
|
||||
if (idx >= 0) {
|
||||
form.typeIds.splice(idx, 1)
|
||||
} else {
|
||||
form.typeIds.push(id)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Remove the typeOptions computed**
|
||||
|
||||
Delete the `typeOptions` computed (lines 152-154):
|
||||
|
||||
```typescript
|
||||
const typeOptions = computed(() =>
|
||||
props.types.map(t => ({ label: t.label, value: t.id }))
|
||||
)
|
||||
```
|
||||
|
||||
This is no longer needed since we won't use `MalioSelect`.
|
||||
|
||||
- [ ] **Step 4: Replace MalioSelect template with multi-select badges**
|
||||
|
||||
Replace the `MalioSelect` for type (lines 75-81):
|
||||
|
||||
```vue
|
||||
<MalioSelect
|
||||
v-model="form.typeId"
|
||||
:options="typeOptions"
|
||||
label="Type"
|
||||
empty-option-label="— Aucun —"
|
||||
min-width="w-full"
|
||||
/>
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<p class="mb-2 text-sm font-semibold text-neutral-700">Types</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<label
|
||||
v-for="type in types"
|
||||
:key="type.id"
|
||||
class="cursor-pointer rounded-full px-3 py-1 text-xs font-semibold transition"
|
||||
:class="form.typeIds.includes(type.id)
|
||||
? 'text-white'
|
||||
: 'bg-neutral-100 text-neutral-600 hover:bg-neutral-200'"
|
||||
:style="form.typeIds.includes(type.id) ? { backgroundColor: type.color } : {}"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="hidden"
|
||||
:value="type.id"
|
||||
:checked="form.typeIds.includes(type.id)"
|
||||
@change="toggleType(type.id)"
|
||||
/>
|
||||
{{ type.label }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Update populateForm to use typeIds**
|
||||
|
||||
In the `populateForm` function, replace (line 194):
|
||||
|
||||
```typescript
|
||||
form.typeId = entry.types?.[0]?.id ?? null
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
form.typeIds = entry.types?.map(t => t.id) ?? []
|
||||
```
|
||||
|
||||
And in the else branch (line 203), replace:
|
||||
|
||||
```typescript
|
||||
form.typeId = null
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
form.typeIds = []
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Update onSubmit payload to use typeIds**
|
||||
|
||||
In the `onSubmit` function, replace (line 233):
|
||||
|
||||
```typescript
|
||||
types: form.typeId ? [`/api/task_types/${form.typeId}`] : [],
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```typescript
|
||||
types: form.typeIds.map(id => `/api/task_types/${id}`),
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Verify in browser**
|
||||
|
||||
Run: `make dev-nuxt`
|
||||
|
||||
1. Open time tracking page
|
||||
2. Open an existing time entry → verify existing types are pre-selected as colored badges
|
||||
3. Toggle types on/off → verify visual feedback (colored background when selected)
|
||||
4. Save → verify types are persisted correctly
|
||||
5. Create a new time entry with multiple types → verify they save correctly
|
||||
|
||||
- [ ] **Step 8: Commit**
|
||||
|
||||
```bash
|
||||
git add frontend/components/time-tracking/TimeEntryDrawer.vue
|
||||
git commit -m "feat(frontend) : allow multiple type selection in time entry drawer"
|
||||
```
|
||||
Reference in New Issue
Block a user