eCourt-Doc-Edit/web/app/pages/templates/index.vue
2026-08-20 11:15:04 +07:00

173 lines
7.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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`"
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"
>
แก้ไข
</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>