139 lines
7.0 KiB
TypeScript
139 lines
7.0 KiB
TypeScript
'use client'
|
||
|
||
import { useMemo, useState } from 'react'
|
||
import {
|
||
CheckCircle2,
|
||
ChevronDown,
|
||
Clock3,
|
||
PackageOpen,
|
||
} from 'lucide-react'
|
||
import type {
|
||
MonthlyLaunchBrandSummary,
|
||
MonthlyLaunchInsights,
|
||
MonthlyLaunchWorkflowStatus,
|
||
} from '@/lib/ding-workflow-sync'
|
||
import MonthlyLaunchPeriodFilter from './MonthlyLaunchPeriodFilter'
|
||
import { formatMonthlyLaunchPeriod } from './monthly-launch-period'
|
||
import WorkflowDetailTable from './WorkflowDetailTable'
|
||
|
||
function statusLabel(status: MonthlyLaunchWorkflowStatus, period: string) {
|
||
return status === 'completed' ? `${period}已完结新品` : '当前进行中新品'
|
||
}
|
||
|
||
export default function MonthlyLaunchesInsights({ insights }: { insights: MonthlyLaunchInsights }) {
|
||
const [selectedBrand, setSelectedBrand] = useState(insights.brands[0]?.brand || '亿家达')
|
||
const [selectedStatus, setSelectedStatus] = useState<MonthlyLaunchWorkflowStatus>('completed')
|
||
const [visibleCount, setVisibleCount] = useState(30)
|
||
const period = formatMonthlyLaunchPeriod(insights.summary.monthStart, insights.summary.monthEnd)
|
||
const monthOptions = insights.availableMonths || []
|
||
|
||
const selectedItems = useMemo(() => insights.items.filter((item) => (
|
||
item.brand === selectedBrand && item.launchStatus === selectedStatus
|
||
)), [insights.items, selectedBrand, selectedStatus])
|
||
|
||
const selectGroup = (brand: MonthlyLaunchBrandSummary['brand'], status: MonthlyLaunchWorkflowStatus) => {
|
||
setSelectedBrand(brand)
|
||
setSelectedStatus(status)
|
||
setVisibleCount(30)
|
||
}
|
||
|
||
return (
|
||
<section aria-labelledby="monthly-launch-title" className="mt-10">
|
||
<div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
|
||
<div>
|
||
<h2 id="monthly-launch-title" className="text-xl font-bold tracking-[-0.02em] sm:text-2xl">六品牌{period}新品流程</h2>
|
||
<p className="mt-1 max-w-3xl text-sm leading-6 text-slate-600">
|
||
已完结按所选日期范围内整条美术设计流程的完结时间统计;进行中复用“进行中项目”的当前品牌中心在办美术流程口径。流程自 2026 年 4 月 1 日起计入。
|
||
</p>
|
||
</div>
|
||
<p className="text-sm font-semibold tabular-nums text-slate-600">
|
||
{period}已完结 {insights.summary.completed} · 当前进行中 {insights.summary.ongoing}
|
||
</p>
|
||
</div>
|
||
|
||
<MonthlyLaunchPeriodFilter summary={insights.summary} options={monthOptions} />
|
||
|
||
<div className="mt-5 grid gap-3 lg:grid-cols-2" aria-label="六品牌新品流程数量">
|
||
{insights.brands.map((brand) => (
|
||
<article key={brand.brand} className="min-w-0 overflow-hidden rounded-xl border border-slate-200 bg-white">
|
||
<div className="flex items-center justify-between gap-4 border-b border-slate-200 px-5 py-4">
|
||
<h3 className="min-w-0 truncate text-lg font-bold text-slate-950">{brand.brand}</h3>
|
||
<p className="shrink-0 text-sm font-semibold tabular-nums text-slate-500">合计 {brand.total}</p>
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-px bg-slate-200">
|
||
{([
|
||
{ status: 'completed' as const, label: '本月已完结', count: brand.completed, icon: CheckCircle2 },
|
||
{ status: 'ongoing' as const, label: '当前进行中', count: brand.ongoing, icon: Clock3 },
|
||
]).map((group) => {
|
||
const Icon = group.icon
|
||
const groupLabel = group.status === 'completed' ? `${period}已完结` : group.label
|
||
const selected = selectedBrand === brand.brand && selectedStatus === group.status
|
||
return (
|
||
<button
|
||
key={group.status}
|
||
type="button"
|
||
aria-pressed={selected}
|
||
aria-label={`查看${brand.brand}${groupLabel}明细,共${group.count}条`}
|
||
onClick={() => selectGroup(brand.brand, group.status)}
|
||
className={`group min-w-0 px-5 py-4 text-left transition-colors focus-visible:z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[#5a9ce6] ${selected ? 'bg-[#e5f2ff] text-[#075fab]' : 'bg-white hover:bg-slate-50'}`}
|
||
>
|
||
<div className="flex items-start justify-between gap-3">
|
||
<span className="inline-flex min-w-0 items-center gap-2 text-sm font-bold">
|
||
<Icon className="h-4 w-4 shrink-0" aria-hidden="true" />
|
||
<span className="truncate">{groupLabel}</span>
|
||
</span>
|
||
<span className="shrink-0 text-2xl font-bold tabular-nums">{group.count}</span>
|
||
</div>
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
</article>
|
||
))}
|
||
</div>
|
||
|
||
<section aria-labelledby="monthly-launch-detail-title" className="mt-6 overflow-hidden rounded-xl border border-slate-200 bg-white">
|
||
<div className="flex flex-col gap-2 border-b border-slate-200 px-5 py-4 sm:flex-row sm:items-center sm:justify-between">
|
||
<div>
|
||
<h3 id="monthly-launch-detail-title" className="text-lg font-bold text-slate-950">
|
||
{selectedBrand} · {statusLabel(selectedStatus, period)}
|
||
</h3>
|
||
<p className="mt-1 text-sm text-slate-500">明细字段与“进行中项目”一致,完整展示表单信息、当前节点与业务时间。</p>
|
||
</div>
|
||
<p className="text-sm font-semibold tabular-nums text-slate-600">共 {selectedItems.length} 条</p>
|
||
</div>
|
||
|
||
{selectedItems.length === 0 ? (
|
||
<div role="status" className="px-5 py-14 text-center">
|
||
<PackageOpen className="mx-auto h-8 w-8 text-slate-400" aria-hidden="true" />
|
||
<h4 className="mt-4 font-bold text-slate-950">当前分组暂无流程</h4>
|
||
<p className="mt-2 text-sm text-slate-500">切换品牌或流程状态查看其他明细。</p>
|
||
</div>
|
||
) : (
|
||
<>
|
||
<div className="p-4 sm:p-5">
|
||
<WorkflowDetailTable
|
||
items={selectedItems.slice(0, visibleCount)}
|
||
tableLabel={`${selectedBrand}${statusLabel(selectedStatus, period)}流程明细横向滚动表格`}
|
||
mobileListLabel={`${selectedBrand}${statusLabel(selectedStatus, period)}流程列表`}
|
||
/>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{visibleCount < selectedItems.length && (
|
||
<div className="border-t border-slate-200 p-4 text-center">
|
||
<button
|
||
type="button"
|
||
onClick={() => setVisibleCount((count) => count + 30)}
|
||
className="inline-flex min-h-10 items-center gap-2 rounded-xl bg-slate-100 px-4 text-sm font-bold text-slate-700 transition-colors hover:bg-slate-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400"
|
||
>
|
||
展开更多<ChevronDown className="h-4 w-4" aria-hidden="true" />
|
||
</button>
|
||
</div>
|
||
)}
|
||
</section>
|
||
</section>
|
||
)
|
||
}
|