19 KiB
19 KiB
frontend-designer — archive 2026-06 (L2 verbatim · FROZEN additive)
Curated from L1
MEMORY.md@S96 (2026-07-01) — first-overflow distillation (L1 hit 26129B > autoinject-cap 25600B, H17-loop strike-1). Older Activity-log war-stories moved here VERBATIM (byte-exact, additive-only — NEVER rewrite existing bytes). Recent 3 (S79/S69×2) + all reference sections (design-system · FD2-rig · component-inventory · lessons) stay in L1. Map →_INDEX.md· 4-field distill →2026-06.gist.md.
Activity log (archived — moved-set, newest-of-moved on top)
- S66 (2026-06-16) HRM Hồ sơ Nhân sự fe-user REFINE từ eoffice LIVE (3 việc) — layout 3-cột→2-cột + tô màu detail: anh góp ý sau khi xem prod. Việc 1 (layout): 3-cột-ngang
[tree 244 | list 352 | detail 1fr]→ 2-cộtlg:grid-cols-[22rem_1fr] xl:[24rem_1fr]: CỘT TRÁI =<div flex flex-col gap-4>ôm tree (TRÊN,lg:max-h-[44%] lg:shrink-0, overflow-auto) + list+filter (DƯỚI,flex-1, overflow-auto) — mỗi panel cuộn độc lập; CỘT PHẢI = detail (flex-1, rộng hơn nhiều, đỡ chật). <lg vẫn 1-col tree→list→detail + giữ nguyêntreeOpenMobiletoggle. Việc 2+3 (màu detail): thêmACCENTmap 5 tone (brand/teal/violet/amberx/greenx) →Cardpropaccenttô icon-chip nền nhạt (--chip-bg/--chip-fg) + headingtext-{x}-700+ rail trái pseudo-element;Fieldlabel uppercasetext-{x}-700semibold (was slate-400 đơn điệu) + valuefont-medium text-slate-900; mỗi card/section gán 1 accent → có màu rõ nhưng tinh tế, brand #1F7DC1 + Be Vietnam Pro + avatar gradient brand GIỮ. Strategy chống truncation #53 = ONE atomicWritecả file (1556 LOC) → emit change-list + build status SỚM. 2 self-caught bug TRƯỚC build: (1)text-{teal,violet,greenx}-800— accent palettes KHÔNG có stop -800 (chỉ 50/100/500/600/700) → Tailwind v4 silent no-class → đổi head sang -700 (all AA on white); (2) rail pseudo thiếubefore:content-['']→ ::before không render box → thêm.npm run build(tsc -b strict + vite v8) PASS 0 TS err, 495ms (warning @import-order + chunk-size = pre-existing, không phải mình). 5 satellite CRUD + 15 satellite api endpoint + top-level del + 3 reads + 3 query keys (employees-list/employee-detail/departments-tree-hrm) + cây SOLUTION COMPANY + 5 tab + search/filter preserved VERBATIM (grep: 15 satellite api.post/put/delete + 3 queryKey + 5 form fns; tsc type-checks mọi payload shape = wiring bất biến). FD2 authed-screenshot SKIPPED per task instruction + gotcha #3 (rig chặn authed ProtectedRoute; anh xem qua deploy) → structural verify thay thế. fe-admin + BE NOT touched, no commit (em main commits). Tag [s66, hrm-2col-refine, eoffice-ref, accent-system, atomic-write-antitrunc, crud-preserved, build-pass, tailwind-v4-stop-gotcha]. - S65 (2026-06-16) HRM Hồ sơ Nhân sự fe-user → 3-panel master-detail NamGroup-ref: RESTRUCTURE
EmployeesListPage.tsx(1201→~1140 LOC) — 6<details>→ [Org tree | List | Detail 5-tab]. Strategy chống truncation #53 = ONE atomicWrite(cả file) thay piecemeal Edit (atomic Write either fully-lands or errors, KHÔNG half-break) → emit change-list TRƯỚC build → DID BOTH Part A (avatar header+5 tab+section→tab redistribution) + Part B (org tree panel) trong 1 pass, không phải defer B. Org tree consume/departments/treeverified BE-side (DepartmentFeatures.cs DepartmentTreeNodeDto, controller[HttpGet("tree")], class-Authorize only). Foundation màu mới DÙNG:.app-gradient-brandheader /.icon-chip/ accent palette teal/violet/amberx/greenx (avatar tones) — brand #1F7DC1 + Be Vietnam Pro KEPT. 5 satellite CRUD + 16 api endpoint + query keys preserved VERBATIM (grep-verified: 16 api.post/put/delete identical payload shape, 5 form fns intact).npm run build(tsc -b strict + vite) PASS 0 TS err, 6.13s. 1 self-caught bug: typo garbage token网络Placeholdertrong lucide import (mojibake autocomplete) → removed, all 21 icons valid (node-checked). FD2 authed-screenshot SKIPPED per explicit task instruction + gotcha #3 (rig blocks authed; anh xem qua deploy) — did static structural verify instead (grep endpoint/key preservation). fe-admin NOT touched (mirror = separate pass), no commit. Tag [s65, hrm-3panel, namgroup-ref, atomic-write-antitrunc, crud-preserved, build-pass]. - S58 (2026-06-11) fe-user redesign theo UI/UX guide AI_INFRA canonical — KEEP brand [em main proxy — truncated #53 giữa FD2 screenshot, 2nd consecutive]: Mirror design-system fe-admin S55 → 14 file fe-user (index.css heading-ladder+.label-eyebrow / 6 ui primitives — Button gần SHA-identical fe-admin chỉ khác comment / 6 shell DataTable+RowActions-additive·Layout-brand-left-rail·TopBar·PageHeader·PhaseBadge-ring·EmptyState / LoginPage polish). Rubric mới = guide 13 mục
D:\Dropbox\CONG_VIEC\AI_INFRA\docs\reference\ui-ux-design-guide.md(density 14px/h32-34/radius-8/thead-sticky/action-luôn-hiện/no-font-bold). BRAND KEPT: #1F7DC1 + Be Vietnam Pro + slate (guide cho plug hue riêng). Chết NGAY TRƯỚC with_server.py screenshot /login → em main recover: build ×2 PASS 0 TS + diff-review key-stability từng file + shipe959f72; authed visual qua deploy prod (rig-gotcha #3 standing). LESSON: 2 lần liên tiếp truncate ở CÙNG điểm (sau khi sửa xong, lúc bắt đầu FD2 rig) → lần sau EMIT file-list verdict TRƯỚC khi vào screenshot loop. Tag [s58, fe-user-redesign, guide-aiinfra, keep-brand, truncated-53-proxy]. - S55 (2026-06-09) Phase-1 fe-admin redesign — density-first NAMGROUP-ref, KEEP brand [em main proxy — truncated #53]: 14 file (index.css density ladder + 6 ui primitive Button h-7/8/10 brand-ring + 6 shell + DashboardPage KPI). Brand #1F7DC1+Be Vietnam Pro KEPT. build 0 TS. Visual loop BLOCKED authed-rig#3 → chỉ /login. → S58 mirror sang fe-user. (detail archive nếu cần). Tag [s55, density-namgroup, keep-brand].
- S47 (2026-06-02) FD2 RIG VERIFIED ✅ — first real spawn. Ran full FD2 loop end-to-end on fe-user
/login: read DS (Tailwind v4 CSS-first, corrected stale config-path assumption) → started Vite viawith_server.py→ Playwright screenshot 375+1440 → Read PNGs → FD4 critique → 1-line contrast fix → re-screenshot confirmed →npm run build0 TS error. Closes adap-report2026-06-02-Agent-frontend-designer-floorFD2 runtime proof. 2 Vite gotchas captured above. Loop is REAL, not theoretical.
Moved @S160 (2026-07-29) — verbatim from L1 Activity log (moved-not-cut)
- S69 (2026-06-17) OfficeDashboardPage.tsx fe-user — E-Office landing, PURO HomePage, COMPOSES 3 shared widgets: built
pages/office/OfficeDashboardPage.tsx(~400 LOC) over EXISTING data hooks of 4 modules. Read-first: 3 ui widgets (exact prop sigs) + 4 source pages (ProposalsListPage/WorkflowAppsListPage/ItTicketsPage/MeetingCalendarPage) to harvest queryKey+endpoint + types + App.tsx routes. Reused hooks verbatim → shared TanStack cache, ZERO new API/BE:GET /proposals(+inboxOnly query for needs-my-action) ·/leave|ot|travel-requests(merged, countByStatus client-side) ·/it-tickets·/meeting-bookings(today window). Layout = PageHeader(brand) +lg:grid-cols-3[LEFT col-span-2 = 4 WidgetCards w/ KpiCard filter-chip bodies | RIGHT col-span-1 = "Công việc của tôi" hero+MetricRows + "Thao tác nhanh" 3 buttons], 1-col <lg. Routing insight (verified App.tsx): đơn-từ + ticket have NO standalone/newroute (creation in-page) → quick-actions point at landings/workflow-apps/leave+/it-tickets(only/proposals/newis a real create route) so no link hits the*"chưa build" fallback. Per-widget graceful states: WidgetError(retry)/WidgetSkeleton(pulse, motion-reduce)/empty — never blocks. a11y full (role=button+Enter/Space+focus-ring on clickables, reduced-motion).npm run build(tsc -b strict + vite v8) PASS 0 TS err, 434ms (only pre-existing @import-order + chunk-size + INEFFECTIVE_DYNAMIC_IMPORT warnings — none from new file). Routing/menu NOT wired (next agent; page not yet in App.tsx). fe-admin NOT mirrored. FD2 authed-screenshot SKIP (ProtectedRoute + rig gotcha #3 — verify via deploy). Tag [s69, office-dashboard, compose-3-widgets, reuse-hooks-shared-cache, no-new-be, routing-existing-only, build-pass]. - S69 (2026-06-17) Văn phòng số / Đơn từ fe-user RE-SKIN (PURO + HRM visual lang) — 2 file, CONSUMES shared ui (parallel fan-out, 7 agents same app): surgical re-skin (KHÔNG rewrite)
pages/office/WorkflowAppsListPage.tsx+WorkflowAppDetailPage.tsx(:kind-driven leave/ot/travel/vehicle via KIND_CONFIG). LIST: swap@/components/PageHeader(constrained)→@/components/ui/PageHeader(eyebrow "Văn phòng số · Đơn từ" / title từ KIND_CONFIG / icon per-kind / accent teal) + status filter = ROW 4ui/KpiCard(Tất cả teal / Đã gửi duyệt amberx / Trả lại violet / Đã duyệt greenx,grid-cols-2 sm:grid-cols-4) + slate table chrome (thead uppercase 11px slate-500, hover teal-50). Filter là CLIENT-SIDE view — addeduseState<StatusFilter>+ 2useMemo(counts + visibleItems) DERIVED over already-fetcheditems; KHÔNG touch query/endpoint/queryKey/navigation (page chỉ fetchpage:1như cũ, không có filter-state sẵn nên thêm view-layer = thuần presentation; empty-state phân biệt "chưa có data" vs "không có đơn ở trạng thái này"). DETAIL: ui/PageHeader teal + 4 section dùng localCard(accent-rail pseudobefore:bg-{x}-500+ icon-chip) +Field(label uppercasetext-{x}-700, valuetext-brand-800) — copy idiom HRM EmployeesListPage (KHÔNG import HRM, helper local riêng). Accent gán: Thông tin=teal · Số dư phép=greenx · Quy trình=violet · Ý kiến=brand. Status badge giữWORKFLOW_APP_STATUS_BADGE. Drop raw "⚠️" emoji trong over-budget banner→text thuần (anti-slop FD3). ALL data logic VERBATIM (grep-verified): 2 query[endpoint,id]+['approval-workflows-v2',applicableType](key/endpoint/enabledy nguyên) · 3 mutation pinWorkflow(PUT /workflow)/submit(POST /submit)/action(POST /{k}) body+onSuccess+invalidate identical · 3 state + flags isDraft/isInWorkflow/hasWorkflow + mọi onClick/nav target bất biến. gotcha Tailwind-v4 stop: dùng CHỈ -50/-500/-700 cho teal/violet/amberx/greenx (no -800) —border-l-greenx-500/bg-amberx-50/text-amberx-700đều stop tồn tại (index.css verified). Self-caught:SendHorizonal(KpiCard "Đã gửi duyệt" icon) export-aggregation-line trong lucide d.ts → đổiSend(proven-safe export) tránh alias-risk. Self-review: mọi import resolve, 0 unused local (noUnusedLocals strict) —Info/Wallet/GitBranch/MessageSquareTextđều dùng. KHÔNG run npm build (em main builds central, 7-agent parallel interference) + KHÔNG modify ui/index.css (other agents edit). FD2 authed-screenshot SKIP (ProtectedRoute + rig gotcha #3 — verify via deploy). fe-admin NOT mirrored. Tag [s69, eoffice-donutu-reskin, puro-hrm-visual, consume-ui-pageheader-kpicard, client-side-filter-view, logic-verbatim, no-build-parallel-fanout, lucide-alias-dodge].
S79 (2026-06-19) PE list fe-user — DECOUPLE chọn/mở-rộng
- S79 (2026-06-19) PE list fe-user → DECOUPLE chọn/mở-rộng (anh chốt, annotated screenshot):
pages/pe/PurchaseEvaluationsListPage.tsx. S78 cũ: bấm dòng = mở overlay full-bleed, 2 panel giữa/phải LÀ placeholder vĩnh viễn. S79 đổi: bấm dòng = inline 3-panel detail "như cũ" (8e68ed1) + thêm nút "Xem mở rộng" mỗi dòng → overlay. Mechanism = param thứ-2?expand=1cạnh?id: overlay render CHỈ khiid && expand; inline detail (panel giữa<PeDetailTabs readOnly onBack=closeDetail>+ panel phải<PeWorkflowPanel readOnly={!pendingMe} onApproved>) render khiid && !expand. Handlers:selectRow=set id+clear expand (NHƯNG<lg/innerWidth<1024 → set expand=1 đi thẳng overlay vì 3-panel không vừa) ·expandRow=id+expand=1 ·collapseFocus(Thu gọn/Esc/backdrop)=setParam('expand',null)GIỮ id (về inline, KHÔNG về list) ·closeDetail(← Đóng/del)=clear id+expand → list ·onApproved=clear cả hai → list (phiếu rời inbox). Overlay gate đổihasSelection→overlayActive=id&&expand; Esc gọi collapseFocus (không closeFocus). Nút "Xem mở rộng" =Maximize2(lucide, import THÊM) icon-btn 7×7 góc phải-trên mỗi leaf row,opacity-0 group-hover/row:opacity-100+opacity-100khi selected,title+aria-label+brand focus-ring; leaf<button>cũ bọc trong<div className="group/row relative">+pr-9chừa chỗ nút. ALL logic VERBATIM (grep ✓): 3 queryKey (approval-workflows-v2-filter/pe-list/pe-detail) · 3 endpoint (/inbox+paged+detail) ·delmutation (onSuccess giờ clear id+expand thaysetParam) ·yearGroups4-tầng tree memo ·PeUrgentChipspill · search/filter/SLA/localStorage-expand.PurchaseEvaluationDetailPageroute GIỮ. slide+a11y S78 giữ NGUYÊN (mount-rAF×2, role=dialog/aria-modal, focus-trap Tab-vòng, body scroll-lock, motion-reduce).npm run build(tsc -b strict + vite v8) PASS 0 TS err, 570ms (3 warning pre-existing). FD2: static layout-shell harness (backend :5443 down → authed dev-rig blocked gotcha#3) — Playwright 6 shot × 3 state: (a) inline 3-panel desktop1920+laptop1280 = list+PeDetailTabs giữa+PeWorkflowPanel phải, dòng-chọn có nút Maximize2; (b) overlay 1920+1280 = full-bleed phiếu+panel cạnh; (c) mobile390 list (1-col, nút mở-rộng) + mobile390 overlay (stack). Read tất 6 PNG — đúng hết, no overflow, pill+tree intact. Harness/driver/shots ĐÃ XÓA. Rubric PASS. fe-admin NOT mirrored (implementer-frontend SHA-mirror riêng), no BE, no commit. Tag [s79, pe-decouple-select-expand, expand-param, maximize2-row-btn, inline-restore-8e68ed1, lg-straight-overlay, collapse-keeps-id, logic-verbatim, build-pass, harness-fd2].
S69 (2026-06-17) 3 shared UI cho Văn phòng số / E-Office
- 3 shared UI cho Văn phòng số / E-Office (S69, 2026-06-17) — PURO-style + HRM visual language. ALL build-PASS 0 TS:
fe-user/src/components/ui/PageHeader.tsx— richer page header (eyebrow/title/subtitle/icon/accent/actions/breadcrumb). ⚠️ KHÁC@/components/PageHeader(constrained {title,description,actions}) — module path@/components/ui/PageHeaderriêng, KHÔNG collision. icon-chip accent-tinted + titletext-xl font-boldaccent-head. LocalACCENTmap {chipBg,chipFg,head} (brand=text-brand-800, rest -700). Title trên nền SÁNG → KHÔNG cầntext-white!.fe-user/src/components/ui/KpiCard.tsx— clickable stat card = FILTER chip (PURO: row KpiCards thay tabs). icon-chip +.stat-value text-2xlaccent +.label-eyebrow. active =bg-{x}-50+border-{x}-300+ring-{x}-500. a11y FULL:onClick→role=button+tabIndex=0+Enter/Space (e.key===' ')+aria-pressed=active+focus-visible ring; no-onClick = inert div. hover-translate-y-0.5+motion-reduce:transform-none.fe-user/src/components/ui/WidgetCard.tsx— dashboard widget container (PURO HomePage). Wrap.card-accent(rail via inline--accent). Header: brand=.app-gradient-brand text-white· non-brand=tintedbg-{x}-50bar. gotcha 66 APPLIED: gradient<h3>title =text-white!(bang) — plain text-white thua unlayered h1-h4 rule. Props: title/icon/accent/stats[]/onExpand/onRefresh/children/empty/emptyText.stats[]= clickable StatChip row (mỗi chip a11y button khi có onClick). empty → muted icon-chip + emptyText. Header IconButton (RefreshCw/Maximize2) contrast-adapt gradient↔tinted, aria-label.- 3 đều: NAMED export ·
import type(verbatimModuleSyntax) ·cnfrom@/lib/cn· lucide-react · accent palettes stop 50/100/500/600/700 ONLY (no -800) → head/value -700 (brand -800 OK) · icon-chip recolor['--chip-bg' as string]/['--chip-fg' as string]inline (pattern từ HRM Card). NO new npm dep. Buildtsc -b && vitePASS 0 TS, 24.87s (warning @import-order + chunk-size = pre-existing). fe-admin NOT mirrored (separate pass nếu cần). FD2 authed-screenshot SKIP (components chưa wired vào page nào — pure library; visual verify khi page tiêu thụ chúng).
S69 (2026-06-17) OfficeDashboardPage.tsx — E-Office landing
fe-user/src/pages/office/OfficeDashboardPage.tsx— E-Office landing dashboard (S69, 2026-06-17) — PURO HomePage, COMPOSES the 3 shared ui widgets. build-PASS 0 TS, 434ms. Layout:ui/PageHeader(eyebrow "Văn phòng số" / title "Bảng điều khiển" / icon LayoutDashboard / accent brand) on top →grid grid-cols-1 lg:grid-cols-3 gap-5: LEFTlg:col-span-2= stack of 4WidgetCard(Đề xuất brand / Đơn từ teal / Ticket CNTT violet / Phòng họp hôm nay amberx — each body = row of 3KpiCardfilter-chips except Phòng họp = 1 KpiCard + next-4 booking peek list) · RIGHTlg:col-span-1= "Công việc của tôi" WidgetCard (brand-50 hero countmyTodo+ 3 clickableMetricRow) + "Thao tác nhanh".card-accentpanel (3 Button primary/secondary/outline). Stacks 1-col <lg. Hooks REUSED (verbatim queryKey+endpoint = shared TanStack cache, NO new API, NO new BE): proposals['proposals',{…}]→GET /proposals(+ separateinboxOnly:truequery for "Cần duyệt" = needs-my-action signal) · đơn-từ['/leave-requests'|'/ot-requests'|'/travel-requests',{page:1}]→those 3 endpoints, merged+countByStatusclient-side · tickets['it-tickets']→GET /it-tickets· meetings['meeting-bookings',{…}]→GET /meeting-bookingswindowed to today (local-midnight→+1d ISO). Counts ALL client-side (countByStatusreducer; status enums from@/types/proposal+@/types/workflowApps). States graceful per-widget: isError→WidgetError(retry btn, accent-500 chip) · isLoading→WidgetSkeleton(3 pulse bars,motion-reduce:animate-none) · empty→WidgetCardemptyprop. NEVER blocks page. Routing question: only/proposals/newexists as real create route → quick-actions "Tạo đề xuất"→/proposals/new, "Tạo đơn"→/workflow-apps/leave(đơn-từ landing, NO standalone /new), "Tạo ticket"→/it-tickets(ticket landing) — every link hits an EXISTING route (no*fallback dead-link). onExpand per widget → its real route. a11y: KpiCard/MetricRow clickable = role=button+Enter/Space+focus-ring; reduced-motion honored. Routing/menu wiring NOT done (next agent's job — page NOT imported in App.tsx yet). fe-admin NOT mirrored. FD2 authed-screenshot SKIP (ProtectedRoute + gotcha #3 rig blocks authed; visual verify via deploy).