Add inline edit for children and subjects in admin, restore port 8054

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 23:29:43 -08:00
parent 781e179090
commit 51a1639fee
2 changed files with 81 additions and 16 deletions

View File

@@ -40,7 +40,7 @@ services:
container_name: homeschool_frontend
restart: unless-stopped
ports:
- "8057:80"
- "8054:80"
depends_on:
- backend
networks:

View File

@@ -20,15 +20,26 @@
<div class="item-list">
<div v-for="child in childrenStore.children" :key="child.id" class="item-row">
<div class="item-color" :style="{ background: child.color }"></div>
<span class="item-name">{{ child.name }}</span>
<span class="item-meta">{{ child.is_active ? 'Active' : 'Inactive' }}</span>
<div class="item-actions">
<button class="btn-sm" @click="toggleChild(child)">
{{ child.is_active ? 'Deactivate' : 'Activate' }}
</button>
<button class="btn-sm btn-danger" @click="deleteChild(child.id)">Delete</button>
</div>
<template v-if="editingChild && editingChild.id === child.id">
<input v-model="editingChild.name" class="edit-input" required />
<input v-model="editingChild.color" type="color" title="Color" />
<div class="item-actions">
<button class="btn-sm btn-primary" @click="saveChild">Save</button>
<button class="btn-sm" @click="editingChild = null">Cancel</button>
</div>
</template>
<template v-else>
<div class="item-color" :style="{ background: child.color }"></div>
<span class="item-name">{{ child.name }}</span>
<span class="item-meta">{{ child.is_active ? 'Active' : 'Inactive' }}</span>
<div class="item-actions">
<button class="btn-sm" @click="startEditChild(child)">Edit</button>
<button class="btn-sm" @click="toggleChild(child)">
{{ child.is_active ? 'Deactivate' : 'Activate' }}
</button>
<button class="btn-sm btn-danger" @click="deleteChild(child.id)">Delete</button>
</div>
</template>
</div>
<div v-if="childrenStore.children.length === 0" class="empty-small">No children added yet.</div>
</div>
@@ -51,12 +62,24 @@
<div class="item-list">
<div v-for="subject in subjects" :key="subject.id" class="item-row">
<div class="item-color" :style="{ background: subject.color }"></div>
<span class="item-icon">{{ subject.icon }}</span>
<span class="item-name">{{ subject.name }}</span>
<div class="item-actions">
<button class="btn-sm btn-danger" @click="deleteSubject(subject.id)">Delete</button>
</div>
<template v-if="editingSubject && editingSubject.id === subject.id">
<input v-model="editingSubject.name" class="edit-input" required />
<input v-model="editingSubject.icon" placeholder="Icon" maxlength="4" style="width:60px" class="edit-input" />
<input v-model="editingSubject.color" type="color" title="Color" />
<div class="item-actions">
<button class="btn-sm btn-primary" @click="saveSubject">Save</button>
<button class="btn-sm" @click="editingSubject = null">Cancel</button>
</div>
</template>
<template v-else>
<div class="item-color" :style="{ background: subject.color }"></div>
<span class="item-icon">{{ subject.icon }}</span>
<span class="item-name">{{ subject.name }}</span>
<div class="item-actions">
<button class="btn-sm" @click="startEditSubject(subject)">Edit</button>
<button class="btn-sm btn-danger" @click="deleteSubject(subject.id)">Delete</button>
</div>
</template>
</div>
<div v-if="subjects.length === 0" class="empty-small">No subjects added yet.</div>
</div>
@@ -78,6 +101,8 @@ const showChildForm = ref(false)
const showSubjectForm = ref(false)
const newChild = ref({ name: '', color: '#4F46E5' })
const newSubject = ref({ name: '', icon: '📚', color: '#10B981' })
const editingChild = ref(null)
const editingSubject = ref(null)
async function createChild() {
await childrenStore.createChild(newChild.value)
@@ -85,6 +110,19 @@ async function createChild() {
showChildForm.value = false
}
function startEditChild(child) {
editingChild.value = { ...child }
showChildForm.value = false
}
async function saveChild() {
await childrenStore.updateChild(editingChild.value.id, {
name: editingChild.value.name,
color: editingChild.value.color,
})
editingChild.value = null
}
async function toggleChild(child) {
await childrenStore.updateChild(child.id, { is_active: !child.is_active })
}
@@ -100,6 +138,22 @@ async function loadSubjects() {
subjects.value = res.data
}
function startEditSubject(subject) {
editingSubject.value = { ...subject }
showSubjectForm.value = false
}
async function saveSubject() {
const res = await api.patch(`/api/subjects/${editingSubject.value.id}`, {
name: editingSubject.value.name,
icon: editingSubject.value.icon,
color: editingSubject.value.color,
})
const idx = subjects.value.findIndex((s) => s.id === editingSubject.value.id)
if (idx !== -1) subjects.value[idx] = res.data
editingSubject.value = null
}
async function createSubject() {
const res = await api.post('/api/subjects', newSubject.value)
subjects.value.push(res.data)
@@ -171,6 +225,17 @@ h2 { font-size: 1.1rem; color: #94a3b8; text-transform: uppercase; letter-spacin
.empty-small { color: #64748b; font-size: 0.9rem; padding: 1rem 0; }
.edit-input {
padding: 0.35rem 0.6rem;
background: #0f172a;
border: 1px solid #4f46e5;
border-radius: 0.5rem;
color: #f1f5f9;
font-size: 0.9rem;
flex: 1;
min-width: 100px;
}
.btn-primary {
padding: 0.5rem 1rem;
background: #4f46e5;