eCourt-Doc-Edit/web/app/pages/templates/index.vue
2026-08-20 21:34:45 +07:00

162 lines
7 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-6">
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div class="min-w-0">
<h1 class="text-page-title">จัดการ template</h1>
</div>
<div class="flex shrink-0 flex-wrap items-center gap-2">
<NuxtLink to="/templates/new" class="btn btn-primary w-full sm:w-auto">+ สร้าง template ใหม่</NuxtLink>
</div>
</div>
<label class="flex items-center gap-2 text-sm text-muted">
<input v-model="showInactive" type="checkbox" class="h-4 w-4 accent-primary" />
แสดง template ที่ deactivate ไว้ด้วย
</label>
<div v-if="loading" class="flex items-center gap-3 py-10 text-sm text-muted">
<span class="spinner" />
กำลังโหลด…
</div>
<AlertBox v-else-if="error" type="error">{{ error }}</AlertBox>
<div v-else-if="templates.length === 0" class="empty-state">
ยังไม่มี template{{ showInactive ? ' เลย' : 'ที่ active' }} —
<NuxtLink to="/templates/new" class="link">สร้าง template แรก</NuxtLink>
</div>
<template v-else>
<!-- มือถือ: card list -->
<ul class="space-y-3 md:hidden">
<li v-for="t in templates" :key="t.id" class="card space-y-3 p-4">
<div class="flex items-start justify-between gap-3">
<NuxtLink :to="`/templates/${t.id}/edit`" class="link min-w-0 break-words font-semibold">
{{ t.name }}
</NuxtLink>
<span v-if="t.isActive" class="badge badge-success shrink-0">active</span>
<span v-else class="badge badge-neutral shrink-0">deactivated</span>
</div>
<p class="line-clamp-2 text-sm text-muted">{{ t.description ?? '—' }}</p>
<p class="text-xs text-muted">
{{ t.fieldCount }} fields · อัปเดต {{ formatDateTime(t.updatedAt) }}
</p>
<div class="flex flex-wrap gap-2">
<NuxtLink :to="`/templates/${t.id}/documents`" class="btn btn-secondary btn-xs">เอกสาร</NuxtLink>
<NuxtLink :to="`/templates/${t.id}/edit`" class="btn btn-secondary btn-xs">แก้ไข</NuxtLink>
<button
type="button"
:disabled="busyId === t.id"
class="btn btn-xs"
:class="t.isActive ? 'btn-outline-danger' : 'btn-outline-success'"
@click="toggleActive(t)"
>
{{ busyId === t.id ? '…' : t.isActive ? 'deactivate' : 'activate' }}
</button>
</div>
</li>
</ul>
<!-- desktop: ตาราง -->
<div class="table-card hidden md:block">
<table class="w-full text-sm">
<thead class="table-head">
<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-line">
<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="link">{{ t.name }}</NuxtLink>
</td>
<td class="max-w-60 px-4 py-3 text-muted">{{ 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="badge badge-success">active</span>
<span v-else class="badge badge-neutral">deactivated</span>
</td>
<td class="px-4 py-3 text-muted">{{ formatDateTime(t.updatedAt) }}</td>
<td class="whitespace-nowrap px-4 py-3 text-right">
<NuxtLink :to="`/templates/${t.id}/documents`" class="btn btn-secondary btn-xs">เอกสาร</NuxtLink>
<NuxtLink :to="`/templates/${t.id}/edit`" class="btn btn-secondary btn-xs ml-2">แก้ไข</NuxtLink>
<button
type="button"
:disabled="busyId === t.id"
class="btn btn-xs ml-2"
:class="t.isActive ? 'btn-outline-danger' : 'btn-outline-success'"
@click="toggleActive(t)"
>
{{ busyId === t.id ? '…' : t.isActive ? 'deactivate' : 'activate' }}
</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<p class="text-xs text-muted">
การ deactivate เป็น soft-delete — เอกสารที่สร้างจาก template นั้นไปแล้วยังใช้งานต่อได้ตามปกติ
</p>
</section>
</AuthGate>
</template>