import { useEffect, useState } from 'react' import { Link } from 'react-router-dom' import { Helmet } from 'react-helmet-async' import { getCustomerDashboardSummary, listCustomers, type CustomerDashboardSummary, } from '../../../services/api/customers' import type { CustomerEntity } from '../../../services/api/customers' import { usePageContext } from '../../../shared/pageContext' import { DashboardLayout } from '../../../layouts/DashboardLayout' const emptySummary: CustomerDashboardSummary = { total: 0, today: 0, followUp: 0, simulated: 0, weeklyRecords: 0, } const HAIR_LENGTH_API_MAP: Record = { '全部': undefined, '短发': 'SHORT', '中长发': 'MEDIUM', '长发': 'LONG', '未确认': 'UNCONFIRM', '全て': undefined, 'ショート': 'SHORT', 'ミディアム': 'MEDIUM', 'ロング': 'LONG', '未確認': 'UNCONFIRM', } function getHairLengthLabel(length: string | undefined, pageCopy: CustomerListPageCopy) { if (length === 'SHORT') return pageCopy.hairLengthMap.short if (length === 'MEDIUM') return pageCopy.hairLengthMap.medium if (length === 'LONG') return pageCopy.hairLengthMap.long if (length === 'UNCONFIRM') return pageCopy.statusMap?.none || '—' return '—' } function getVisiblePages(currentPage: number, totalPages: number) { if (totalPages <= 5) { return Array.from({ length: totalPages }, (_, index) => index + 1) } const start = Math.max(1, Math.min(currentPage - 2, totalPages - 4)) return Array.from({ length: 5 }, (_, index) => start + index) } type CustomerListPageCopy = ReturnType['copy']['customerListPage'] export function CustomerListPage() { const { locale, path, copy } = usePageContext() const [items, setItems] = useState([]) const [summary, setSummary] = useState(emptySummary) const [query, setQuery] = useState('') const [page, setPage] = useState(1) const [pageSize] = useState(20) const [total, setTotal] = useState(0) const [styleFilter, setStyleFilter] = useState(copy.customerListPage.filters.styleOptions[0]) const pageCopy = copy.customerListPage useEffect(() => { const timer = setTimeout(() => { listCustomers({ page, pageSize, q: query.trim() || undefined, hairLength: HAIR_LENGTH_API_MAP[styleFilter], }) .then((result) => { setItems(result.items) setTotal(result.total) }) .catch(() => { setItems([]) setTotal(0) }) }, 300) return () => clearTimeout(timer) }, [page, pageSize, query, styleFilter]) useEffect(() => { setPage(1) }, [query, styleFilter]) useEffect(() => { getCustomerDashboardSummary().then(setSummary).catch(() => setSummary(emptySummary)) }, []) const totalPages = Math.max(1, Math.ceil(total / pageSize)) const visiblePages = getVisiblePages(page, totalPages) useEffect(() => { if (page > totalPages) { setPage(totalPages) } }, [page, totalPages]) return ( {`Aitori Stylist | ${pageCopy.title}`}
{pageCopy.badge}

{pageCopy.title}

{pageCopy.lead}

{pageCopy.newCustomer}
setQuery(event.target.value)} placeholder={pageCopy.filters.searchPlaceholder} />
{summary.total}{pageCopy.stats.total}
{summary.simulated}{pageCopy.stats.simulated}
{pageCopy.resultBadge}

{pageCopy.resultTitle.replace('{{count}}', String(total))}

{pageCopy.tableHeaders.map((label: string, index: number) => ( {label} ))}
{items.length === 0 ? (

{copy.common.empty}

) : items.map((customer) => (
{customer.name}
{customer.name} {customer.phone} {' / '} {getHairLengthLabel(customer.hairLength, pageCopy)}
{customer.trouble || '—'} {pageCopy.detail} ))}
{visiblePages.map((pageNumber) => ( ))}
) }