feat(reject-history): multi-pareto 3×2 grid with cross-filter linkage
Replace single-dimension Pareto dropdown with 6 simultaneous Pareto charts (不良原因, PACKAGE, TYPE, WORKFLOW, 站點, 機台) in a responsive 3-column grid. Clicking items in one Pareto cross-filters the other 5 (exclude-self logic), and the detail table applies all dimension selections with AND logic. Backend: - Add batch-pareto endpoint (cache-only, no Oracle queries) - Add _apply_cross_filter() with exclude-self pattern - Extend view/export endpoints for multi-dimension sel_* params Frontend: - New ParetoGrid.vue wrapping 6 ParetoSection instances - Simplify ParetoSection: remove dimension dropdown, keep TOP20 toggle - Replace single-dimension state with paretoSelections reactive object - Adaptive x-axis labels (font size, rotation, hideOverlap) for compact grid - Responsive grid: 3-col desktop, 2-col tablet, 1-col mobile Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,7 @@ The API SHALL return time-series trend data for quantity and rate metrics.
|
||||
- **THEN** invalid granularity SHALL return HTTP 400
|
||||
|
||||
### Requirement: Reject History API SHALL provide reason Pareto endpoint
|
||||
The API SHALL return sorted reason distribution data with cumulative percentages.
|
||||
The API SHALL return sorted reason distribution data with cumulative percentages. The endpoint supports dimension selection via `dimension` parameter for single-dimension queries.
|
||||
|
||||
#### Scenario: Pareto response payload
|
||||
- **WHEN** `GET /api/reject-history/reason-pareto` is called
|
||||
@@ -65,6 +65,68 @@ The API SHALL return sorted reason distribution data with cumulative percentages
|
||||
- **THEN** accepted values SHALL be `reject_total` or `defect`
|
||||
- **THEN** invalid `metric_mode` SHALL return HTTP 400
|
||||
|
||||
#### Scenario: Dimension selection
|
||||
- **WHEN** `dimension` parameter is provided with a valid value (reason, package, type, workflow, workcenter, equipment)
|
||||
- **THEN** the endpoint SHALL return Pareto data for that dimension
|
||||
- **WHEN** `dimension` is not provided
|
||||
- **THEN** the endpoint SHALL default to `reason`
|
||||
|
||||
### Requirement: Reject History API SHALL provide batch Pareto endpoint with cross-filter
|
||||
The API SHALL provide a batch Pareto endpoint that returns all 6 dimension Pareto results in a single response, supporting cross-dimension filtering with exclude-self logic.
|
||||
|
||||
#### Scenario: Batch Pareto response structure
|
||||
- **WHEN** `GET /api/reject-history/batch-pareto` is called with valid `query_id`
|
||||
- **THEN** response SHALL be `{ success: true, data: { dimensions: { reason: {...}, package: {...}, type: {...}, workflow: {...}, workcenter: {...}, equipment: {...} } } }`
|
||||
- **THEN** each dimension object SHALL include `items` array with same schema as reason-pareto items (`reason`, `metric_value`, `pct`, `cumPct`, `MOVEIN_QTY`, `REJECT_TOTAL_QTY`, `DEFECT_QTY`, `count`)
|
||||
|
||||
#### Scenario: Cross-filter exclude-self logic
|
||||
- **WHEN** `sel_reason=A&sel_type=X` is provided
|
||||
- **THEN** reason Pareto SHALL be computed with type=X filter applied (but NOT reason=A filter)
|
||||
- **THEN** type Pareto SHALL be computed with reason=A filter applied (but NOT type=X filter)
|
||||
- **THEN** package/workflow/workcenter/equipment Paretos SHALL be computed with both reason=A AND type=X filters applied
|
||||
|
||||
#### Scenario: Empty selections return unfiltered Paretos
|
||||
- **WHEN** batch-pareto is called with no `sel_*` parameters
|
||||
- **THEN** all 6 dimensions SHALL return their full Pareto distribution (same as calling reason-pareto individually with no cross-filter)
|
||||
|
||||
#### Scenario: Cache-only computation
|
||||
- **WHEN** `query_id` does not exist in cache
|
||||
- **THEN** the endpoint SHALL return HTTP 400 with error message indicating cache miss
|
||||
- **THEN** the endpoint SHALL NOT fall back to Oracle query
|
||||
|
||||
#### Scenario: Supplementary and policy filters apply
|
||||
- **WHEN** batch-pareto is called with supplementary filters (packages, workcenter_groups, reason) and policy toggles
|
||||
- **THEN** all 6 dimension Paretos SHALL be computed after applying policy and supplementary filters first (before cross-filter)
|
||||
|
||||
#### Scenario: Data source is cached DataFrame only
|
||||
- **WHEN** batch-pareto computes dimension Paretos
|
||||
- **THEN** computation SHALL operate on the in-memory cached Pandas DataFrame (populated by the primary query)
|
||||
- **THEN** the endpoint SHALL NOT issue any additional Oracle database queries
|
||||
- **THEN** response time SHALL be sub-100ms since all computation is in-memory
|
||||
|
||||
#### Scenario: Display scope (TOP20) support
|
||||
- **WHEN** `pareto_display_scope=top20` is provided
|
||||
- **THEN** applicable dimensions (type, workflow, equipment) SHALL truncate results to top 20 items after sorting
|
||||
- **WHEN** `pareto_display_scope` is omitted or `all`
|
||||
- **THEN** all items SHALL be returned (subject to pareto_scope 80% filter if active)
|
||||
|
||||
### Requirement: Reject History API SHALL support multi-dimension Pareto selection in view and export
|
||||
The detail view and export endpoints SHALL accept multiple dimension selections simultaneously and apply them with AND logic.
|
||||
|
||||
#### Scenario: Multi-dimension filter on view endpoint
|
||||
- **WHEN** `GET /api/reject-history/view` is called with `sel_reason=A&sel_type=X`
|
||||
- **THEN** returned rows SHALL match reason=A AND type=X (both filters applied simultaneously)
|
||||
|
||||
#### Scenario: Multi-dimension filter on export endpoint
|
||||
- **WHEN** `GET /api/reject-history/export-cached` is called with `sel_reason=A&sel_type=X`
|
||||
- **THEN** exported CSV SHALL contain only rows matching reason=A AND type=X
|
||||
|
||||
#### Scenario: Backward compatibility with single-dimension params
|
||||
- **WHEN** `pareto_dimension` and `pareto_values` are provided (legacy format)
|
||||
- **THEN** the API SHALL still accept and apply them as before
|
||||
- **WHEN** both `sel_*` params and legacy params are provided
|
||||
- **THEN** `sel_*` params SHALL take precedence
|
||||
|
||||
### Requirement: Reject History API SHALL provide paginated detail endpoint
|
||||
The API SHALL return paginated detailed rows for the selected filter context.
|
||||
|
||||
|
||||
@@ -74,33 +74,63 @@ The page SHALL show both quantity trend and rate trend to avoid mixing unit scal
|
||||
- **THEN** rate values SHALL be displayed as percentages
|
||||
|
||||
### Requirement: Reject History page SHALL provide reason Pareto analysis
|
||||
The page SHALL provide a Pareto view for loss reasons and support downstream filtering.
|
||||
The page SHALL display 6 Pareto charts simultaneously (不良原因, PACKAGE, TYPE, WORKFLOW, 站點, 機台) in a 3-column grid layout with cross-filter linkage, replacing the single-dimension dropdown switcher.
|
||||
|
||||
#### Scenario: Multi-Pareto grid layout
|
||||
- **WHEN** Pareto data is loaded
|
||||
- **THEN** 6 Pareto charts SHALL be rendered simultaneously in a 3-column grid (3×2)
|
||||
- **THEN** each chart SHALL display one dimension: 不良原因, PACKAGE, TYPE, WORKFLOW, 站點, 機台
|
||||
- **THEN** there SHALL be no dimension dropdown selector
|
||||
|
||||
#### Scenario: Pareto rendering and ordering
|
||||
- **WHEN** Pareto data is loaded
|
||||
- **THEN** items SHALL be sorted by selected metric descending
|
||||
- **THEN** a cumulative percentage line SHALL be shown
|
||||
- **THEN** items in each Pareto SHALL be sorted by selected metric descending
|
||||
- **THEN** each Pareto SHALL show a cumulative percentage line
|
||||
|
||||
#### Scenario: Pareto 80% filter is managed in supplementary filters
|
||||
- **WHEN** the page first loads Pareto
|
||||
- **THEN** supplementary filters SHALL include "Pareto 僅顯示累計前 80%" control
|
||||
- **THEN** the control SHALL default to enabled
|
||||
- **THEN** the 80% filter SHALL apply uniformly to all 6 Pareto charts
|
||||
|
||||
#### Scenario: TYPE/WORKFLOW/機台 support display scope selector
|
||||
- **WHEN** Pareto dimension is `TYPE`, `WORKFLOW`, or `機台`
|
||||
- **THEN** the UI SHALL provide `全部顯示` and `只顯示 TOP 20` options
|
||||
- **THEN** `全部顯示` SHALL still respect the current 80% cumulative filter setting
|
||||
#### Scenario: Cross-filter linkage between Pareto charts
|
||||
- **WHEN** user clicks an item in one Pareto chart (e.g., selects reason "A")
|
||||
- **THEN** the other 5 Pareto charts SHALL recalculate with the selection applied as a filter
|
||||
- **THEN** the clicked Pareto chart itself SHALL NOT be filtered by its own selections
|
||||
- **THEN** the detail table below SHALL apply ALL selections from ALL dimensions
|
||||
|
||||
#### Scenario: Pareto click filtering supports multi-select
|
||||
- **WHEN** user clicks Pareto bars or table rows
|
||||
- **WHEN** user clicks Pareto bars or table rows in any dimension
|
||||
- **THEN** clicked items SHALL become active selected chips
|
||||
- **THEN** multiple selected items SHALL be supported at the same time
|
||||
- **THEN** detail list SHALL reload using current selected Pareto items as additional filter criteria
|
||||
- **THEN** multiple selected items SHALL be supported within each dimension
|
||||
- **THEN** multiple dimensions SHALL support simultaneous selections
|
||||
|
||||
#### Scenario: Re-click clears selected item only
|
||||
- **WHEN** user clicks an already selected Pareto item
|
||||
- **THEN** only that item SHALL be removed from selection
|
||||
- **THEN** remaining selected items SHALL stay active
|
||||
- **THEN** remaining selected items across all dimensions SHALL stay active
|
||||
- **THEN** all Pareto charts SHALL recalculate to reflect the updated selections
|
||||
|
||||
#### Scenario: Filter chips display all dimension selections
|
||||
- **WHEN** items are selected across multiple Pareto dimensions
|
||||
- **THEN** selected items SHALL be displayed as chips grouped by dimension label
|
||||
- **THEN** each chip SHALL show the dimension label and selected value (e.g., "TYPE: X")
|
||||
- **THEN** clicking a chip's remove button SHALL deselect that item and trigger recalculation
|
||||
|
||||
#### Scenario: Responsive grid layout
|
||||
- **WHEN** viewport is desktop width (>1200px)
|
||||
- **THEN** Pareto charts SHALL render in a 3-column grid
|
||||
- **WHEN** viewport is medium width (768px–1200px)
|
||||
- **THEN** Pareto charts SHALL render in a 2-column grid
|
||||
- **WHEN** viewport is below 768px
|
||||
- **THEN** Pareto charts SHALL stack in a single column
|
||||
|
||||
#### Scenario: TOP20/ALL display scope control
|
||||
- **WHEN** Pareto grid is displayed
|
||||
- **THEN** supplementary filters SHALL include a global "只顯示 TOP 20" toggle
|
||||
- **THEN** when enabled, applicable dimensions (TYPE, WORKFLOW, 機台) SHALL truncate to top 20 items
|
||||
- **THEN** the toggle SHALL apply uniformly to all applicable Pareto charts (not per-chart selectors)
|
||||
- **THEN** dimensions not in the applicable set (不良原因, PACKAGE, 站點) SHALL be unaffected by this toggle
|
||||
|
||||
### Requirement: Reject History page SHALL show paginated detail rows
|
||||
The page SHALL provide a paginated detail table for investigation and traceability.
|
||||
@@ -119,7 +149,7 @@ The page SHALL allow users to export records using the exact active filters.
|
||||
|
||||
#### Scenario: Export with all active filters
|
||||
- **WHEN** user clicks "匯出 CSV"
|
||||
- **THEN** export request SHALL include current primary filters, supplementary filters, trend-date filters, metric filters, and Pareto-selected items
|
||||
- **THEN** export request SHALL include current primary filters, supplementary filters, trend-date filters, metric filters, and all Pareto-selected items from all 6 dimensions
|
||||
- **THEN** downloaded file SHALL contain exactly the same rows currently represented by the detail list filter context
|
||||
|
||||
#### Scenario: Export remains UTF-8 Excel compatible
|
||||
@@ -197,7 +227,8 @@ The page template SHALL delegate sections to focused sub-components, following t
|
||||
- **THEN** the filter panel SHALL be a separate `FilterPanel.vue` component
|
||||
- **THEN** the KPI summary cards SHALL be a separate `SummaryCards.vue` component
|
||||
- **THEN** the trend chart SHALL be a separate `TrendChart.vue` component
|
||||
- **THEN** the pareto section (chart + table) SHALL be a separate `ParetoSection.vue` component
|
||||
- **THEN** the pareto grid SHALL be a separate `ParetoGrid.vue` component containing 6 `ParetoSection.vue` instances
|
||||
- **THEN** each individual pareto chart+table SHALL be a `ParetoSection.vue` component
|
||||
- **THEN** the detail table with pagination SHALL be a separate `DetailTable.vue` component
|
||||
|
||||
#### Scenario: App.vue acts as orchestrator
|
||||
|
||||
Reference in New Issue
Block a user