173 lines
7.2 KiB
Vue
173 lines
7.2 KiB
Vue
<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>
|