// Generic Workflow Apps List Page — Phase 10.3 G-O4+G-O5+G-O6 (S38 2026-05-28). // SKELETON Phase 1: read-only list. Create form + workflow actions DEFER Phase 11. // Handles 4 module via URL `:kind` param: leave / ot / travel / vehicle. // File MIRROR SHA256 identical fe-user counterpart. import { useQuery } from '@tanstack/react-query' import { useParams } from 'react-router-dom' import { CalendarOff, Clock, Plane, Car, FileSignature } from 'lucide-react' import { PageHeader } from '@/components/PageHeader' import { api } from '@/lib/api' import { cn } from '@/lib/cn' import { WORKFLOW_APP_STATUS_BADGE, WORKFLOW_APP_STATUS_LABELS, type PagedResult, } from '@/types/workflowApps' type Kind = 'leave' | 'ot' | 'travel' | 'vehicle' const KIND_CONFIG: Record React.ReactNode }> }> = { leave: { title: 'Đơn xin nghỉ phép', description: 'Danh sách đơn nghỉ phép — Workflow V2 ApplicableType=5', endpoint: '/leave-requests', columns: [ { key: 'maDonTu', label: 'Mã', render: (x) => {x.maDonTu ?? '—'} }, { key: 'requesterFullName', label: 'Người xin', render: (x) => x.requesterFullName }, { key: 'startDate', label: 'Từ', render: (x) => new Date(x.startDate).toLocaleDateString('vi-VN') }, { key: 'endDate', label: 'Đến', render: (x) => new Date(x.endDate).toLocaleDateString('vi-VN') }, { key: 'numDays', label: 'Ngày', render: (x) => x.numDays }, { key: 'reason', label: 'Lý do', render: (x) => {x.reason} }, ], }, ot: { title: 'Đơn đăng ký OT', description: 'Danh sách đơn OT — Workflow V2 ApplicableType=6', endpoint: '/ot-requests', columns: [ { key: 'maDonTu', label: 'Mã', render: (x) => {x.maDonTu ?? '—'} }, { key: 'requesterFullName', label: 'Người xin', render: (x) => x.requesterFullName }, { key: 'otDate', label: 'Ngày OT', render: (x) => new Date(x.otDate).toLocaleDateString('vi-VN') }, { key: 'hours', label: 'Giờ', render: (x) => x.hours }, { key: 'reason', label: 'Lý do', render: (x) => {x.reason} }, ], }, travel: { title: 'Đơn đi công tác', description: 'Danh sách đăng ký công tác', endpoint: '/travel-requests', columns: [ { key: 'maDonTu', label: 'Mã', render: (x) => {x.maDonTu ?? '—'} }, { key: 'requesterFullName', label: 'Người xin', render: (x) => x.requesterFullName }, { key: 'destination', label: 'Địa điểm', render: (x) => x.destination }, { key: 'startDate', label: 'Từ', render: (x) => new Date(x.startDate).toLocaleDateString('vi-VN') }, { key: 'endDate', label: 'Đến', render: (x) => new Date(x.endDate).toLocaleDateString('vi-VN') }, { key: 'purpose', label: 'Mục đích', render: (x) => {x.purpose} }, ], }, vehicle: { title: 'Đặt xe công', description: 'Danh sách booking xe — Workflow V2 ApplicableType=7', endpoint: '/vehicle-bookings', columns: [ { key: 'maDonTu', label: 'Mã', render: (x) => {x.maDonTu ?? '—'} }, { key: 'requesterFullName', label: 'Người đặt', render: (x) => x.requesterFullName }, { key: 'vehicleLicense', label: 'Biển số', render: (x) => {x.vehicleLicense} }, { key: 'destination', label: 'Đến', render: (x) => x.destination }, { key: 'startAt', label: 'Bắt đầu', render: (x) => new Date(x.startAt).toLocaleString('vi-VN') }, { key: 'driverName', label: 'Tài xế', render: (x) => x.driverName ?? '—' }, ], }, } const ICON_MAP: Record = { leave: CalendarOff, ot: Clock, travel: Plane, vehicle: Car, } export function WorkflowAppsListPage() { const { kind = 'leave' } = useParams<{ kind: Kind }>() const config = KIND_CONFIG[kind as Kind] const Icon = ICON_MAP[kind as Kind] ?? FileSignature const list = useQuery({ queryKey: [config.endpoint, { page: 1 }], queryFn: async () => (await api.get>(config.endpoint, { params: { page: 1, pageSize: 50 } })).data, enabled: !!config, }) const items = list.data?.items ?? [] if (!config) { return
Module không tồn tại: {kind}
} return (
⚠️ Skeleton Phase 1 (S38): Read-only list. Form tạo + workflow Approve/Reject defer Phase 11 polish. Em chủ trì kích hoạt full ApproveV2 wire khi anh main yêu cầu.
{config.columns.map((c) => ( ))} {list.isLoading && ( )} {!list.isLoading && items.length === 0 && ( )} {items.map((item: any) => ( {config.columns.map((c) => ( ))} ))}
{c.label}Trạng thái
Đang tải...
Chưa có dữ liệu.
{c.render(item)} {WORKFLOW_APP_STATUS_LABELS[item.status]}
) }