Add inline block editing to schedule templates
- Backend: PATCH /api/schedules/{template_id}/blocks/{block_id} endpoint
- Frontend: Edit button on each block row expands an inline form
pre-filled with current subject, times, and label; saves via PATCH
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -154,11 +154,32 @@
|
||||
</div>
|
||||
|
||||
<div class="block-list">
|
||||
<div v-for="block in template.blocks" :key="block.id" class="block-row">
|
||||
<span class="block-time">{{ block.time_start }} – {{ block.time_end }}</span>
|
||||
<span class="block-label">{{ block.label || subjectName(block.subject_id) || 'Unnamed' }}</span>
|
||||
<button class="btn-sm btn-danger" @click="deleteBlock(template.id, block.id)">✕</button>
|
||||
</div>
|
||||
<template v-for="block in template.blocks" :key="block.id">
|
||||
<!-- Edit mode -->
|
||||
<form
|
||||
v-if="editingBlock && editingBlock.id === block.id"
|
||||
class="edit-block-form"
|
||||
@submit.prevent="saveBlock(template.id)"
|
||||
>
|
||||
<select v-model="editingBlock.subject_id">
|
||||
<option :value="null">No subject</option>
|
||||
<option v-for="s in subjects" :key="s.id" :value="s.id">{{ s.icon }} {{ s.name }}</option>
|
||||
</select>
|
||||
<input v-model="editingBlock.time_start" type="time" required />
|
||||
<span>to</span>
|
||||
<input v-model="editingBlock.time_end" type="time" required />
|
||||
<input v-model="editingBlock.label" placeholder="Label (optional)" />
|
||||
<button type="submit" class="btn-sm btn-primary">Save</button>
|
||||
<button type="button" class="btn-sm" @click="editingBlock = null">Cancel</button>
|
||||
</form>
|
||||
<!-- Display mode -->
|
||||
<div v-else class="block-row">
|
||||
<span class="block-time">{{ block.time_start }} – {{ block.time_end }}</span>
|
||||
<span class="block-label">{{ block.label || subjectName(block.subject_id) || 'Unnamed' }}</span>
|
||||
<button class="btn-sm" @click="startEditBlock(block)">Edit</button>
|
||||
<button class="btn-sm btn-danger" @click="deleteBlock(template.id, block.id)">✕</button>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="template.blocks.length === 0" class="empty-small">No blocks yet.</div>
|
||||
</div>
|
||||
|
||||
@@ -275,6 +296,7 @@ const showCreateForm = ref(false)
|
||||
const editingTemplate = ref(null)
|
||||
const newTemplate = ref({ name: '', child_id: null, is_default: false })
|
||||
const newBlock = ref({ subject_id: null, time_start: '', time_end: '', label: '', order_index: 0 })
|
||||
const editingBlock = ref(null)
|
||||
|
||||
function childName(id) {
|
||||
return childrenStore.children.find((c) => c.id === id)?.name || 'Unknown'
|
||||
@@ -313,6 +335,23 @@ async function addBlock(templateId) {
|
||||
await loadTemplates()
|
||||
}
|
||||
|
||||
function startEditBlock(block) {
|
||||
editingBlock.value = {
|
||||
id: block.id,
|
||||
subject_id: block.subject_id,
|
||||
time_start: block.time_start ? block.time_start.slice(0, 5) : '',
|
||||
time_end: block.time_end ? block.time_end.slice(0, 5) : '',
|
||||
label: block.label || '',
|
||||
}
|
||||
}
|
||||
|
||||
async function saveBlock(templateId) {
|
||||
const { id, ...payload } = editingBlock.value
|
||||
await api.patch(`/api/schedules/${templateId}/blocks/${id}`, payload)
|
||||
editingBlock.value = null
|
||||
await loadTemplates()
|
||||
}
|
||||
|
||||
async function deleteBlock(templateId, blockId) {
|
||||
await api.delete(`/api/schedules/${templateId}/blocks/${blockId}`)
|
||||
await loadTemplates()
|
||||
@@ -442,7 +481,8 @@ h2 { font-size: 1.1rem; color: #94a3b8; text-transform: uppercase; letter-spacin
|
||||
.block-time { font-size: 0.8rem; color: #64748b; font-variant-numeric: tabular-nums; }
|
||||
.block-label { flex: 1; font-size: 0.9rem; }
|
||||
|
||||
.add-block-form {
|
||||
.add-block-form,
|
||||
.edit-block-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
@@ -452,7 +492,9 @@ h2 { font-size: 1.1rem; color: #94a3b8; text-transform: uppercase; letter-spacin
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
.add-block-form select,
|
||||
.add-block-form input {
|
||||
.add-block-form input,
|
||||
.edit-block-form select,
|
||||
.edit-block-form input {
|
||||
padding: 0.4rem 0.6rem;
|
||||
background: #1e293b;
|
||||
border: 1px solid #334155;
|
||||
@@ -460,7 +502,9 @@ h2 { font-size: 1.1rem; color: #94a3b8; text-transform: uppercase; letter-spacin
|
||||
color: #f1f5f9;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.add-block-form span { color: #64748b; }
|
||||
.add-block-form span,
|
||||
.edit-block-form span { color: #64748b; }
|
||||
.edit-block-form { border: 1px solid #4f46e5; }
|
||||
|
||||
.empty-small { color: #64748b; font-size: 0.9rem; padding: 1rem 0; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user