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
|
2026-08-20 11:15:04 +07:00
|
|
|
|
: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"
|
2026-08-20 11:15:04 +07:00
|
|
|
|
>
|
|
|
|
|
|
เอกสาร
|
|
|
|
|
|
</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>
|