Fibretrace Monet docs/Walkthroughs and notes/Persona case matrix

Lovable dashboard — exhaustive persona × case matrix (A-Z 100%)

Mục tiêu: liệt kê TẤT CẢ các case (persona × route × state × điều kiện kích hoạt) của app Lovable fibre-trace-studio, để hiểu hết mọi màn hình mà không cần bấm tay qua browser. Phương pháp: code-first fan-out — 7 Explore agent đọc toàn bộ source (read-only) và enumerate mọi nhánh render. Code là ground-truth: một case chỉ chạy khi seed data đúng điều kiện vẫn được liệt kê ở đây (browser thì sẽ bỏ sót). Nguồn: clone fibre-trace-studio-8b5c03d8 (origin/main), read-only. Ngày dựng: 2026-07-16. Feeds task T74. Mọi dòng đều có file:line — đây là bằng chứng, không phải diễn giải.


PART A — Access / persona model (backbone)

A1. Persona được suy ra từ dữ liệu, KHÔNG có field role cứng

Một company map sang persona 100% từ currentUser.company_id + tín hiệu dữ liệu. Hai hook song song:

usePersona (src/hooks/usePersona.ts) → "producer" | "brand" | "scanner":

Flag Rule file:line
isProducer company_modules(module_name="sdu").enabled OR có ≥1 programme_producers row usePersona.ts:34 (query :28-31)
isBrand !isProducer && (có production_claims OR programme_reservations) usePersona.ts:38
isScanner !isProducer && !isBrand (fallback) usePersona.ts:39
primary thứ tự producer → brand → scanner, mặc định “scanner” usePersona.ts:47

Lưu ý (usePersona.ts:35-37): chỉ tham gia programme (Tier1 được thêm vào để scan/share) không đủ thành “brand” — phải có claim/reservation thật; còn lại rơi về scanner.

usePersonaContext (src/hooks/usePersonaContext.ts) → workflow PO/linking mới:

Flag Rule file:line
isProducer có ≥1 programme_producers row usePersonaContext.ts:36
isRetailer !isProducer && company_type ∈ {brand,retailer} && có programme_participants usePersonaContext.ts:37-39
hasIncomingPoShares purchase_orders với partner_company_id=companyId AND archived=false usePersonaContext.ts:32,40
isManufacturer = hasIncomingPoShares (cùng tín hiệu, alias) usePersonaContext.ts:41

A2. Demo customer types (nút “Switch Customer Type”)

CUSTOMER_TYPES (DemoNavBar.tsx:15-23) — 7 persona demo:

Label Company userId
Fibre Producer GIN (Tier4) ACME Cotton GIN ee5a149d-…c83d2b
Fibre Producer rPET (Tier4) ACME rPET Recyclers f1e7bfde-…d6049d6
Apparel Manufacturer (Tier1) ACME Apparel Tier1 f1f1f1f1-…111111
Retail Brand (Tier0) ACME Retail Global Inc e5f6a7b8-…901234 (= DEFAULT_USER_ID)
Program Owner ACME Program Office 22222222-…222222
Retail Brand v2 (Empty State) Retail Brand - Empty State a1a1a1a1-…000002
Manufacturer v2 (Empty State) Apparel Manufacturer - Empty State b1b1b1b1-…000002

Cơ chế switchUser() (DemoNavBar.tsx:37-52): query company_users theo userId → setCurrentUser({...user, company_name}) → ghi localStorage["fibretrace_current_user_id"]window.location.href="/" (full reload). Đây là swap context client-side thuần trên demo Supabase backend: KHÔNG login lại, KHÔNG đổi token — chỉ đổi currentUser/company_id rồi reload để mọi query chạy lại.

“Tier” chỉ là chữ trong label; tier thật lấy từ company_details.supply_chain_tier lúc runtime (AppSidebar.tsx:94-106).

Empty-state personas (v2): KHÔNG phải component riêng — chỉ là company seed không có facility/session/programme, nên mọi query trả []/0 và mỗi widget render nhánh empty bình thường của nó.

A3. Demo actions (panel “Demo Controls”)

Control Hành động file:line
Website Directory mở <DirectorySearchModal> DemoNavBar.tsx:127-131
SDU Automation mở <DemoSDUModal> DemoNavBar.tsx:132-137
EWR Automation navigate("/fibre-creation?ewr=1") (mở DemoEWRModal) DemoNavBar.tsx:139-143
Scanner Demo mở <DemoScanModal> DemoNavBar.tsx:144-149
Scanner Demo v2 mở <DemoScanModalV2> DemoNavBar.tsx:150-155
Admin navigate("/admin") DemoNavBar.tsx:156-161
Developer Tool Tips toggle DevToolTipsContext DemoNavBar.tsx:90-101
Show as Wireframe toggle WireframeContext (thêm class wf-on) DemoNavBar.tsx:108-119

A4. Route access tiers (App.tsx)

  • Public (không layout/auth): /login, /register-interest (/register→redirect), /directory, /v/:token, /c/:token, /verify/:guid, /verifications/preview/:sessionId(/scan/:auditId), /verifications/webhook/:sessionId, /collections/create, /facilities/add, /team/invite, *(404). (App.tsx:124-136,214)
  • Redirects: /register/register-interest; /allocations, /allocations/new, /report/:claimId/programs. (App.tsx:134-140)
  • <AppLayout> (App.tsx:141-186): toàn bộ app end-user. Guard AppLayout.tsx:22-24: if (loginRequired && !userAuthed) → Navigate /login.
  • <AdminLayout> (App.tsx:187-213): /admin/* (24 route). Guard AdminLayout.tsx:59-61: if (adminLoginRequired && !adminAuthed) → /admin/login. Mặc định adminLoginRequired=false → /admin mở cho bất kỳ ai (xem Part C).

A5. Nav-gating matrix (AppSidebar.tsx) — persona nào thấy mục nào

Nav item Điều kiện hiển thị file:line
Verification History verificationsMenuEnabled (module, default ON) :138-139
Production Records / SDU status fibreCreationEnabled (có producer programme) hoặc sduEnabled :111-113,418-481
Owner nav (Program record/uptake/Documents/Certificates) programOwnerEnabled (module, default OFF) :483-495
Fibre Programs !isTier4 (tier ≠ “Tier 4”) :106,272-274
Claim Position / Evidence Packs / Request Evidence !persona.isRetailer :148,275-281
Evidence section wrapper hasParticipantProgrammes || showRetailerNav :498
Purchase Orders / Ready to Claim / Claims showRetailerNav = persona.isRetailer :148,283-287,508
Supply Chain (dưới PO) participantProgrammes.length > 0 :514-516
Data Connections !isExclusiveProgramOwner :136-137,521,528
Link to PO persona.isManufacturer || persona.hasIncomingPoShares :149,533-542
Scanners (settings) scannersMenuEnabled (module, default ON) :130
API keys (settings) company_api_access.enabled :117-128
Billing (settings) useBillingVisible() (site-wide flag AND company module) :126,129

PART B — Full case matrix by domain

Cột chung: State/Case = tình huống render; Trigger = điều kiện code (file:line); Renders = hiển thị gì.

B0. Home (/, Index → Home.tsx) — điểm rẽ persona

Persona Case Trigger Renders
role auditor Lockout Home.tsx:60 currentUser.role === "auditor" force sign-out → /login
Program Owner co. Redirect Home.tsx:82 isProgramOwner /owner
Producer co. Branch Home.tsx:337 isProducer <ProducerDashboard>
Manufacturer-only Branch Home.tsx:343 isManufacturer && !isRetailer <ManufacturerDashboard>
Brand/Retailer (Tier0) Pipeline scanner vs brand Home.tsx:348 isScannerPersona ? 4-step : 3-step pipeline cards
Tier0 Onboarding widget Home.tsx:365 onboardingEnabled && tasks>0 checklist / null
Tier0 Pipeline vẫn hiện tới khi onboarding xong Home.tsx:534 pipeline / ẩn
Tier0 Heatmap vs fallback Home.tsx:609 heatmapProgrammes>0 world heatmap / Recent Verifications
Tier0 Thứ tự widget (drag) Home.tsx:309-311 localStorage layout 2 cột
Tier0 KPI khi chưa có programme Tier0HomeWidgets.tsx:74 !current mọi KPI = 0 / “Cap pending”
Tier0 Per-KPI chip Tier0HomeWidgets.tsx:97-139 ceiling>0 ? … : "Cap pending" tone/label chip
Tier0 ActiveProgrammesGrid empty Tier0HomeWidgets.tsx:296 !active.length ẩn widget
Tier0 EvidenceReadiness blockers Tier0HomeWidgets.tsx:458-463 red tile + bullet blockers
Tier0 RecentActivity empty Tier0HomeWidgets.tsx:742 items.length===0 “No recent activity yet.”

B1. Producer (Tier4 GIN + rPET)

/programs + /programs/:id (Programs → ProgrammeDetail)

Case Trigger Renders
Loading / Empty Programs.tsx:91 / :93 ordered.length===0 “Loading programs…” / “No programs available.”
Single vs multiple (carousel) Programs.tsx:97 ordered.length>1 carousel bar prev/next / ẩn
Current archived Programs.tsx:111 status==="expired" badge “Archived”
Có archived Programs.tsx:105 archivedCount>0 chip “N archived”
Ordering Programs.tsx:25-27 active trước, expired sau
Detail loading / not-found ProgrammeDetail.tsx:179 / :180 “Loading…” / “Program not found”
status active vs other ProgrammeDetail.tsx:243/246 badge “Active” / raw status
Expiring ≤90d ProgrammeDetail.tsx:183 badge “Expires soon”
cardLayout default/bar/gauge ProgrammeDetail.tsx:297/307/333 stat rows / bars / radial gauge
canReserve gate ProgrammeDetail.tsx:187,283 remaining_capacity_mt>0 “Add reservation” enabled/disabled
canClaim gate ProgrammeDetail.tsx:188,286 available_to_claim_mt>0 “Make claim” enabled/disabled
Add-reservation modal + validate/success/error ProgrammeDetail.tsx:359,192,143-145 dialog + mailto sales; toast error/success
Make-claim modal: eligible sessions loading/empty/populated ProgrammeDetail.tsx:401/403/407-429 picker checkbox own/shared badge
Claim validate: no amount / no evidence ProgrammeDetail.tsx:216/220 toast error
Claim rollback / success ProgrammeDetail.tsx:163-166 / :174 xoá claim khi link fail / toast created
Back button chỉ khi standalone ProgrammeDetail.tsx:229 !embedId link “Back to Fibre Programs”

/fibre-creation (FibreCreation) — điểm áp pigment / log production của gin

Case Trigger Renders
hasSdu banner FibreCreation.tsx:1107 promo QR “Start producing”
No programmes / no active :1142 / :1180 empty states
Active/Expired tabs (expired ẩn nếu 0) :1149-1219 tabs
Single vs multi active (carousel) :1182 / :1158 card / prev-next
cardLayout default/bar/gauge :913/958/1002 3 layout
“Log Production Manually” chỉ khi facility có manual_logging_enabled :931,970,1032 nút hiện/ẩn
isArchived dimming :922,961,1004 border mờ
Production tab empty/populated + pager :607 / :820 “No production records yet.” / table
Verifications tab empty/populated :834 / :838-895 “No activation verifications yet.” / rows
Per-record status derivation :649-655 verified / more scans needed / awaiting verification
Record excluded :754 r.excluded badge “excluded” + row mờ
Shortcode inline edit + errors (empty/<3/duplicate) :663,467,468,477 input / inline error
Source badge manual/sdu_demo/ewr_demo :730-737 “Manual”/“SDU Demo”/“EWR eCotton”
Expandable linked sessions :786 session sub-rows
Order Pigment dialog (submit disabled tới khi đủ field) :1224,1310 modal form
Log Production dialog (facility filtered; file upload “coming soon”) :1320,1348,1381 modal
production/pigment submit success/error :452,458,426,431 toast
EWR demo auto-open (?ewr=1) :167 DemoEWRModal
Link verification modal :1404 modal

/sdu (SDU list + detail + FibreProductionTab)

Case Trigger Renders
Detail vs list SDU.tsx:191 detailId && detail detail (Device/Fibre tabs) / list
List loading / empty(no SDU) / empty(filtered) / populated+pager :343/345/348/394 3 empty variants / table
Status chip (feeding/ended/idle/starting/paused/blocked/fault/inactive) :32-52 badge màu
Demo SDU modal :323-328 modal
Fibre tab: loading/empty :970/972 “Loading…” / “No bales match…”
Bale verified vs not :1034-1039 “Verified” / “Not scanned”
Weight inline edit + invalid :1043,753 input / toast “Enter a valid weight”
Bulk action bar :937 selected.size>0 Show/Hide/Add-to-Verification/Clear
Add-to-verification modal (empty note) :1117,1130 picker / “No verifications found”
Link error: no scans :697-700 toast “verify them first”
CSV import errors (no facility / no programme / parse) :797,801,809-820 toast error
CSV import success :839 toast “Imported N bale…”
Export nothing selected :715 toast “Nothing to export”
kg/lb toggle; pageSize “All” :889-895,640,1103 recompute; ẩn pager

B2. Program Owner

/owner (OwnerDashboard)

Case Trigger Renders
Loading / no owned programmes OwnerDashboard.tsx:82 / :84 “Loading…” / “does not own any programs”
Admin banners :131 (AdminBanner null nếu 0) banner dismissible / nothing
Programme filter (all/one) :74-77,150-158 KPI scope
Cert count active+valid :116-118 KPI number
“Activated” tone: có production vs Pending :226 icon tone khác
Logo present/absent :237 img / Package icon
Producers/Participants table empty :289 / :313 “No producers assigned.” / “No participants yet.”
BUG: “View details” link → /owner/programme-uptake nhưng route là /owner/program-uptake :254 vs App.tsx:170 link 404 (xem Part C)

/owner/documents — CRUD tài liệu programme: loading/empty (:195/196), Add vs Edit dialog (:136-181), status draft/published toggle (:204-207), no owned programmes note (:156), title required (:60), save success/error (:94/98), delete confirm() (:105), existing-file label (:172-174).

/owner/certificates — loading/empty (:189/190); status revoked/expired/active (:200-202); Revoke chỉ khi active (:205) + prompt() reason (:105); Add/Edit dialog (:136-175, upload đã bỏ); validate type/valid_from (:65); delete confirm() (:112).

/owner/program-record — loading / no programmes (:146/147); programme selector (:183-190); logo present/absent + upload/replace/remove (:219-238); description conditional (:253); FibreTrace items empty + chỉ toggle/reorder, không edit/delete (:270,262-292); Program items empty / row editing vs view (:307,384); move up/down disabled ở biên (:282-283,319-320); title required (:359); delete confirm() (:372); wording sửa ở /admin/programs, không ở đây (:327-328).

/owner/program-uptake — loading / no programmes (:232/233); quarterly chart empty production vs claims (:442); chart filter producer/participant (:208,215); confidence pill null vs 3-tier (>80/>50/else) (:28-32); participants/producers table empty (:331/381); producer không có facility → row dấu “—” (:158-174); ClaimedCell no-claims vs popover (:478-506).

B3. Brand / Retailer (Tier0)

/claim-position(/:id) (ClaimPosition) — KPI derivation (:101-107): activated = total_produced_mt (toàn programme), reserved=my_reserved, claimed=my_claimed, available=available_to_claim, pending=max(0,activated-claimed-available), pct=v/ceiling.

Case Trigger Renders
Loading / no programs ClaimPosition.tsx:79 / :82 “Loading…” / empty
7 KPI tiles :158-199 Reserved/Activated/Claimed/Available/Pending/Ceiling/Expiry
Capacity flow bar 6 stage :169-176 arrow chain + segmented % bar
Remaining ceiling segment :360 remaining>0 grey bar / ẩn
Wording panels :416 Supported / Not-yet cards / ẩn

/claim-position/list — loading/empty (:120/122), active-first ordering (:29-31).

/claims (Claims) — no-reservation gate: “Make a claim” disabled + tooltip (:311,319-326); view mode active/list (:69); deep-link ?claim= (:73-77); loading/empty (:492/497); featured card (:397); filter loại proposed/rejected (:99-100); pack preview/share nav (:449,566,582); MakeClaimModal (:65,310). MakeClaimModal: validate no-programme/qty≤0/no-sessions (:123-127), no eligible sessions (:220-222), success/error toast (:153-160).

/purchase-orders — status enum draft/shared(“Awaiting link”)/linked/ready/partially_claimed/fully_claimed (:20-36); loading (:257); empty vs no-match (:260); delete guard nếu đã claim (:139); edit lock nếu có pack (:304); archive toggle (:152-159); Create/Import-CSV modals (:341-342); coming-soon ERP banner (:326).

/ready-to-claim — eligibility filter (:42); loading/empty (:189-192); trạng thái proposed → Review button + ReviewClaimProposalModal (:223-237); confirmed packs (:239-251); rejected (:253-260); default “Awaiting partner” (:262); modal chỉ mở khi proposed (:275). ReviewClaimProposalModal: confirm/reject toast (:79-101), reject cần reason (:88-89), confirm disabled nếu 0 session (:210).

Evidence packs — Index loading/not-found (:21-26), pack status ready/pending (:29,86); Detail legacy redirect (:34), unknown pack (:44), export/share CTAs (:170-217), PDF fail (:109-112), no documents info (:210).

Evidence requests — sent/received tabs + counts (:126-133); status pending/responded/cancelled (:46-48); empty per tab (:57-67); new-request gate no-reservation (:28); New: no partners (:52) / no reservation (:71); Detail requester vs recipient (:32-33), responded banner → LogProductionModal (:101), no sessions to share (:158).

/connections — Claims tab chỉ khi isParticipant (:495); 3 tabs sessions/groups/claims (:87-93); highlight deep-link (:94-101); public-link on/off (:686-707); webhook on/off (:710-728); empty claims (:834); claim row expand → per-pack share (:863); type switches CSV/JSON/PDF/public/webhook toast (:1057-1063); coming-soon vendors (:1082-1109).

/programs/:programmeId/supply-chain (RetailerSupplyChain) — loading “Assembling the chain…” (:155); no anchors (:157); no shared verifications banner (:164); per-tier placeholders Tier1/2/3 (:183-213); identity hidden nếu không phải partner → Lock icon (:45); connector labels giữa tier (Shipment joined / No active production / Tier Activated / Awaiting scans) (:262-273).

B4. Manufacturer (Tier1/Tier2) + shared verification/collection/facility/scanner

/link-to-po (LinkToPo) — 3 nhóm chính:

  • Awaiting evidence pack: loading/empty/populated (:130/132/70-75); sub-case rejected → “Rejected — revise” + reason (:218,282-289); canPublishMore vs “Fully claimed” (:217,276).
  • Awaiting brand confirmation: empty/populated (:146/77-82) → “Awaiting brand” clock badge.
  • Confirmed packs: empty/populated (:160/84-87) → “View pack” links.
  • LinkVerificationsToPoModal: fully-claimed guard (:37,121); step1 loading/empty/no-match/populated (:132-138); step 1/2/3 (:106-108); validate toasts (:86-89); publish success (:99).
  • PO status enum (usePurchaseOrders.ts:5-11); confirm→pack / reject→PO về “shared” (:453,469,563,581).
  • Invite-partners callout luôn hiện (:117).

/verifications (Verifications) — session detail drill-down (:130); tabs mine/shared-with-me/shared-by-me + counts (:141-144,177-181); loading/empty (:397/399); “Needs shipping link” filter chip (:301); row badges: needs_shipping_link (:529), is_locked (:545), linked-to-production (:505), shared (:524); unshared-only filter disabled ngoài tab mine (:344); share blocked (production/claim) → toast (:227-234) vs allowed → ShareSessionModal (:236); share disabled reason tooltip (:492-494); blockchain sync mock badge (:666); column visibility localStorage (:74-80); pager >1 (:742). ShareSessionModal: no partners → CTA /settings/partners (:187); already shared card (:160); duplicate/success toast (:103-107).

/verifications/preview/:sessionId (VerificationPreview) — loading/not-found (:177/185); claim claimed vs unclaimed (:278-297); scan rows empty/list (:356); row clickable real vs synthetic (:371); pager (:400); copy public link (:44-51). /…/scan/:auditId (ScanPreview) — loading/not-found (:62/70); status label pass→“Verified” (:111,125).

Collections — detail drill-down (:115); loading/empty (:189/191); status open/active vs other (:243); GUID vs “—” (:221). CollectionPreview: loading/not-found (:80/88); sessions empty/list (:156).

Facilities — detail vs list (:195); empty (:373); row dimmed inactive (:396); add blocked no-license → toast (:314-316); license buy dialog select/processing/done (:495,520,526); delete confirm dialog (:539); status active/inactive toast (:146-159). CreateFacility: no-license block (:50-53).

Scanners — loading/empty/no-match (:319/320/394); assigned vs “Pending assignment” (:177,401-405); Demo Scan: no facility toast (:110-113) / single auto (:114-115) / multi picker (:117-119); Buy/Modify/Support dialogs (mailto, backend pending) (:489,532,552); Modify send disabled tới khi chọn action (:582).

B5. Settings / Team / Notifications / Help

/settings/* (SettingsLayout: index → account). Sidebar: Billing ẩn nếu !billingVisible (:129); API keys ẩn nếu !apiEnabled (:128).

  • account: field view/edit ternary (:141,101); copy blockchain key (:231); password mismatch (:80) / mock update (:81); update-password disabled (:262); guides toggle (:273-277); save fail toast (:64/68/72).
  • company: role gate isAdmin = role∈{admin,owner} (:21) → non-admin read-only, no Edit (:84,101); required invalid (:78,98); company_type select vs text (:131); supply_chain_tier required (:158-177); hide-from-directory toggle admin-only (:184-188); tax_id_type required (:206-217); confidence card chỉ khi companyId (:230).
  • notifications: empty/populated templates (:57); per-row enabled fallback default (:44-47).
  • partners: connected loading/empty(no-partner)/empty(no-match)/populated (:130,133,135-163); pending empty/incoming(Accept+Decline)/outgoing(Cancel) (:169,174-205,206-229); search <2 chars ẩn (:251); search no-match/results (:253); pager >1 (:101); invite non-FT company mailto (:289-293); invite duplicate toast (usePartners.ts:107); accept adds notification (:118-127); disconnect notification chỉ nếu đã accepted (:145-151).
  • api-keys: access disabled → “contact administrator” (:89); 0 keys / có keys (:123,126-154); mask/visible toggle (:132); max 5 keys disabled+throw (:106,50); create dialog (:175-191); Overview vs docs tab (:112).
  • billing: role gate edit (:31,128); field editing (:119); invoice status “Pay”→Stripe link vs static (:229); status chip color (:20-24); view/download PDF missing → info toast (:142,146-154); empty (:264); pager >1 (:271); sort toggle (:103-106); Manage licenses → /facilities (:183).
  • store: placeholder “Store coming soon.” (:12-14).
  • Settings.tsx (legacy): KHÔNG mounted trong router — dead code (save error/success/saving states only).

/team — detail vs list (:120); loading (:335); active/pending tabs empty (:339); role filter all/owner/admin/auditor + clear (:216-217,324); row badge active(role-color) vs “Pending” (:378,114-117); “You” tag (:366); pager >1 (:399); edit-role dialog (Admin/Auditor only) (:167-187); remove confirm dialog (:190-203); 3-dot menu (:141,144); column visibility (:63,226-233); CSV export (:235-250). /team/invite: role Admin/Auditor + permission preview (:101-108,24-39); validate no-email (:47-50); submit disabled (:130); insert error (:60-63); success → back (:64-65).

/notifications — search no-match/populated (:46,49-71); unread styling (:57,59,64); mark-all-read disabled (:36); click → markRead + navigate (:53-56); sources merged/deduped (Context:143-153); seeded mock rows luôn có (Context:17-24); realtime insert (Context:92-99); realtime partner-invite (Context:136-139); icon per category (Context:51-57).

/help — audience filter theo persona: producer (:23) vs participant (:25); draft ẩn trừ khi search (:34); loading (:70); no-result có/không query (:73-74); grouped sections (:77-108); draft badge (:97). /help/:slug: loading/not-found/found (:17,19,26); draft badge (:32); audience label (:31); optional summary (:35).

B6. Public / Auth / Demo

Route Case Trigger Renders
/login default (loginRequired=false) Login.tsx:38 DEFAULT_USER_ID email/blank → login demo user
/login loginRequired ON: thiếu field / email not found / fail :31-33 / :47-48 / :69-70 toast error
/register form vs submitted Register.tsx:92 / :82 form mailto sales / “Enquiry sent”
/register-interest canSubmit false / submitted :257,33-34 / :78 disabled / “Thanks…”
/directory empty / results Directory.tsx:138 / :143 “No organizations…” / cards
/v/:token loading / not-found / disabled / claimed / unclaimed PublicVerification.tsx:79/43/44/108-121/117-122 spinner / “not found” / “not publicly visible” / green banner / neutral
/c/:token not-found / disabled / pack variants PublicClaim.tsx:44/45/8-12 “Claim not found” / “not visible” / icon per trade/sustainability/procurement
/verifications/webhook/:id loading / not-found / result derivation WebhookPreview.tsx:77/85/35-41 JSON page (pending/pass/fail/review)
Scanner Demo multi-facility chưa chọn / gin auto-outbound / 0 pigment / no facility DemoScanModal.tsx:446/148/585/215 disabled + hint / skip to ftid / red error / toast
Scanner Demo v2 GPS detecting / mismatch / 0 programme / 0 partner / 0 verification DemoScanModalV2.tsx:360/380-384/1275/969/921 spinner / amber banner / error text / empty card
Scanner Demo v2 inbound no-match shipment :1090,1130 “No matching shipment”
SDU Automation 0 active SDU DemoSDUModal.tsx:233 “No active SDUs found”
/placeholder stub Placeholder.tsx:9-10 “coming soon”
* 404 NotFound.tsx:7-9 404 card

B7. Admin console (/admin/*)

Auth: AdminLayout.tsx:59 gate; adminLoginRequired default false/admin mở cho bất kỳ ai. Nếu bật: check email/password vs adminAccounts (localStorage plaintext, seed support@fibretrace.io/hZ8nD7vL6nO7, AuthSettingsContext.tsx:36-39,122-125); adminAuthed = sessionStorage (per-tab). Không có enforcement server-side/RLS.

Pages có logic riêng:

  • AdminPigmentIds: TAU-range overlap guard, Save cần confirm checkbox khi overlap (:46-58,126,157-172).
  • AdminFibreProduction: tabs pending/ready/verified/rejected/all (:135-141); review dialog Reject / Save&approve chỉ khi pending (:150,294-311); Reopen cho rejected (:220-224).
  • AdminLicensing: rollup license_count vs used (chỉ !is_free && active && !deleted) (:51-59); bar đỏ khi over (:144-158).
  • AdminSDU: status enum; facility filtered theo company + block nếu 0 facility (:122,167-169); soft-delete dim (:214,223).
  • AdminOnboarding: filter all vs company + status default active_pending (:74-78); “Seed defaults” chỉ khi chọn company (:234-238); global widget toggle riêng (:145-169).
  • AdminCms/Detail: CSV export (:113-128) + import chỉ update key tồn tại (:167-171); PAGE_SIZE=50; “Revert to default” (default_value).
  • AdminCompanies: per-company module toggles (billing/verified/verifications/scanners default-ON khi no row :92-93/program owner); billing switch disabled nếu !billingSiteWide (:397-398); confidence manual override (:188-191,111-114).
  • AdminFacilities: effectiveStatus virtual “expired” từ expires_at (:15-16); expired đỏ (:365); is_free Free/Paid; soft-delete; license editor embedded.
  • AdminVerifications: 4 tabs collection/verification/session/audit + counts; showDeleted toggle (:392); soft-delete/restore; Edit chỉ safe fields (:311-326).
  • AdminProgrammes (lớn nhất): status active/hidden/expired/deleted (list loại deleted :123); 4 dialog per programme (edit / Producers manual_logging / Participants reserved+claimed / Manufacturers) + Provides editor (:1201+).
  • AdminCronJobs: 2 job demo (recalc-confidence skip manual-override :48; OS-Hub stub console.log :148-149, không persist).

Standard CRUD (loading/empty/populated + add/edit/delete): AdminProgramOwners, AdminUsers (+impersonate), AdminScanners (+assign), AdminNotifications (templates + API-access switches), AdminBanners (audience all vs companies + active window), AdminHelpPages/Detail (draft/published + markdown preview), AdminBilling (invoices + site-wide switch), AdminEvidencePacks (void-with-confirm, no create), AdminOpenSupplyHub (read-only + fake Sync 900ms), AdminSettings (toggles).


PART C — Notable findings / bugs / gaps

  1. Persona không có role field cứng — hoàn toàn suy ra từ dữ liệu company (producer rows, claims/reservations, participant rows, company_type, incoming PO). Đây là lý do “switch customer type” hoạt động: chỉ cần trỏ sang company_id khác là toàn bộ UI đổi.
  2. switchUser = client-side context swap thuần, không auth. Không tái hiện được trong monet apps/dashboard (userId demo là seed Supabase; monet route qua Fibretrace REST).
  3. BUG route Owner: OwnerDashboard.tsx:254 link /owner/programme-uptake nhưng route đăng ký /owner/program-uptake (App.tsx:170) → 404. Đáng report cho Lovable/BE.
  4. /admin mở mặc định (adminLoginRequired=false), gate chỉ client-side, account plaintext trong localStorage — chỉ hợp lệ cho demo, KHÔNG được mang lên production.
  5. Dead/unreachable branches: DemoScanModalV2 banner “warning”/“No scanner connection” định nghĩa nhưng không bao giờ được truyền (:701,718-721); Settings.tsx legacy không mount trong router.
  6. 7 error bottom-sheet của SAT KHÔNG tồn tại trong dashboard dưới các tên đó (subscription expired / wrong company / GPS mismatch / lost connection…). Grep repo không match; gần nhất chỉ là facility_licenses.subscription_status filter + programme “expired” + GPS-mismatch riêng của Scanner v2. Đây là khác biệt SAT vs Dashboard cần nhớ.
  7. Empty-state personas (v2) không phải feature riêng — chỉ là company seed rỗng data, dùng để demo mọi empty branch cùng lúc.
  8. Nhiều “backend pending” / mock: Scanners Buy/Modify (mailto), OS-Hub sync (fake 900ms + console.log), password update (mock toast), webhook (JSON page, không phải webhook thật). Khớp với chiến lược adapter của monet (READ unmapped → [], WRITE unmapped → toast “backend pending”).

PART D — Cách tái hiện / test

  • Trên Lovable live: bấm nút “Demo” → “Switch Customer Type” (đổi persona), + Demo Actions (SDU/EWR/Scanner/Admin), + toggle Wireframe/Dev Tips. Đây là quan sát — dùng ma trận này làm checklist để không bỏ sót branch.
  • Trong monet apps/dashboard: switcher này KHÔNG được port; persona suy ra từ tài khoản thật đăng nhập. Muốn test theo tier = login bằng account thật đúng company_type/vai của mỗi tier.
  • Coverage 100% đến từ tài liệu này (code enumeration), không từ browser.