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:
@@ -40,7 +40,7 @@ services:
|
|||||||
container_name: homeschool_frontend
|
container_name: homeschool_frontend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8057:80"
|
- "8054:80"
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -20,15 +20,26 @@
|
|||||||
|
|
||||||
<div class="item-list">
|
<div class="item-list">
|
||||||
<div v-for="child in childrenStore.children" :key="child.id" class="item-row">
|
<div v-for="child in childrenStore.children" :key="child.id" class="item-row">
|
||||||
|
<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>
|
<div class="item-color" :style="{ background: child.color }"></div>
|
||||||
<span class="item-name">{{ child.name }}</span>
|
<span class="item-name">{{ child.name }}</span>
|
||||||
<span class="item-meta">{{ child.is_active ? 'Active' : 'Inactive' }}</span>
|
<span class="item-meta">{{ child.is_active ? 'Active' : 'Inactive' }}</span>
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
|
<button class="btn-sm" @click="startEditChild(child)">Edit</button>
|
||||||
<button class="btn-sm" @click="toggleChild(child)">
|
<button class="btn-sm" @click="toggleChild(child)">
|
||||||
{{ child.is_active ? 'Deactivate' : 'Activate' }}
|
{{ child.is_active ? 'Deactivate' : 'Activate' }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn-sm btn-danger" @click="deleteChild(child.id)">Delete</button>
|
<button class="btn-sm btn-danger" @click="deleteChild(child.id)">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="childrenStore.children.length === 0" class="empty-small">No children added yet.</div>
|
<div v-if="childrenStore.children.length === 0" class="empty-small">No children added yet.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,12 +62,24 @@
|
|||||||
|
|
||||||
<div class="item-list">
|
<div class="item-list">
|
||||||
<div v-for="subject in subjects" :key="subject.id" class="item-row">
|
<div v-for="subject in subjects" :key="subject.id" class="item-row">
|
||||||
|
<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>
|
<div class="item-color" :style="{ background: subject.color }"></div>
|
||||||
<span class="item-icon">{{ subject.icon }}</span>
|
<span class="item-icon">{{ subject.icon }}</span>
|
||||||
<span class="item-name">{{ subject.name }}</span>
|
<span class="item-name">{{ subject.name }}</span>
|
||||||
<div class="item-actions">
|
<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>
|
<button class="btn-sm btn-danger" @click="deleteSubject(subject.id)">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="subjects.length === 0" class="empty-small">No subjects added yet.</div>
|
<div v-if="subjects.length === 0" class="empty-small">No subjects added yet.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,6 +101,8 @@ const showChildForm = ref(false)
|
|||||||
const showSubjectForm = ref(false)
|
const showSubjectForm = ref(false)
|
||||||
const newChild = ref({ name: '', color: '#4F46E5' })
|
const newChild = ref({ name: '', color: '#4F46E5' })
|
||||||
const newSubject = ref({ name: '', icon: '📚', color: '#10B981' })
|
const newSubject = ref({ name: '', icon: '📚', color: '#10B981' })
|
||||||
|
const editingChild = ref(null)
|
||||||
|
const editingSubject = ref(null)
|
||||||
|
|
||||||
async function createChild() {
|
async function createChild() {
|
||||||
await childrenStore.createChild(newChild.value)
|
await childrenStore.createChild(newChild.value)
|
||||||
@@ -85,6 +110,19 @@ async function createChild() {
|
|||||||
showChildForm.value = false
|
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) {
|
async function toggleChild(child) {
|
||||||
await childrenStore.updateChild(child.id, { is_active: !child.is_active })
|
await childrenStore.updateChild(child.id, { is_active: !child.is_active })
|
||||||
}
|
}
|
||||||
@@ -100,6 +138,22 @@ async function loadSubjects() {
|
|||||||
subjects.value = res.data
|
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() {
|
async function createSubject() {
|
||||||
const res = await api.post('/api/subjects', newSubject.value)
|
const res = await api.post('/api/subjects', newSubject.value)
|
||||||
subjects.value.push(res.data)
|
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; }
|
.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 {
|
.btn-primary {
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
background: #4f46e5;
|
background: #4f46e5;
|
||||||
|
|||||||
Reference in New Issue
Block a user