eCourt-Doc-Edit/web/app/pages/templates/index.vue

174 lines
7.2 KiB
Vue
Raw Normal View History

2026-08-20 06:20:16 +07:00
<script setup lang="ts">
/** FR-9 UI — รายการ template ทั้งหมด (admin): ดู/แก้ไข/activate-deactivate */
import type { TemplateListItem } from '~/types/api'
const { apiJson } = useApi()
useHead({ title: 'จัดการ template — e-Court Documents Versioning' })
const templates = ref<TemplateListItem[]>([])
const loading = ref(true)
const error = ref<string | null>(null)
const showInactive = ref(false)
const busyId = ref<string | null>(null)
async function load() {
loading.value = true
error.value = null
try {
const qs = showInactive.value ? '?includeInactive=true' : ''
templates.value = (await apiJson<TemplateListItem[]>(`/api/templates${qs}`)) ?? []
} catch (err) {
error.value = err instanceof Error ? err.message : 'โหลดรายการ template ไม่สำเร็จ'
} finally {
loading.value = false
}
}
/** deactivate = DELETE (soft-delete), activate กลับ = PUT { isActive: true } */
async function toggleActive(t: TemplateListItem) {
const verb = t.isActive ? 'deactivate' : 'activate'
if (!window.confirm(`ยืนยัน${verb === 'deactivate' ? 'การ deactivate' : 'การ activate'} template “${t.name}”?`)) return
busyId.value = t.id
error.value = null
try {
if (t.isActive) await apiJson(`/api/templates/${t.id}`, { method: 'DELETE' })
else await apiJson(`/api/templates/${t.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ isActive: true }),
})
await load()
} catch (err) {
error.value = err instanceof Error ? err.message : `${verb} ไม่สำเร็จ`
} finally {
busyId.value = null
}
}
watch(showInactive, load)
onMounted(load)
</script>
<template>
<AuthGate>
<section class="space-y-5">
<div class="flex flex-wrap items-center justify-between gap-3">
<h1 class="text-2xl font-bold">จัดการ template</h1>
<NuxtLink
to="/templates/new"
class="rounded-md bg-sky-600 px-4 py-2 text-sm font-medium text-white hover:bg-sky-700"
>
+ สร้าง template ใหม่
</NuxtLink>
</div>
<label class="flex items-center gap-2 text-sm text-slate-600 dark:text-slate-300">
<input
v-model="showInactive"
type="checkbox"
class="h-4 w-4 rounded border-slate-300 dark:border-slate-600 dark:bg-slate-800"
/>
แสดง template ที่ deactivate ไว้ด้วย
</label>
<div v-if="loading" class="flex items-center gap-3 py-10 text-sm text-slate-500 dark:text-slate-400">
<span
class="h-4 w-4 animate-spin rounded-full border-2 border-slate-300 border-t-slate-600 dark:border-slate-600 dark:border-t-slate-300"
/>
กำลังโหลด…
</div>
<AlertBox v-else-if="error" type="error">{{ error }}</AlertBox>
<div
v-else-if="templates.length === 0"
class="rounded-lg border border-dashed border-slate-300 p-8 text-center text-sm text-slate-500 dark:border-slate-600 dark:text-slate-400"
>
ยังไม่มี template{{ showInactive ? ' เลย' : 'ที่ active' }} —
<NuxtLink to="/templates/new" class="font-medium text-sky-600 hover:underline dark:text-sky-400">
สร้าง template แรก
</NuxtLink>
</div>
<div
v-else
class="overflow-x-auto rounded-lg border border-slate-200 bg-white dark:border-slate-700 dark:bg-slate-800"
>
<table class="w-full text-sm">
<thead class="border-b border-slate-200 text-left text-xs uppercase text-slate-500 dark:border-slate-700 dark:text-slate-400">
<tr>
<th class="px-4 py-3">ชื่อ</th>
<th class="px-4 py-3">คำอธิบาย</th>
<th class="px-4 py-3 text-center">fields</th>
<th class="px-4 py-3">สถานะ</th>
<th class="px-4 py-3">อัปเดตล่าสุด</th>
<th class="px-4 py-3 text-right">การกระทำ</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 dark:divide-slate-700">
<tr v-for="t in templates" :key="t.id" class="align-top">
<td class="px-4 py-3 font-medium">
<NuxtLink
:to="`/templates/${t.id}/edit`"
class="text-sky-700 hover:underline dark:text-sky-400"
>
{{ t.name }}
</NuxtLink>
</td>
<td class="max-w-60 px-4 py-3 text-slate-600 dark:text-slate-300">{{ t.description ?? '—' }}</td>
<td class="px-4 py-3 text-center">{{ t.fieldCount }}</td>
<td class="px-4 py-3">
<span
v-if="t.isActive"
class="rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-medium text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300"
>
active
</span>
<span
v-else
class="rounded-full bg-slate-200 px-2 py-0.5 text-xs font-medium text-slate-600 dark:bg-slate-700 dark:text-slate-300"
>
deactivated
</span>
</td>
<td class="px-4 py-3 text-slate-500 dark:text-slate-400">{{ formatDateTime(t.updatedAt) }}</td>
<td class="px-4 py-3 text-right whitespace-nowrap">
<NuxtLink
:to="`/templates/${t.id}/documents`"
2026-08-20 06:20:16 +07:00
class="rounded-md border border-slate-300 px-2.5 py-1 text-xs hover:bg-slate-100 dark:border-slate-600 dark:hover:bg-slate-700"
>
เอกสาร
</NuxtLink>
<NuxtLink
:to="`/templates/${t.id}/edit`"
class="ml-2 rounded-md border border-slate-300 px-2.5 py-1 text-xs hover:bg-slate-100 dark:border-slate-600 dark:hover:bg-slate-700"
2026-08-20 06:20:16 +07:00
>
แก้ไข
</NuxtLink>
<button
type="button"
:disabled="busyId === t.id"
class="ml-2 rounded-md border px-2.5 py-1 text-xs disabled:opacity-50"
:class="
t.isActive
? 'border-red-300 text-red-600 hover:bg-red-50 dark:border-red-800 dark:text-red-400 dark:hover:bg-red-950'
: 'border-emerald-300 text-emerald-600 hover:bg-emerald-50 dark:border-emerald-800 dark:text-emerald-400 dark:hover:bg-emerald-950'
"
@click="toggleActive(t)"
>
{{ busyId === t.id ? '…' : t.isActive ? 'deactivate' : 'activate' }}
</button>
</td>
</tr>
</tbody>
</table>
</div>
<p class="text-xs text-slate-500 dark:text-slate-400">
การ deactivate เป็น soft-delete — เอกสารที่สร้างจาก template นั้นไปแล้วยังใช้งานต่อได้ตามปกติ
</p>
</section>
</AuthGate>
</template>