Skip to content

fix(finance): comprehensive product master UX overhaul, sortable tables, and detail drawer#63

Merged
ilramdhan merged 23 commits into
mutugading:mainfrom
ilramdhan:fix/product-costing-request
Jul 8, 2026
Merged

fix(finance): comprehensive product master UX overhaul, sortable tables, and detail drawer#63
ilramdhan merged 23 commits into
mutugading:mainfrom
ilramdhan:fix/product-costing-request

Conversation

@ilramdhan

Copy link
Copy Markdown
Member

Description

Pull Request ini menghadirkan perombakan UX (User Experience) secara masif pada halaman Product Master. Pembaruan ini mencakup implementasi tabel yang dapat diurutkan secara server-side menggunakan komponen SortableHeader baru, kontrol visibilitas kolom yang tersimpan (persisted), filter multi-select, serta navigasi baris yang dapat diklik (clickable rows).

Selain itu, PR ini memperkenalkan Product Detail Drawer, sebuah laci interaktif (interactive drawer) dengan sticky header/footer dan lazy loading per-seksi, yang menggantikan kebutuhan navigasi ke halaman terpisah untuk melihat detail. Seluruh pola UI baru ini—termasuk standardisasi tinggi elemen kontrol (h-9), responsivitas icon-only di layar kecil, dan ritme tata letak (layout rhythm)—telah dikodifikasikan ke dalam dokumen design system (DESIGN.md, LAYOUT.md, RULES.md, CLAUDE.md).

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 🎨 UI/UX improvement
  • ♻️ Refactor
  • 📚 Documentation
  • 🔧 Chore (deps, config)

Module/Component Affected

  • Dashboard
  • Finance (Product Master)
  • HR / IT / CI / EXSIM
  • Components (common/)
  • Components (ui/)
  • Navigation
  • API Routes

Changes Made

1. Advanced Data Table & Filters:

  • SortableHeader: Mengimplementasikan komponen header tabel yang dapat diurutkan (mendukung server-side sorting untuk 9 kolom) lengkap dengan indikator segitiga fa-sort, area klik penuh, dan dukungan aksesibilitas (aria-sort & navigasi keyboard).
  • Column Visibility: Menambahkan komponen ColumnVisibilityMenu dan hook useColumnVisibility untuk menampilkan/menyembunyikan kolom secara dinamis (kode produk diatur agar tidak dapat disembunyikan).
  • Multi-Type Filter: Menambahkan ProductTypeMultiCombobox yang memungkinkan filter multi-select dan secara otomatis menyinkronkan data ke URL dalam format CSV.
  • Baris tabel (table rows) kini dapat diklik untuk menavigasi pengguna langsung ke detail produk (mengecualikan area kolom actions).

2. Product Detail Drawer:

  • Menggantikan halaman view konvensional dengan ProductDetailDrawer yang responsif.
  • Drawer ini memuat informasi dalam seksi-seksi berbentuk kartu (Details, Routing, Params yang dikelompokkan berdasarkan display group, dan Cost History).
  • Menerapkan arsitektur lazy queries per seksi agar pembacaan data rute dan parameter tidak memblokir render awal drawer.

3. UI Polish & Bug Fixes:

  • Menyempurnakan ritme UI: standardisasi tinggi toolbar dan filter row ke h-9, ukuran tombol yang seragam, KpiGrid dengan lebar penuh (full-width span) untuk elemen ganjil terakhir, table edge padding, serta floor responsivitas layar di batas 375px.
  • Menambahkan penyesuaian header actions menjadi icon-only pada resolusi layar di bawah sm (mobile).
  • Bug Fix: Memperbaiki state URL sync dengan memasukkan parameter sortBy dan sortOrder ke dalam defaults useUrlState (sebelumnya nilai ini hilang secara silent saat state berubah).

4. Design System Documentation:

  • Memperbarui DESIGN.md dan LAYOUT.md dengan konvensi UI terbaru (flat list page variant, h-9 parity, multi-select combobox, aturan row-click).
  • Menambahkan aturan baru ke RULES.md dan CLAUDE.md (Mistake 12: missing URL defaults, Mistake 13: stale typography), serta dokumentasi penggunaan SortableHeader.

Related Issues

Fixes #
Related to #

Screenshots

Before

After

Testing Performed

Manual Testing

  • Desktop (1440px+)
  • Tablet (768px)
  • Mobile (375px)
  • Light mode
  • Dark mode

Browser Testing

  • Chrome
  • Firefox
  • Safari
  • Edge

Build Verification

  • npm run lint passes
  • npx tsc --noEmit passes
  • npm run build succeeds

Accessibility

  • Keyboard navigation works (SortableHeader accessible via keyboard)
  • Screen reader compatible (aria-sort implemented)
  • Proper ARIA labels
  • Color contrast adequate

Performance

  • No unnecessary re-renders
  • Images optimized
  • Heavy components lazy loaded (Lazy queries per-section in Drawer)

Pre-merge Checklist

  • I have read and followed RULES.md
  • I have read and followed CONTRIBUTING.md
  • Loading states implemented (if data fetching)
  • Error handling present
  • Component props typed properly
  • Uses semantic color classes
  • Responsive design tested
  • Dark mode compatible
  • Screenshots included (for UI changes)

Reviewer Notes

  • State Synchronization (useUrlState Fix): Harap perhatikan perbaikan pada hook state parameter. Kealpaan memasukkan variabel ke dalam objek defaultValues pada useUrlState akan menyebabkan status URL terhapus otomatis saat ada interaksi lain. Aturan ini telah dibakukan di dokumen RULES.md.
  • UI Element Height Parity: Standar tinggi elemen kontrol (inputs, dropdowns, buttons) pada toolbar di atas tabel sekarang secara presisi dikunci di ukuran h-9 untuk memastikan konsistensi dan estetika setara di seluruh dashboard.
  • Row Click vs Action Clicks: Logika clickable rows sudah ditambahkan peringatan internal untuk tidak memicu navigasi drawer saat pengguna mengklik tombol di dalam sel actions.

ilramdhan and others added 23 commits July 3, 2026 20:13
Replace ad-hoc initials logic scattered across comments, nav-user, and
profile-header with a single UserAvatar component that renders the user's
actual profile photo (AvatarImage) when available and falls back to
deterministic-color initials (hash mod 6 palette) when not.

Supersedes the former UserInitials helper (removed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l dialog; hide Use Existing Costing

Replace two separate single-step dialogs (VerifyClassification and
Feasibility) with a single ClassificationAndFeasibilityDialog that
submits both steps in sequence, shows partial-failure state when
classification succeeds but feasibility fails, and retries only the
failed step.

Also hides the Use Existing Costing button in the UNDER_REVIEW action
block — the transition existed but had no useful completion path for
the user at that stage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…entity pre-claim

Add FillTaskIdentity component that displays the product name (not just
code) and the assigned filler/approver's name before they have claimed
the task. Rendered in FillTrackingCompact and FillTaskRow so users can
identify which product each fill task belongs to without memorizing
product codes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lReviewDrawer

Add is_approval_visible toggle and approval_display_order number input to
the Parameter Master form dialog so operators can configure which
parameters appear in the CPR approval drawer.

FillApprovalReviewDrawer replaces the one-click Approve button with a
drawer that shows the filtered (isApprovalVisible=true), ordered
parameter values for every product at the fill level before the approver
confirms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… guard

useUnlinkRoute.onSuccess was missing the ["finance","cost-route"]
invalidation added to useLinkExistingRoute, leaving the route's linked-
request list stale after an unlink. Add the missing key.

Also disables the Unlink button with a tooltip when the linked route is
in LOCKED status, reflecting the new backend 409 guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update UserAvatar section to document the new colorHash prop behavior
and note the removal of the former UserInitials component.
Fix stale test references to removed components.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
routing-panel: disable Unlink button and show lock tooltip when route is
in LOCKED status, preventing accidental detach of routes with active cost
results.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the reviewer selects NOT_FEASIBLE, hide the classification section
entirely and submit directly to the feasibility RPC without first calling
the classification RPC. Eliminates the confusing question "which
classification?" for a product that is not feasible.

Changes:
- handleSubmit: early-return to submitFeasibility() when isInfeasible
- canSubmit: short-circuit canSubmitClassification with isInfeasible
- classification section + separator: wrapped in {!isInfeasible && ...}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ead of count

Replace the ambiguous "N level(s)" count with "Level N, M" showing the
actual route level numbers each product sits at. Also display the product
name below the product code so users can identify products without
memorizing codes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each of these drawers had both a SheetClose/X icon button and a
"← Back to request" button doing the exact same thing. Remove the X
button — "Back to request" is more informative and less ambiguous.

FillTrackingDrawer had only an X and no Back button; replaced with a
proper "← Back" button for consistency.

Two-level drawers (param-detail, override-params, param-edit-log) are
intentionally left unchanged — their X/Back correctly closes only the
inner drawer while keeping the outer one open.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply the same displayGroup / displayOrder grouping used in the cost
breakdown modal to FillParamProductSection and FillApprovalReviewDrawer.
Params are now grouped under labelled section headers (text-xs uppercase)
sorted by the group's minimum displayOrder; ungrouped params render last
with no header.

Makes param layout consistent across fill entry, approval review, and
cost results breakdown views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cancel and Close both terminate a request to CLOSED status. The
difference (Cancel forces substatus=cancelled; Close lets the user pick
won/lost/cancelled/on_hold) was not visible to users, creating three
overlapping buttons.

Remove Cancel entirely. Users who want to close with substatus=cancelled
use the Close dialog and select that option. Reject remains separate
(terminates to REJECTED — recoverable via Revise — not CLOSED).

Also updates the action-bar test to assert Cancel is absent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…w dialog

Feasibility is the primary decision — show it first. Classification only
appears (below a separator) when FEASIBLE is selected, which already
matches the skip-classification-when-NOT_FEASIBLE behavior added in the
previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…code

Each product badge in the fill tracking card now shows the product name
in a truncated muted line below the code, matching the product summary
card behavior. productName is optional so cards without name data render
unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
param-detail-drawer (drawer 1), override-params-drawer (drawer 2), and
param-edit-log-drawer (drawer 2) each had both a SheetClose/X icon and
a Back button doing the same thing. Remove the X button; keep Back only.

Two-level behavior is preserved: Back on drawer 2 calls onClose which
unmounts only the inner drawer, leaving the outer one open.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…awer

- ParamValueEntry type gains displayGroup + displayOrder with normalizer
- ParamGroupedList component renders params grouped under section headers
  (divider line + bold uppercase label) ordered by min displayOrder per group
- Ungrouped params (empty display_group) fall to bottom
- Generated TS types updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ixes

List page:
- Split page.tsx into server component + routes-page-client.tsx (client)
- Add loading.tsx skeleton
- useUrlState for all filter/sort/pagination params (URL-synced)
- SortableHeader on product, status, version columns
- ColumnVisibilityMenu in filter bar
- DataTablePagination replacing prev/next buttons
- TableRow onClick → router.push (whole row is clickable)
- KpiGrid with 4 KpiCards matching product-master layout

Detail page:
- Add loading.tsx skeleton
- Fix locked banner: remove absolute positioning → inline amber card with Lock icon
- Replace emoji Fork button (🔱) with GitFork Lucide icon
- Replace raw <Badge> status with <StatusBadge type="route">
- Hide notes textarea behind <details> to declutter header
- Replace window.confirm with <ConfirmDialog variant="destructive">
- LinkedRequestsPopover → responsive Sheet drawer (side="right")

React Flow dark mode:
- StageNode: add dark:bg-emerald-950/40 dark:border-emerald-700 variants
- RmNode: add dark:bg-purple-950/40 / dark:bg-amber-950/40 dark variants
- text-foreground on node text to prevent invisible text in dark mode

Bug fixes:
- deleteRm: was directly mutating seq.rms then shallow-spreading seqs
  → now uses .map() for fully immutable update (fixes delete 1 → deletes all)
- deleteStage: was directly mutating base.seqs via assignment
  → now uses .filter() returning new array (fully immutable)
- handleStageClick/handleEdgeClick: added seqId===0/rmId===0 guard
  → shows toast "Save first" instead of opening panel on unsaved nodes

Cards view:
- RM chips show typed color border (green=PRODUCT, purple=GROUP, amber=ITEM)
- routeRmName shown (group name already populated by RmGroupCombobox onChange)
- ratio displayed on second line of each chip for readability

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, rm drag, dark mode, modals

List page: level/rm count columns, sort fix (head_id/version), KPI/filter polish.
Detail page: client-uuid keying for stages/RMs (fixes new-node-edit + multi-delete
on unsaved elements), RM drag position persists, RM label shows group name, dark
mode support in react-flow nodes, add-stage/add-RM dialogs restyled.
Renamed linked-requests-popover.tsx -> linked-requests-sheet.tsx (Popover -> Sheet).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d of onClick

Replace TableRow onClick + router.push with an absolutely-positioned Link
covering the product_code cell, so the row stays keyboard/middle-click/
new-tab friendly. Actions cell raised to z-10 to stay clickable above it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… doc audit

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tend)

Combines two batches whose frontend edits landed interleaved in the working
tree (product-requests-ux-batch, product-request-workflow-revamp):

product-requests-ux-batch:
- request-table.tsx: sortable columns, "Pending" badge for pending classification.
- product-requests-page-client.tsx: sort state wired to SortableHeader.
- request-form-dialog.tsx: form restructure (D1-D5 field changes).

product-request-workflow-revamp:
- RoutingResolver component + useResolveRouting() hook replace the deleted
  create-routing-wizard.tsx / pick-existing-route-dialog.tsx (link-existing /
  create-from-product / create-master-then-route, all inline).
  RoutingPanel and the review dialog's routing phase both consume it.
- transition-dialogs.tsx: ClassificationAndFeasibilityDialog gains a routing
  phase between classified/deciding, gated on resolvedHeadId.
- useSubmitAndDecide hook + BFF route for the merged Submit+StartReview action.
- cost-product-request-import-dialog.tsx + BFF export/import/template routes
  for bulk request handling (xlsx, UOM-pattern), wired into a dropdown next
  to "New request".
- PaperTubeName: fixed Paper/Plastic enum display, falls back to legacy
  cost_paper_tube_type lookup for historical rows.
- FillTrackingDrawer: product info extracted into FillTrackingDrawerRow +
  badges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…de merge

Propagate the P3 helper rewrite (submitAndDecide, submitViaApi/startReviewViaApi
API-bypass pair, useExistingCostingViaApi, findAnyProductSysId) through every
remaining test that still referenced deleted helpers or pre-merge UI text.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Drop the standalone description field from the create/edit form;
  product description now comes solely from the spec section, removing
  duplicate free-text inputs.
- Merge the description and attachments blocks into the product
  specification card in the detail panel, and always render the spec
  card (previously hidden when spec was absent) so description and
  attachments have a consistent home.
- Move shade code/name helper text to a shared FormDescription instead
  of duplicating it per field.
@ilramdhan ilramdhan added this to the Costing Release Milestone milestone Jul 8, 2026
@ilramdhan ilramdhan self-assigned this Jul 8, 2026
Copilot AI review requested due to automatic review settings July 8, 2026 04:01
@ilramdhan ilramdhan added the bug Something isn't working label Jul 8, 2026
@ilramdhan ilramdhan added enhancement New feature or request fix labels Jul 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ilramdhan
ilramdhan merged commit f6616fe into mutugading:main Jul 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request fix

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants