From fb19fec97ee8642614bd105564f4cc2bd7921e7b Mon Sep 17 00:00:00 2001 From: Udayasree Date: Mon, 1 Jun 2026 13:25:17 +0530 Subject: [PATCH] Add Restocking view, Orders enhancements, i18n updates, and docs - Add new Restocking view with budget slider and recommended restock table - Add useRestocking composable for restocking logic - Enhance Orders view with additional columns and status filtering - Extend en/ja locale files with new i18n keys - Update App.vue and main.js for new route registration - Update CLAUDE.md files with revised guidance - Add architecture.html doc and fix scripts/hooks Co-Authored-By: Claude Sonnet 4.6 --- .claude/hooks/post-tool-use.sh | 0 CLAUDE.md | 34 +- client/CLAUDE.md | 4 + client/package.json | 3 + client/src/App.vue | 3 + client/src/composables/useRestocking.js | 18 + client/src/locales/en.js | 26 +- client/src/locales/ja.js | 26 +- client/src/main.js | 2 + client/src/views/Orders.vue | 66 ++++ client/src/views/Restocking.vue | 448 ++++++++++++++++++++++++ docs/architecture.html | 300 ++++++++++++++++ scripts/start.sh | 0 scripts/stop.sh | 0 14 files changed, 923 insertions(+), 7 deletions(-) mode change 100755 => 100644 .claude/hooks/post-tool-use.sh create mode 100644 client/src/composables/useRestocking.js create mode 100644 client/src/views/Restocking.vue create mode 100644 docs/architecture.html mode change 100755 => 100644 scripts/start.sh mode change 100755 => 100644 scripts/stop.sh diff --git a/.claude/hooks/post-tool-use.sh b/.claude/hooks/post-tool-use.sh old mode 100755 new mode 100644 diff --git a/CLAUDE.md b/CLAUDE.md index d2086efa..2337606c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,12 +11,16 @@ Use the Task tool with these specialized subagents for appropriate tasks: - Examples: Creating components, fixing reactivity issues, performance optimization, complex state management - **MANDATORY RULE: ANY time you need to create or significantly modify a .vue file, you MUST delegate to vue-expert** - **code-reviewer**: Use after writing significant code to review quality and best practices +- **security-auditor**: Use to audit code for security issues (this is a demo with no auth/validation, so flag accordingly) - **Explore**: Use for understanding codebase structure, searching for patterns, or answering questions about how components work - **general-purpose**: Use for complex multi-step tasks or when other agents don't fit ### Skills - **backend-api-test** skill: Use when writing or modifying tests in `tests/backend` directory with pytest and FastAPI TestClient +### Slash Commands +Project commands live in `.claude/commands/`: `/start`, `/stop`, `/test`, `/optimize`, `/demo-branch`, `/reset-branch`. + ### MCP Tools - **ALWAYS use GitHub MCP tools** (`mcp__github__*`) for ALL GitHub operations - Exception: Local branches only - use `git checkout -b` instead of `mcp__github__create_branch` @@ -24,9 +28,9 @@ Use the Task tool with these specialized subagents for appropriate tasks: - Test against: `http://localhost:3000` (frontend), `http://localhost:8001` (API) ## Stack -- **Frontend**: Vue 3 + Composition API + Vite (port 3000) -- **Backend**: Python FastAPI (port 8001) -- **Data**: JSON files in `server/data/` loaded via `server/mock_data.py` +- **Frontend**: Vue 3 + Composition API + Vite (port 3000), vue-router, axios. i18n (en/ja) and client-only mock auth via composables. +- **Backend**: Python FastAPI (port 8001), managed with `uv` (Python >=3.11) +- **Data**: JSON files in `server/data/` loaded via `server/mock_data.py`; regenerate with `server/generate_data.py` ## Quick Start @@ -40,6 +44,18 @@ cd client npm install && npm run dev ``` +`./scripts/start.sh` / `stop.sh` start both at once but are macOS/Linux only — on Windows run the two commands above in separate terminals. + +## Testing + +```bash +cd tests +uv run pytest backend/ -v # all backend tests +uv run pytest backend/test_inventory.py::test_name -v # single test +``` + +Tests use FastAPI `TestClient` (fixture in `tests/backend/conftest.py`); config in `tests/pytest.ini`. There is no frontend test runner configured. + ## Key Patterns **Filter System**: 4 filters (Time Period, Warehouse, Category, Order Status) apply to all data via query params @@ -52,6 +68,8 @@ npm install && npm run dev - `GET /api/dashboard/summary` - All filters - `GET /api/demand`, `/api/backlog` - No filters - `GET /api/spending/*` - Summary, monthly, categories, transactions +- `GET /api/reports/quarterly`, `/api/reports/monthly-trends` - Reports view data +- `GET /api/inventory/{item_id}`, `/api/orders/{order_id}` - Single item (404 if missing) ## Common Issues 1. Use unique keys in v-for (not `index`) - use `sku`, `month`, etc. @@ -60,10 +78,16 @@ npm install && npm run dev 4. Inventory filters don't support month (no time dimension) 5. Revenue goals: $800K/month single, $9.6M YTD all months +## Conventions +- Always document non-obvious logic changes with comments + ## File Locations -- Views: `client/src/views/*.vue` +- Views: `client/src/views/*.vue` (Dashboard, Inventory, Orders, Demand, Backlog, Spending, Reports) +- Components: `client/src/components/*.vue` (FilterBar, detail modals, profile/language menus) +- Composables: `client/src/composables/` (`useFilters.js`, `useI18n.js`, `useAuth.js`) +- Locales: `client/src/locales/{en,ja}.js`; formatting helpers in `client/src/utils/` - API Client: `client/src/api.js` -- Backend: `server/main.py`, `server/mock_data.py` +- Backend: `server/main.py` (routes + Pydantic models), `server/mock_data.py`, `server/generate_data.py` - Data: `server/data/*.json` - Styles: `client/src/App.vue` diff --git a/client/CLAUDE.md b/client/CLAUDE.md index bb9960e7..1ef4b568 100644 --- a/client/CLAUDE.md +++ b/client/CLAUDE.md @@ -1,3 +1,7 @@ + + + + # CLAUDE.md - Client This file provides guidance to Claude Code (claude.ai/code) when working with the Vue 3 frontend. diff --git a/client/package.json b/client/package.json index 58375dc8..7ca85b4f 100644 --- a/client/package.json +++ b/client/package.json @@ -15,5 +15,8 @@ "devDependencies": { "@vitejs/plugin-vue": "^5.0.4", "vite": "^5.2.0" + }, + "allowScripts": { + "esbuild@0.21.5": true } } diff --git a/client/src/App.vue b/client/src/App.vue index c2da05a5..dd6849b1 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -22,6 +22,9 @@ {{ t('nav.demandForecast') }} + + {{ t('nav.restocking') }} + Reports diff --git a/client/src/composables/useRestocking.js b/client/src/composables/useRestocking.js new file mode 100644 index 00000000..51797191 --- /dev/null +++ b/client/src/composables/useRestocking.js @@ -0,0 +1,18 @@ +import { ref } from 'vue' + +// Module-level ref (singleton) - shared across the Restocking and Orders views. +// Restock orders are kept client-side only (the app has no POST /orders endpoint), +// so these reset on a full page refresh, consistent with tasks/purchase orders. +const submittedOrders = ref([]) + +export function useRestocking() { + // Add a newly placed restock order to the front of the shared list + const addSubmittedOrder = (order) => { + submittedOrders.value.unshift(order) + } + + return { + submittedOrders, + addSubmittedOrder + } +} diff --git a/client/src/locales/en.js b/client/src/locales/en.js index 03a58fe6..ff1010dd 100644 --- a/client/src/locales/en.js +++ b/client/src/locales/en.js @@ -6,6 +6,7 @@ export default { orders: 'Orders', finance: 'Finance', demandForecast: 'Demand Forecast', + restocking: 'Restocking', companyName: 'Catalyst Components', subtitle: 'Inventory Management System' }, @@ -106,6 +107,8 @@ export default { title: 'Orders', description: 'View and manage customer orders', allOrders: 'All Orders', + submittedOrders: 'Submitted Orders', + leadTimeDays: '{days} days', totalOrders: 'Total Orders', totalRevenue: 'Total Revenue', avgOrderValue: 'Avg Order Value', @@ -125,7 +128,8 @@ export default { totalValue: 'Total Value', status: 'Status', expectedDelivery: 'Expected Delivery', - actualDelivery: 'Actual Delivery' + actualDelivery: 'Actual Delivery', + leadTime: 'Lead Time' } }, @@ -188,6 +192,25 @@ export default { } }, + // Restocking + restocking: { + title: 'Restocking', + description: 'Set a budget and restock high-demand items', + budgetLabel: 'Available Budget', + budgetHint: 'Drag to set how much you can spend on restocking', + recommendations: 'Recommended Restock', + noRecommendations: 'No items need restocking for the current filters.', + qtyToOrder: 'Qty to Order', + unitCost: 'Unit Cost', + lineTotal: 'Line Total', + selectedTotal: 'Order Total', + remaining: 'Remaining Budget', + itemsSelected: '{count} items within budget', + placeOrder: 'Place Order', + orderPlaced: 'Restock order {orderNumber} submitted. View it in the Orders tab.', + estimatedNote: 'Estimated unit cost (no matching inventory item); based on the average inventory unit cost.' + }, + // Filters filters: { timePeriod: 'Time Period', @@ -204,6 +227,7 @@ export default { shipped: 'Shipped', processing: 'Processing', backordered: 'Backordered', + submitted: 'Submitted', inStock: 'In Stock', lowStock: 'Low Stock', adequate: 'Adequate' diff --git a/client/src/locales/ja.js b/client/src/locales/ja.js index db33223a..486ca8a3 100644 --- a/client/src/locales/ja.js +++ b/client/src/locales/ja.js @@ -6,6 +6,7 @@ export default { orders: '注文', finance: '財務', demandForecast: '需要予測', + restocking: '在庫補充', companyName: '触媒コンポーネンツ', subtitle: '在庫管理システム' }, @@ -106,6 +107,8 @@ export default { title: '注文', description: '顧客注文の表示と管理', allOrders: 'すべての注文', + submittedOrders: '送信済み注文', + leadTimeDays: '{days}日', totalOrders: '総注文数', totalRevenue: '総収益', avgOrderValue: '平均注文額', @@ -125,7 +128,8 @@ export default { totalValue: '合計金額', status: 'ステータス', expectedDelivery: '予定配達日', - actualDelivery: '実際の配達日' + actualDelivery: '実際の配達日', + leadTime: 'リードタイム' } }, @@ -188,6 +192,25 @@ export default { } }, + // Restocking + restocking: { + title: '在庫補充', + description: '予算を設定して需要の高い品目を補充します', + budgetLabel: '利用可能な予算', + budgetHint: 'スライダーを動かして補充に使える金額を設定します', + recommendations: '推奨補充品目', + noRecommendations: '現在のフィルター条件では補充が必要な品目はありません。', + qtyToOrder: '発注数量', + unitCost: '単価', + lineTotal: '小計', + selectedTotal: '注文合計', + remaining: '残り予算', + itemsSelected: '予算内の品目:{count}件', + placeOrder: '注文する', + orderPlaced: '補充注文 {orderNumber} を送信しました。注文タブで確認できます。', + estimatedNote: '推定単価(一致する在庫品目がないため、在庫の平均単価に基づきます)。' + }, + // Filters filters: { timePeriod: '期間', @@ -204,6 +227,7 @@ export default { shipped: '出荷済み', processing: '処理中', backordered: 'バックオーダー', + submitted: '送信済み', inStock: '在庫あり', lowStock: '在庫僅少', adequate: '適量' diff --git a/client/src/main.js b/client/src/main.js index 477c2d96..3347ae01 100644 --- a/client/src/main.js +++ b/client/src/main.js @@ -7,6 +7,7 @@ import Orders from './views/Orders.vue' import Demand from './views/Demand.vue' import Spending from './views/Spending.vue' import Reports from './views/Reports.vue' +import Restocking from './views/Restocking.vue' const router = createRouter({ history: createWebHistory(), @@ -15,6 +16,7 @@ const router = createRouter({ { path: '/inventory', component: Inventory }, { path: '/orders', component: Orders }, { path: '/demand', component: Demand }, + { path: '/restocking', component: Restocking }, { path: '/spending', component: Spending }, { path: '/reports', component: Reports } ] diff --git a/client/src/views/Orders.vue b/client/src/views/Orders.vue index 7413f6e6..7a4c3f89 100644 --- a/client/src/views/Orders.vue +++ b/client/src/views/Orders.vue @@ -27,6 +27,57 @@ +
+
+

{{ t('orders.submittedOrders') }} ({{ submittedOrders.length }})

+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
{{ t('orders.table.orderNumber') }}{{ t('orders.table.customer') }}{{ t('orders.table.items') }}{{ t('orders.table.status') }}{{ t('orders.table.orderDate') }}{{ t('orders.table.leadTime') }}{{ t('orders.table.totalValue') }}
{{ order.order_number }}{{ order.customer }} +
+ + {{ t('orders.itemsCount', { count: order.items.length }) }} + +
+
+ {{ translateProductName(item.name) }} + {{ t('orders.quantity') }}: {{ item.quantity }} @ {{ currencySymbol }}{{ item.unit_price }} +
+
+
+
+ + {{ t(`status.${order.status.toLowerCase()}`) }} + + {{ formatDate(order.order_date) }} +
{{ t('orders.leadTimeDays', { days: order.lead_time_days }) }}
+
{{ formatDate(order.expected_delivery) }}
+
{{ currencySymbol }}{{ order.total_value.toLocaleString() }}
+
+
+

{{ t('orders.allOrders') }} ({{ orders.length }})

@@ -83,12 +134,16 @@ import { ref, onMounted, watch, computed } from 'vue' import { api } from '../api' import { useFilters } from '../composables/useFilters' import { useI18n } from '../composables/useI18n' +import { useRestocking } from '../composables/useRestocking' export default { name: 'Orders', setup() { const { t, currentCurrency, translateProductName, translateCustomerName } = useI18n() + // Client-side restock orders submitted from the Restocking tab + const { submittedOrders } = useRestocking() + const currencySymbol = computed(() => { return currentCurrency.value === 'JPY' ? '¥' : '$' }) @@ -160,6 +215,7 @@ export default { loading, error, orders, + submittedOrders, getOrdersByStatus, getOrderStatusClass, formatDate, @@ -276,4 +332,14 @@ export default { font-size: 0.813rem; color: #64748b; } + +.lead-time { + font-weight: 600; + color: #0f172a; +} + +.lead-time-date { + font-size: 0.75rem; + color: #64748b; +} diff --git a/client/src/views/Restocking.vue b/client/src/views/Restocking.vue new file mode 100644 index 00000000..06f2ae56 --- /dev/null +++ b/client/src/views/Restocking.vue @@ -0,0 +1,448 @@ + + + + + diff --git a/docs/architecture.html b/docs/architecture.html new file mode 100644 index 00000000..661a43be --- /dev/null +++ b/docs/architecture.html @@ -0,0 +1,300 @@ + + + + + +Factory Inventory Management — System Architecture + + + +
+
+

Factory Inventory Management System

+

System architecture, technology stack, and data flow — a full-stack demo application.

+
+ Vue 3 SPA + FastAPI + In-memory mock data + No database + Demo / workshop +
+
+
+ +
+ + +
+

Technology Stack

+
+
+

Frontend

+ client/ · port 3000 +
    +
  • Vue 3.4 (Composition API)
  • +
  • Vite 5 dev server & build
  • +
  • vue-router 4 (SPA routing)
  • +
  • axios (HTTP client)
  • +
  • Custom SVG charts, scoped CSS
  • +
  • i18n (English / Japanese)
  • +
+
+
+

Backend

+ server/ · port 8001 +
    +
  • Python 3.11+ / FastAPI
  • +
  • Uvicorn ASGI server
  • +
  • Pydantic v2 models & validation
  • +
  • CORS open (allow_origins=["*"])
  • +
  • Auto docs at /docs
  • +
  • Managed with uv
  • +
+
+
+

Data Layer

+ server/data/ · 7 JSON files +
    +
  • No database — files loaded at startup
  • +
  • Held in memory for the process lifetime
  • +
  • inventory · orders · demand
  • +
  • backlog · purchase_orders
  • +
  • spending · transactions
  • +
  • Loaded via mock_data.py
  • +
+
+
+
+ + +
+

System Architecture

+
+ +
+
+ 1 +

Browser — Vue 3 Single-Page App

+ localhost:3000 +
+
+
ViewsDashboard, Inventory, Orders, Demand, Spending, Reports
+
ComponentsFilterBar, detail modals, profile & language menus
+
ComposablesuseFilters (shared state), useI18n, useAuth
+
api.jsCentralized axios client
+
+
+ +
+ HTTP / JSON · axios GET with query params + +
+ +
+
+ 2 +

FastAPI Application (main.py)

+ localhost:8001 +
+
+
CORS middlewareAllows all origins (demo)
+
Route handlersinventory, orders, demand, backlog, dashboard, spending, reports
+
Filter helpersapply_filters() · filter_by_month() (quarter aware)
+
Pydantic modelsValidate & shape responses (response_model)
+
+
+ +
+ In-process function calls · in-memory filtering + +
+ +
+
+ 3 +

Mock Data Layer (mock_data.py)

+ in memory +
+
+
JSON loaderReads server/data/*.json at import time
+
inventory.jsonStock items, reorder points
+
orders.json12 months of orders
+
spending / transactionsAnalytics data
+
backlog / purchase_ordersFulfillment data
+
+
+ +
+
+ + +
+

Request & Data Flow

+
+
A user selects filters in FilterBar (Time Period, Warehouse, Category, Order Status). These write to the shared useFilters singleton state.
+
A view (e.g. Dashboard.vue) calls getCurrentFilters() and passes the result to a method on api.js.
+
api.js builds a URLSearchParams query (skipping any 'all' values) and issues an axios.get to the FastAPI backend.
+
The matching FastAPI route receives the query params and applies apply_filters() and filter_by_month() over the in-memory lists.
+
The result is validated and serialized through a Pydantic response model, then returned as JSON.
+
The view stores raw data in refs; computed properties derive metrics and chart data, which Vue renders as custom SVG charts and tables.
+
+
+ + +
+

API Endpoints

+ + + + + + + + + + + + + + +
MethodPathPurposeFilters
GET/api/inventoryInventory itemswarehouse, category
GET/api/inventory/{id}Single item (404 if missing)
GET/api/ordersOrderswarehouse, category, status, month
GET/api/orders/{id}Single order (404 if missing)
GET/api/demandDemand forecasts
GET/api/backlogBacklog + purchase-order flag
GET/api/dashboard/summaryAggregated KPIswarehouse, category, status, month
GET/api/spending/*summary · monthly · categories · transactions
GET/api/reports/quarterlyQuarterly performance
GET/api/reports/monthly-trendsMonth-over-month trends
+
+ + +
+

Notes & Caveats

+
+

Demo application — not production-ready

+
    +
  • No persistence: data lives in memory; restarting the server resets all state to the JSON files.
  • +
  • No authentication: useAuth is a client-only mock; CORS is fully open.
  • +
  • Client/backend drift: api.js includes methods for /tasks and /purchase-orders endpoints that are not yet implemented in main.py.
  • +
  • Routing: the router exposes Dashboard, Inventory, Orders, Demand, Spending and Reports; Backlog.vue exists but is not currently routed.
  • +
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh old mode 100755 new mode 100644 diff --git a/scripts/stop.sh b/scripts/stop.sh old mode 100755 new mode 100644