Files

10 KiB

Purpose

Define stable requirements for vue-vite-page-architecture.

Requirements

Requirement: Pure Vite pages SHALL be served as static HTML

The system SHALL serve in-scope pure Vite pages through backend static HTML delivery under a shell-first canonical routing policy. Direct-entry compatibility for in-scope routes SHALL be explicit and governed. Admin targets /admin/pages and /admin/performance SHALL be represented as governed shell navigation targets, while maintaining backend auth/session authority.

Scenario: In-scope canonical shell entry

  • WHEN a user navigates to an in-scope canonical shell route
  • THEN the shell SHALL render the target route via governed route contracts and static asset delivery

Scenario: Direct-entry compatibility policy for in-scope routes

  • WHEN a user opens an in-scope route through direct non-canonical entry
  • THEN the system SHALL apply explicit compatibility behavior without breaking established query semantics

Scenario: Admin targets in shell governance

  • WHEN shell navigation is rendered for an authorized admin user
  • THEN /admin/pages and /admin/performance SHALL be reachable through governed admin navigation targets

Scenario: Deferred routes excluded from this phase architecture criteria

  • WHEN this phase architecture compliance is evaluated
  • THEN /tables, /excel-query, /query-tool, and /mid-section-defect SHALL be excluded and handled in a follow-up change

Requirement: Vite config SHALL support Vue SFC and HTML entry points

The Vite build configuration SHALL support Vue Single File Components alongside existing vanilla JS entries.

Scenario: Vue plugin coexistence

  • WHEN vite build is executed
  • THEN Vue SFC (.vue files) SHALL be compiled by @vitejs/plugin-vue
  • THEN existing vanilla JS entry points SHALL continue to build without modification

Scenario: HTML entry point

  • WHEN a page uses an HTML file as its Vite entry point
  • THEN Vite SHALL process the HTML and its referenced JS/CSS into static/dist/
  • THEN the output SHALL include <page-name>.html, <page-name>.js, and <page-name>.css

Scenario: Chunk splitting

  • WHEN Vite builds the project
  • THEN Vue runtime SHALL be split into a vendor-vue chunk
  • THEN ECharts modules (including TreemapChart, BarChart, LineChart) SHALL be split into the existing vendor-echarts chunk
  • THEN chunk splitting SHALL NOT affect existing page bundles

Scenario: Migrated page entry replacement

  • WHEN a vanilla JS page is migrated to Vue 3
  • THEN its Vite entry SHALL change from JS file to HTML file (e.g., src/wip-overview/main.jssrc/wip-overview/index.html)
  • THEN the original JS entry SHALL be replaced, not kept alongside

Scenario: Hold Overview entry point

  • WHEN the hold-overview page is added
  • THEN vite.config.js input SHALL include 'hold-overview': resolve(__dirname, 'src/hold-overview/index.html')
  • THEN the build SHALL produce hold-overview.html, hold-overview.js, and hold-overview.css in static/dist/

Scenario: Hold History entry point

  • WHEN the hold-history page is added
  • THEN vite.config.js input SHALL include 'hold-history': resolve(__dirname, 'src/hold-history/index.html')
  • THEN the build SHALL produce hold-history.html, hold-history.js, and hold-history.css in static/dist/

Scenario: Shared CSS import across migrated pages

  • WHEN multiple migrated pages import a shared CSS module (e.g., wip-shared/styles.css)
  • THEN Vite SHALL bundle the shared CSS into each page's output CSS
  • THEN shared CSS SHALL NOT create a separate shared chunk that requires additional HTTP requests

Scenario: Shared composable import across module boundaries

  • WHEN a migrated page imports a composable from another shared module (e.g., hold-history imports useAutoRefresh from wip-shared/)
  • THEN the composable SHALL be bundled into the importing page's JS output
  • THEN cross-module imports SHALL NOT create unexpected shared chunks

Requirement: Pure Vite pages SHALL handle API calls without legacy MesApi

Pure Vite pages SHALL use the existing frontend/src/core/api.js module for API communication without depending on the global window.MesApi object from _base.html.

Scenario: API GET request from pure Vite page

  • WHEN a pure Vite page makes a GET API call
  • THEN the call SHALL use the apiGet function from core/api.js
  • THEN the call SHALL work without window.MesApi being present

Requirement: Pure Vite pages SHALL handle POST API calls without legacy MesApi

Pure Vite pages SHALL use the apiPost function from core/api.js for POST requests without depending on window.MesApi.

Scenario: API POST request from pure Vite page

  • WHEN a pure Vite page makes a POST API call
  • THEN the call SHALL use the apiPost function from core/api.js
  • THEN the call SHALL include Content-Type: application/json header
  • THEN the call SHALL work without window.MesApi being present

Scenario: CSRF token handling in POST requests

  • WHEN a pure Vite page calls apiPost
  • THEN apiPost SHALL attempt to read CSRF token from <meta name="csrf-token">
  • THEN if no meta tag exists, the request SHALL still proceed (non-admin APIs do not enforce CSRF)

Requirement: Pure Vite pages with server-side route validation SHALL use send_from_directory with pre-validation

Pages that require server-side parameter validation before serving SHALL validate parameters in the Flask route and then serve the static HTML.

Scenario: Hold Detail reason validation

  • WHEN user navigates to /hold-detail without a reason parameter
  • THEN Flask SHALL redirect to /wip-overview
  • WHEN user navigates to /hold-detail?reason={value}
  • THEN Flask SHALL serve the pre-built HTML file from static/dist/ via send_from_directory
  • THEN the HTML SHALL NOT pass through Jinja2 template rendering

Scenario: Frontend fallback validation

  • WHEN the pure Vite hold-detail page loads
  • THEN the page SHALL read reason from URL parameters
  • THEN if reason is empty or missing, the page SHALL redirect to /wip-overview

Requirement: Mid-section defect page SHALL separate filter state from query state

The mid-section defect page SHALL maintain separate reactive state for UI input (filters) and committed query parameters (committedFilters).

Scenario: User changes date without clicking query

  • WHEN user modifies the date range in the filter bar but does not click "查詢"
  • THEN auto-refresh, pagination, and CSV export SHALL continue using the previously committed filter values
  • THEN the new date range SHALL NOT affect any API calls until "查詢" is clicked

Scenario: User clicks query button

  • WHEN user clicks "查詢"
  • THEN the current filters state SHALL be snapshotted into committedFilters
  • THEN all subsequent API calls SHALL use the committed values

Scenario: CSV export uses committed filters

  • WHEN user clicks "匯出 CSV" after modifying filters without re-querying
  • THEN the export SHALL use the committed filter values from the last query
  • THEN the export SHALL NOT use the current UI filter values

Requirement: Mid-section defect page SHALL cancel in-flight requests on new query

The mid-section defect page SHALL use AbortController to cancel in-flight API requests when a new query is initiated.

Scenario: New query cancels previous query

  • WHEN user clicks "查詢" while a previous query is still in-flight
  • THEN the previous query's summary and detail requests SHALL be aborted
  • THEN the AbortError SHALL be handled silently (no error banner shown)

Scenario: Page navigation cancels previous detail request

  • WHEN user clicks next page while a previous page request is still in-flight
  • THEN the previous page request SHALL be aborted
  • THEN the new page request SHALL proceed independently

Scenario: Query and pagination use independent abort keys

  • WHEN a query is in-flight and user triggers pagination
  • THEN the query SHALL NOT be cancelled by the pagination request
  • THEN the pagination SHALL use a separate abort key from the query

Requirement: Reject History page SHALL be a pure Vite HTML entry

The reject-history page SHALL be built from an HTML entry and emitted as static dist assets.

Scenario: Vite entry registration

  • WHEN Vite config inputs are evaluated
  • THEN reject-history SHALL map to frontend/src/reject-history/index.html

Scenario: Build output artifacts

  • WHEN vite build completes
  • THEN output SHALL include reject-history.html, reject-history.js, and reject-history.css in static/dist/

Requirement: Reject History route SHALL serve static dist HTML

The Flask route for /reject-history SHALL serve pre-built static HTML through send_from_directory.

Scenario: Static page serving

  • WHEN user navigates to /reject-history
  • THEN Flask SHALL serve static/dist/reject-history.html when the file exists
  • THEN HTML SHALL NOT be rendered through Jinja template interpolation

Scenario: Dist fallback response

  • WHEN reject-history.html is missing in dist
  • THEN route SHALL return a minimal fallback HTML that still references /static/dist/reject-history.js

Requirement: Reject History shell integration SHALL use native module loading

The page SHALL integrate with portal-shell native module loading policy.

Scenario: Native module registration

  • WHEN shell resolves a route component for /reject-history
  • THEN it SHALL dynamically import frontend/src/reject-history/App.vue
  • THEN the route style bundle SHALL be loaded via registered style loaders

Requirement: Reject History page SHALL call APIs through shared core API module

The page SHALL call backend APIs via frontend/src/core/api.js without legacy global dependencies.

Scenario: API call path

  • WHEN reject-history page executes GET or export requests
  • THEN requests SHALL use shared API utilities (apiGet/equivalent)
  • THEN page behavior SHALL NOT depend on window.MesApi