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.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() }} |
+
+
+
+
+
+