[CLAUDE] FE-User: redesign foundation "nâng màu giữ brand" — gradient/accent/badge bắt mắt hơn
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m24s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m24s
Anh: giao diện đơn điệu, muốn đẹp + bắt mắt, font/màu đẹp hơn. Hướng anh chốt "nâng màu, giữ nền xanh brand" (eoffice trước). Foundation lan tỏa toàn app, KHÔNG đụng 65 trang lẻ. - index.css: +accent palette teal/amberx/violet/greenx (đặt tên né trùng Tailwind) + utilities .app-gradient-brand / .card-accent / .icon-chip / .stat-value; heading 600->700 đậm hơn; .label-eyebrow brand-600. Brand #1F7DC1 + Be Vietnam Pro GIỮ. - primitives: Button primary/danger gradient nổi bật; Input/Select/Textarea focus-glow mạnh hơn; Label brand-600; Dialog title-bar gradient. variant/size keys STABLE. - shell: Layout stripe dày hơn + logo cap; PageHeader title lớn/đậm + accent bar cao; TopBar gradient hairline; DataTable thead gradient brand chữ trắng. - Dashboard: KPI cards accent + icon chips. - color maps (contract/PE phase + PE display status): -700->-800 đậm chữ, phase nháp tint brand. Visual-only — props/handler/signature nguyên. Build PASS (tsc -b 0 error). a11y: contrast AA + prefers-reduced-motion. fe-admin mirror đợt sau. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -28,6 +28,22 @@ const fmtMoney = (v: number) => {
|
||||
return v.toLocaleString('vi-VN')
|
||||
}
|
||||
|
||||
// 2026-06-16 redesign (anh "nâng màu, bắt mắt hơn"): KPI cards now carry a
|
||||
// coloured LEFT rail (.card-accent --accent) + a tinted icon-chip + a big stat
|
||||
// number. Each tone owns a distinct accent from the new palette so the row
|
||||
// scans as 5 colours, not 5 greys. `tone` extended (brand/teal/warn/good/danger
|
||||
// /violet) — StatCard is local to this page, no external call-site.
|
||||
type StatTone = 'default' | 'teal' | 'warn' | 'good' | 'danger' | 'violet'
|
||||
|
||||
const STAT_TONE: Record<StatTone, { rail: string; chipBg: string; chipFg: string }> = {
|
||||
default: { rail: 'var(--color-brand-500)', chipBg: 'var(--color-brand-50)', chipFg: 'var(--color-brand-600)' },
|
||||
teal: { rail: 'var(--color-teal-500)', chipBg: 'var(--color-teal-50)', chipFg: 'var(--color-teal-700)' },
|
||||
warn: { rail: 'var(--color-amberx-500)', chipBg: 'var(--color-amberx-50)', chipFg: 'var(--color-amberx-700)' },
|
||||
good: { rail: 'var(--color-greenx-500)', chipBg: 'var(--color-greenx-50)', chipFg: 'var(--color-greenx-700)' },
|
||||
danger: { rail: 'var(--color-accent-500)', chipBg: '#fdecec', chipFg: 'var(--color-accent-600)' },
|
||||
violet: { rail: 'var(--color-violet-500)', chipBg: 'var(--color-violet-50)', chipFg: 'var(--color-violet-700)' },
|
||||
}
|
||||
|
||||
function StatCard({
|
||||
icon: Icon,
|
||||
label,
|
||||
@ -40,27 +56,27 @@ function StatCard({
|
||||
label: string
|
||||
value: React.ReactNode
|
||||
hint?: string
|
||||
tone?: 'default' | 'warn' | 'good' | 'danger'
|
||||
tone?: StatTone
|
||||
onClick?: () => void
|
||||
}) {
|
||||
const toneClass =
|
||||
tone === 'warn' ? 'text-amber-600 bg-amber-50' :
|
||||
tone === 'good' ? 'text-emerald-600 bg-emerald-50' :
|
||||
tone === 'danger' ? 'text-red-600 bg-red-50' :
|
||||
'text-brand-600 bg-brand-50'
|
||||
const t = STAT_TONE[tone]
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={!onClick}
|
||||
className="rounded-lg border border-slate-200 bg-white p-4 text-left shadow-sm transition hover:shadow-md disabled:cursor-default disabled:hover:shadow-sm"
|
||||
className="card-accent flex flex-col p-4 text-left disabled:cursor-default"
|
||||
style={{ ['--accent' as string]: t.rail }}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-xs font-medium text-slate-500">{label}</div>
|
||||
<span className={`flex h-6 w-6 items-center justify-center rounded ${toneClass}`}>
|
||||
<Icon className="h-3.5 w-3.5" />
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="text-[11px] font-semibold uppercase tracking-wide text-slate-500">{label}</div>
|
||||
<span
|
||||
className="icon-chip h-9 w-9"
|
||||
style={{ ['--chip-bg' as string]: t.chipBg, ['--chip-fg' as string]: t.chipFg }}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 text-2xl font-bold text-slate-900">{value}</div>
|
||||
<div className="stat-value mt-2 text-3xl">{value}</div>
|
||||
{hint && <div className="mt-0.5 text-xs text-slate-400">{hint}</div>}
|
||||
</button>
|
||||
)
|
||||
@ -91,7 +107,10 @@ export function UserDashboardPage() {
|
||||
/>
|
||||
|
||||
<section className="mb-6">
|
||||
<h2 className="mb-3 text-sm font-semibold text-slate-700">Của tôi</h2>
|
||||
<h2 className="mb-3 flex items-center gap-2 text-sm font-bold text-slate-800">
|
||||
<span aria-hidden className="h-4 w-1 rounded-full bg-gradient-to-b from-brand-400 to-brand-700" />
|
||||
Của tôi
|
||||
</h2>
|
||||
<div className="grid grid-cols-2 gap-3 md:grid-cols-5">
|
||||
<StatCard
|
||||
icon={Pencil}
|
||||
@ -105,7 +124,7 @@ export function UserDashboardPage() {
|
||||
label="Chờ tôi duyệt"
|
||||
value={s?.pendingMyApproval ?? '—'}
|
||||
hint="Vào Hộp thư"
|
||||
tone="good"
|
||||
tone="teal"
|
||||
onClick={() => navigate('/inbox')}
|
||||
/>
|
||||
<StatCard
|
||||
@ -127,15 +146,18 @@ export function UserDashboardPage() {
|
||||
icon={Coins}
|
||||
label="Tổng giá trị nháp"
|
||||
value={s ? fmtMoney(s.draftsTotalValue) : '—'}
|
||||
tone="violet"
|
||||
onClick={() => navigate('/my-contracts')}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded-lg border border-slate-200 bg-white">
|
||||
<header className="flex items-center justify-between border-b border-slate-200 px-4 py-3">
|
||||
<h2 className="flex items-center gap-2 text-sm font-semibold text-slate-700">
|
||||
<FileText className="h-4 w-4" />
|
||||
<section className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
|
||||
<header className="flex items-center justify-between border-b border-slate-200 bg-gradient-to-r from-brand-50/70 to-transparent px-4 py-3">
|
||||
<h2 className="flex items-center gap-2 text-sm font-bold text-slate-800">
|
||||
<span className="icon-chip h-7 w-7">
|
||||
<FileText className="h-4 w-4" />
|
||||
</span>
|
||||
HĐ gần đây
|
||||
</h2>
|
||||
<Button variant="outline" onClick={() => navigate('/my-contracts')}>
|
||||
|
||||
Reference in New Issue
Block a user