From 45a84af5b7f10377f07c3f7387d21d836e99fcb0 Mon Sep 17 00:00:00 2001 From: Ashlesh Patel Date: Thu, 16 Jul 2026 07:18:49 -0700 Subject: [PATCH 1/2] Add restocking workflow: Restocking tab and create-order API Adds a Restocking view where warehouse staff assemble a restock and submit it as a new order. Backend gains POST /api/orders, which stamps the order "Submitted" with a fixed 14-day lead time and computes the total server-side. Orders view surfaces submitted restock orders; en/ja locales and nav/router are wired up for the new tab. Co-Authored-By: Claude Opus 4.8 --- client/src/App.vue | 3 + client/src/api.js | 5 + client/src/locales/en.js | 35 ++- client/src/locales/ja.js | 35 ++- client/src/main.js | 2 + client/src/views/Orders.vue | 77 +++++- client/src/views/Restocking.vue | 447 ++++++++++++++++++++++++++++++++ docs/architecture.html | 420 ++++++++++++++++++++++++++++++ server/main.py | 39 +++ 9 files changed, 1058 insertions(+), 5 deletions(-) create mode 100644 client/src/views/Restocking.vue create mode 100644 docs/architecture.html diff --git a/client/src/App.vue b/client/src/App.vue index c2da05a5c..b61aae150 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -16,6 +16,9 @@ {{ t('nav.orders') }} + + {{ t('nav.restocking') }} + {{ t('nav.finance') }} diff --git a/client/src/api.js b/client/src/api.js index 11cb9db70..3d4b82b11 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -33,6 +33,11 @@ export const api = { return response.data }, + async createOrder(orderData) { + const response = await axios.post(`${API_BASE_URL}/orders`, orderData) + return response.data + }, + async getDemandForecasts() { const response = await axios.get(`${API_BASE_URL}/demand`) return response.data diff --git a/client/src/locales/en.js b/client/src/locales/en.js index 03a58fe6e..d5b0bfcb4 100644 --- a/client/src/locales/en.js +++ b/client/src/locales/en.js @@ -4,6 +4,7 @@ export default { overview: 'Overview', inventory: 'Inventory', orders: 'Orders', + restocking: 'Restocking', finance: 'Finance', demandForecast: 'Demand Forecast', companyName: 'Catalyst Components', @@ -106,12 +107,14 @@ export default { title: 'Orders', description: 'View and manage customer orders', allOrders: 'All Orders', + submittedOrders: 'Submitted Orders', totalOrders: 'Total Orders', totalRevenue: 'Total Revenue', avgOrderValue: 'Avg Order Value', onTimeDelivery: 'On-Time Delivery', itemsCount: '{count} items', quantity: 'Qty', + leadTimeDays: '{count} days', table: { orderNumber: 'Order Number', orderId: 'Order ID', @@ -125,7 +128,36 @@ export default { totalValue: 'Total Value', status: 'Status', expectedDelivery: 'Expected Delivery', - actualDelivery: 'Actual Delivery' + actualDelivery: 'Actual Delivery', + submitted: 'Submitted', + leadTime: 'Lead Time' + } + }, + + // Restocking + restocking: { + title: 'Restocking', + description: 'Set a budget and restock high-demand items from the forecast', + budgetLabel: 'Available Budget', + budgetHint: 'Drag to set how much you can spend on restocking', + recommendedTitle: 'Recommended Restock', + summaryItems: 'Items recommended', + totalCost: 'Total Cost', + budgetRemaining: 'Budget Remaining', + placeOrder: 'Place Order', + placing: 'Placing order...', + orderPlaced: 'Restocking order {orderNumber} submitted successfully.', + viewInOrders: 'View in Orders', + noMatches: 'No forecast items currently match a priced inventory item, so there is nothing to restock.', + budgetTooLow: 'No items fit within this budget. Increase the budget to see recommendations.', + table: { + sku: 'SKU', + itemName: 'Item Name', + trend: 'Trend', + demand: 'Current → Forecast', + restockQty: 'Restock Qty', + unitCost: 'Unit Cost', + lineCost: 'Line Cost' } }, @@ -204,6 +236,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 db33223ac..e9df9ff28 100644 --- a/client/src/locales/ja.js +++ b/client/src/locales/ja.js @@ -4,6 +4,7 @@ export default { overview: '概要', inventory: '在庫', orders: '注文', + restocking: '補充', finance: '財務', demandForecast: '需要予測', companyName: '触媒コンポーネンツ', @@ -106,12 +107,14 @@ export default { title: '注文', description: '顧客注文の表示と管理', allOrders: 'すべての注文', + submittedOrders: '送信済み注文', totalOrders: '総注文数', totalRevenue: '総収益', avgOrderValue: '平均注文額', onTimeDelivery: '定時配達', itemsCount: '{count}件', quantity: '数量', + leadTimeDays: '{count}日', table: { orderNumber: '注文番号', orderId: '注文ID', @@ -125,7 +128,36 @@ export default { totalValue: '合計金額', status: 'ステータス', expectedDelivery: '予定配達日', - actualDelivery: '実際の配達日' + actualDelivery: '実際の配達日', + submitted: '送信日', + leadTime: 'リードタイム' + } + }, + + // Restocking + restocking: { + title: '補充', + description: '予算を設定し、予測に基づいて需要の高い品目を補充します', + budgetLabel: '利用可能な予算', + budgetHint: 'スライダーで補充に使える金額を設定します', + recommendedTitle: '推奨補充', + summaryItems: '推奨品目数', + totalCost: '総コスト', + budgetRemaining: '残り予算', + placeOrder: '発注する', + placing: '発注中...', + orderPlaced: '補充注文 {orderNumber} が正常に送信されました。', + viewInOrders: '注文で表示', + noMatches: '価格付き在庫品目に一致する予測品目が現在ないため、補充対象がありません。', + budgetTooLow: 'この予算に収まる品目がありません。予算を増やすと推奨が表示されます。', + table: { + sku: 'SKU', + itemName: '品目名', + trend: 'トレンド', + demand: '現在 → 予測', + restockQty: '補充数量', + unitCost: '単価', + lineCost: '小計' } }, @@ -204,6 +236,7 @@ export default { shipped: '出荷済み', processing: '処理中', backordered: 'バックオーダー', + submitted: '送信済み', inStock: '在庫あり', lowStock: '在庫僅少', adequate: '適量' diff --git a/client/src/main.js b/client/src/main.js index 477c2d966..611c0a3b1 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(), @@ -14,6 +15,7 @@ const router = createRouter({ { path: '/', component: Dashboard }, { path: '/inventory', component: Inventory }, { path: '/orders', component: Orders }, + { path: '/restocking', component: Restocking }, { path: '/demand', component: Demand }, { path: '/spending', component: Spending }, { path: '/reports', component: Reports } diff --git a/client/src/views/Orders.vue b/client/src/views/Orders.vue index 7413f6e66..15b345e90 100644 --- a/client/src/views/Orders.vue +++ b/client/src/views/Orders.vue @@ -25,11 +25,64 @@
{{ t('status.backordered') }}
{{ getOrdersByStatus('Backordered').length }}
+
+
{{ t('status.submitted') }}
+
{{ submittedOrders.length }}
+
+ + + +
+
+

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

+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
{{ t('orders.table.orderNumber') }}{{ t('orders.table.items') }}{{ t('orders.table.status') }}{{ t('orders.table.submitted') }}{{ t('orders.table.expectedDelivery') }}{{ t('orders.table.leadTime') }}{{ t('orders.table.totalValue') }}
{{ order.order_number }} +
+ + {{ 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) }}{{ formatDate(order.expected_delivery) }}{{ t('orders.leadTimeDays', { count: leadTimeDays(order) }) }}{{ currencySymbol }}{{ order.total_value.toLocaleString() }}
+
-

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

+

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

@@ -45,7 +98,7 @@ - +
{{ order.order_number }} {{ translateCustomerName(order.customer) }} @@ -133,16 +186,27 @@ export default { return orders.value.filter(order => order.status === status) } + // Submitted restocking orders shown in their own section; everything else in "All Orders" + const submittedOrders = computed(() => orders.value.filter(order => order.status === 'Submitted')) + const customerOrders = computed(() => orders.value.filter(order => order.status !== 'Submitted')) + const getOrderStatusClass = (status) => { const statusMap = { 'Delivered': 'success', 'Shipped': 'info', 'Processing': 'warning', - 'Backordered': 'danger' + 'Backordered': 'danger', + 'Submitted': 'info' } return statusMap[status] || 'info' } + // Delivery lead time in whole days between submission and expected delivery + const leadTimeDays = (order) => { + const ms = new Date(order.expected_delivery) - new Date(order.order_date) + return Math.round(ms / 86400000) + } + const formatDate = (dateString) => { const { currentLocale } = useI18n() const locale = currentLocale.value === 'ja' ? 'ja-JP' : 'en-US' @@ -160,8 +224,11 @@ export default { loading, error, orders, + submittedOrders, + customerOrders, getOrdersByStatus, getOrderStatusClass, + leadTimeDays, formatDate, currencySymbol, translateProductName, @@ -199,6 +266,10 @@ export default { width: 140px; } +.col-lead { + width: 100px; +} + .col-value { width: 120px; } diff --git a/client/src/views/Restocking.vue b/client/src/views/Restocking.vue new file mode 100644 index 000000000..6f1a32160 --- /dev/null +++ b/client/src/views/Restocking.vue @@ -0,0 +1,447 @@ + + + + + diff --git a/docs/architecture.html b/docs/architecture.html new file mode 100644 index 000000000..e9a4ca440 --- /dev/null +++ b/docs/architecture.html @@ -0,0 +1,420 @@ + + + + + + Architecture — Factory Inventory Management System + + + +
+ +
+

System Architecture

+

Factory Inventory Management System

+

A full-stack demo for factory operations — inventory tracking, order management, demand + forecasting, backlog monitoring, and spending analytics. A Vue 3 single-page app talks to a + Python FastAPI service that serves in-memory data loaded from JSON files.

+
+ Frontend :3000 + Backend API :8001 + Data In-memory JSON + Persistence None (mock) + Auth None (demo) +
+
+ + +
+

Tech Stack

+
+
+

Frontend

+

client/ · Vite dev server on port 3000

+
    +
  • Vue 3^3.4 · Composition API
  • +
  • Vue Router^4.3 · web history
  • +
  • Axios^1.6 · HTTP client
  • +
  • Vite^5.2 · build / dev
  • +
  • i18ncustom · en / ja
  • +
+
+
+

Backend

+

server/ · Uvicorn on port 8001

+
    +
  • FastAPI^0.110 · REST
  • +
  • Uvicorn^0.24 · ASGI server
  • +
  • Pydanticv2 · validation
  • +
  • CORSallow-all (dev)
  • +
  • Python≥ 3.11 · uv
  • +
+
+
+

Data

+

server/data/ · loaded at startup

+
    +
  • inventory.jsonstock items
  • +
  • orders.json12 mo. of orders
  • +
  • demand_forecaststrends
  • +
  • backlog / spending+ transactions
  • +
  • Storein-memory lists
  • +
+
+
+
+ + +
+

System Architecture

+
+ +
+
+ Client · Vue 3 SPA +

Browser — Single-Page Application

+ client/src +
+
+ App.vue · shell: nav + FilterBar + router-view + Views · Dashboard, Inventory, Orders, Demand, Spending, Reports + Components · FilterBar, detail modals, ProfileMenu + Composables · useFilters, useAuth, useI18n + Router · 6 client-side routes +
+
+ +
+
+ HTTP · Axios +

API Client & Transport

+ client/src/api.js +
+
+ Centralized api object · one method per endpoint + Base URL · http://localhost:8001/api + Builds URLSearchParams · skips 'all' filters + CORS · cross-origin :3000 → :8001 +
+
+ +
+
+ API · FastAPI +

REST Service & Business Logic

+ server/main.py +
+
+ Route handlers · /api/inventory, /orders, /dashboard, /spending, /reports… + Pydantic models · response validation via response_model + apply_filters() · warehouse / category / status + filter_by_month() · month + quarter mapping + Aggregation · summary, quarterly & monthly trends +
+
+ +
+
+ Data · In-Memory +

Mock Data Layer

+ server/mock_data.py · server/data/*.json +
+
+ load_json_file() · reads JSON at startup + Module-level lists · inventory_items, orders, … + No database · changes don't persist; restart reloads +
+
+ +
+
+ + +
+

Data Flow — Request Lifecycle

+
+
+
1
+
+

User interacts / changes a filter

+

The shared FilterBar updates singleton refs in useFilters + (Time Period, Warehouse, Category, Order Status).

+
+
+
+
2
+
+

View requests data

+

A view (e.g. Dashboard.vue) calls getCurrentFilters() and passes + the filter object to the matching api.js method.

+
+
+
+
3
+
+

Axios sends an HTTP GET

+

Non-"all" filters become query params — e.g. + GET /api/orders?warehouse=San%20Francisco&month=Q3-2025.

+
+
+
+
4
+
+

FastAPI filters in memory

+

The route runs apply_filters() then filter_by_month() over the + in-memory lists — filtering on copies, never mutating the source data.

+
+
+
+
5
+
+

Pydantic validates the response

+

Results are validated against a response_model (e.g. List[Order]) + and serialized to JSON.

+
+
+
+
6
+
+

UI reacts

+

Axios resolves the JSON into the view's refs; computed properties derive metrics + and charts, and Vue re-renders the template.

+
+
+
+
+ + +
+

API Surface & Filter Model

+
+ + + + + + + + + + + + + + + +
MethodEndpointFilters
GET/api/inventorywarehouse, category
GET/api/inventory/{id}
GET/api/orderswarehouse, category, status, month
GET/api/orders/{id}
GET/api/demand
GET/api/backlog— (adds PO flag)
GET/api/dashboard/summaryall four
GET/api/spending/*summary · monthly · categories · transactions
GET/api/reports/*quarterly · monthly-trends
+ +
+

Four global filters

+

+ UI state (client) maps to API query params (server):

+
+
Time Period + month · 2025-07 or Q3-2025
+
Warehouse + warehouse
+
Category + category
+
Order Status + status
+
+
+
+
+ + +
+

Notes

+
+ Demo application. Data is mock and held in memory — there is no database, + authentication, or persistence, and CORS is open to all origins. A few endpoints referenced by the + frontend client (/api/tasks, /api/purchase-orders) are not yet implemented + in the backend — an intentional gap in this workshop exercise. Not production-ready without a + database, auth, and input hardening. +
+
+ +
+ Factory Inventory Management System · architecture overview · + generated from source in client/ and server/. +
+ +
+ + diff --git a/server/main.py b/server/main.py index a0c2d8c5a..9b3839f94 100644 --- a/server/main.py +++ b/server/main.py @@ -2,10 +2,14 @@ from fastapi.middleware.cors import CORSMiddleware from typing import List, Optional from pydantic import BaseModel +from datetime import datetime, timedelta from mock_data import inventory_items, orders, demand_forecasts, backlog_items, spending_summary, monthly_spending, category_spending, recent_transactions, purchase_orders app = FastAPI(title="Factory Inventory Management System") +# Fixed delivery lead time (in days) applied to submitted restocking orders +LEAD_TIME_DAYS = 14 + # Quarter mapping for date filtering QUARTER_MAP = { 'Q1-2025': ['2025-01', '2025-02', '2025-03'], @@ -120,6 +124,11 @@ class CreatePurchaseOrderRequest(BaseModel): expected_delivery_date: str notes: Optional[str] = None +class CreateOrderRequest(BaseModel): + items: List[dict] # each item: {sku, name, quantity, unit_price} + warehouse: Optional[str] = None + customer: Optional[str] = "Internal Restock" + # API endpoints @app.get("/") def root(): @@ -161,6 +170,36 @@ def get_order(order_id: str): raise HTTPException(status_code=404, detail="Order not found") return order +@app.post("/api/orders", response_model=Order) +def create_order(req: CreateOrderRequest): + """Create a new (restocking) order and append it to the in-memory orders list. + + Used by the Restocking tab. The order is stamped with status 'Submitted' and a + fixed 14-day delivery lead time. Data is in-memory only, so it persists for the + server session and resets on restart. + """ + now = datetime.now() + # Derive the total server-side so the client can't desync unit price and line totals + total = sum(item.get("quantity", 0) * item.get("unit_price", 0) for item in req.items) + # Sequence submitted orders independently so numbers are stable and human-readable + seq = sum(1 for o in orders if o.get("status") == "Submitted") + 1 + order_number = f"RST-{now.year}-{seq:04d}" + + new_order = { + "id": order_number, + "order_number": order_number, + "customer": req.customer, + "items": req.items, + "status": "Submitted", + "order_date": now.isoformat(timespec="seconds"), + "expected_delivery": (now + timedelta(days=LEAD_TIME_DAYS)).isoformat(timespec="seconds"), + "total_value": round(total, 2), + "warehouse": req.warehouse, + "category": None, + } + orders.append(new_order) + return new_order + @app.get("/api/demand", response_model=List[DemandForecast]) def get_demand_forecasts(): """Get demand forecasts""" From f5a0f07988f8d74db2392ea0b18d43dace64a622 Mon Sep 17 00:00:00 2001 From: Ashlesh Patel Date: Thu, 16 Jul 2026 10:29:49 -0700 Subject: [PATCH 2/2] Add debugger agent and vue-component-analysis skill Adds two Claude Code definitions for the inventory-management app: - debugger agent: runtime-error investigator for the Vue/FastAPI stack. Reproduces failures, localizes root cause, proposes a fix; read-only by design (hands .vue fixes to vue-expert). - vue-component-analysis skill: prioritized performance and code-reuse report for client/src components. Analysis only; no .vue edits. Co-Authored-By: Claude Opus 4.8 --- .claude/agents/debugger.md | 92 +++++++++++++++ .../skills/vue-component-analysis/SKILL.md | 108 ++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 .claude/agents/debugger.md create mode 100644 .claude/skills/vue-component-analysis/SKILL.md diff --git a/.claude/agents/debugger.md b/.claude/agents/debugger.md new file mode 100644 index 000000000..892dbac25 --- /dev/null +++ b/.claude/agents/debugger.md @@ -0,0 +1,92 @@ +--- +name: debugger +description: Runtime-error investigator. Use to diagnose crashes, exceptions, stack traces, and unexpected runtime behavior in the inventory-management app (Vue/Vite frontend and FastAPI backend). Reproduces the failure, reads the trace, localizes the root cause, and proposes a targeted fix — it does not edit files. +tools: Read, Grep, Glob, Bash +model: sonnet +color: cyan +--- + +# Debugger Agent + +You are a runtime-error specialist for the Factory Inventory Management System (Vue 3 + Vite frontend on `:3000`, Python FastAPI backend on `:8001`, in-memory mock data). You investigate crashes, exceptions, and misbehavior; you find the **root cause** and propose a precise fix. You are given a symptom — an error message, a stack trace, a failing request, or "X throws when I do Y" — and you run it to ground. + +## Core principle: reproduce, then reason + +Never diagnose from the error text alone. You have **Bash** — use it to observe the real failure before forming conclusions. A stack trace tells you where it blew up, not why. Confirm the trigger, read the state at the failure point, then explain the mechanism. + +## Boundaries + +- **You do not edit files.** You have no Write/Edit access by design. Produce the diagnosis and a concrete fix (with the exact change), then hand `.vue` fixes to the **vue-expert** agent and other fixes back to the caller. +- **Bash is for observation and reproduction only:** run the app, curl endpoints, read logs, grep source, inspect data, run a failing test. Do **not** use it to mutate source, delete data, kill unrelated processes, or make outward network calls. Prefer read-only commands. +- **Scope is one failure at a time.** Chase the reported symptom to its root cause; note unrelated issues you pass but don't wander. + +## Investigation procedure + +1. **Capture the symptom exactly.** The full error string and stack trace, the action that triggered it, and where it surfaced (browser console, Vite overlay, terminal, API response, log file). +2. **Reproduce it.** Drive the smallest thing that triggers the failure: + - Backend: `curl -s -i http://localhost:8001/api/` (add query params to hit the failing filter); check `/api/docs` for the contract. + - Frontend: load the route, or read the Vite output; a build/transform error appears there, a runtime error in the browser console. + - If the servers aren't up: `./scripts/start.sh` (logs to `/tmp/inventory-backend.log` and `/tmp/inventory-frontend.log`). +3. **Read the trace top-down for cause, bottom-up for origin.** Identify the **deepest frame in first-party code** (`server/*.py`, `client/src/**`) — third-party frames (uvicorn, pydantic, vite, vue internals) usually just carry the error, they don't own the bug. +4. **Inspect state at the failure point.** Read the offending line and the values reaching it — the data shape (`server/data/*.json`, `mock_data.py`), the params, the reactive refs. Grep for where that value is produced. +5. **Form one hypothesis and test it.** Change an input, not the code: a different query param, an empty list, a null field. Confirm the failure appears and disappears as the hypothesis predicts. +6. **Localize the root cause** to a specific line and mechanism, then design the minimal fix. + +## Reading stack traces in this stack + +**Python / FastAPI (backend):** traces print to the terminal and `/tmp/inventory-backend.log`. Read the last `File ".../server/....py", line N, in fn` frame in `server/` — that's the origin. The final line names the exception (`KeyError`, `TypeError: unsupported operand`, `ValidationError`, `AttributeError: 'NoneType'`). A 500 in the API response with no body usually means an unhandled exception — get the traceback from the log, not the HTTP body. `pydantic.ValidationError` means the data or response model drifted from the JSON in `server/data/`. + +**JavaScript / Vue (frontend):** two distinct failure classes — +- **Vite transform / import errors** show in the terminal + full-screen overlay ("Failed to resolve import …", syntax errors). These are build-time and block the whole page. Check `/tmp/inventory-frontend.log`. +- **Runtime errors** show in the browser console with a component trace ("at "). Common here: reading a property of `undefined` before data loads, `.getMonth()` on an invalid `Date`, `.map`/`.filter` on a ref that's still `null`, or a template referencing something not returned from `setup()`. +- Vite serves minified deps; map the trace back to `client/src/**` source, ignore `node_modules` frames. + +## Usual suspects in this codebase + +Check these first — they recur here: +- **Unvalidated dates:** `new Date(x).getMonth()` on a bad/empty string → `NaN`/wrong month. Validate with `isNaN(date.getTime())` first. +- **Data accessed before load:** a computed/template touching `items.value[0]` while `loading` is still true and the ref is empty. Guard for empty. +- **Filter param mismatch:** inventory has no `month`/`status` dimension; passing those, or an unknown `warehouse`/`category`, can yield empty or unexpected results. Confirm against the endpoint's real filters. +- **Model ↔ data drift:** editing `server/data/*.json` or the shape returned by an endpoint without updating the Pydantic model → `ValidationError`. Grep the model and the JSON keys together. +- **Off-by-one / missing key:** `:key="index"` reuse, or `monthlyData[index - 1]` at index 0. +- **CORS / wrong port:** frontend calling the wrong origin surfaces as a network error in the console, not a backend trace. + +## Output format + +```markdown +# Debug Report: + +**Symptom:** +**Reproduced:** Yes — · + +## Root cause + + +## Evidence +- +- +- + +## Suggested fix +**Where:** +**Change:** +``` +// before → after (minimal, targeted) +``` +**Why this fixes it:** +**Apply via:** + +## Verify after fixing + + +## Noted in passing (optional) + +``` + +## Principles + +- **Evidence over guess.** Every root-cause claim is backed by a trace frame, a log line, or an observed reproduction — never "it's probably…". +- **Root cause, not symptom.** A missing null-check that hides the real bug is not a fix. Explain the mechanism. +- **Minimal, targeted fixes.** Smallest change that addresses the cause; respect existing patterns (`client/CLAUDE.md`, `server/CLAUDE.md`). +- **Always give a verification step.** The caller must be able to confirm the fix resolves the exact failure you reproduced. +- **If you cannot reproduce it, say so** and state precisely what you'd need (the full trace, the input, the env) rather than guessing at a fix. diff --git a/.claude/skills/vue-component-analysis/SKILL.md b/.claude/skills/vue-component-analysis/SKILL.md new file mode 100644 index 000000000..b34730a4a --- /dev/null +++ b/.claude/skills/vue-component-analysis/SKILL.md @@ -0,0 +1,108 @@ +--- +name: vue-component-analysis +description: Analyze Vue 3 component structure and produce a prioritized report of performance and code-reuse improvements for the inventory-management client. Use when asked to review, analyze, audit, or "find optimizations for" one or more .vue files or the client as a whole. This skill only reads and reports — it does not edit .vue files (hand fixes to the vue-expert agent). +--- + +# Vue Component Analysis + +A repeatable method for analyzing Vue 3 components in `client/src/` and reporting **performance** and **code-reuse** improvements, ranked by impact. This app uses the Composition API (`setup()`), Vite, scoped CSS, custom SVG charts, Axios via `client/src/api.js`, and shared state in composables (`useFilters`, `useI18n`, `useAuth`). + +## What this skill does and does not do + +- **Does:** read components, measure them against the checks below, and emit a prioritized report with concrete file:line references and a suggested fix for each finding. +- **Does not:** edit `.vue` files. This repo's rule is that any create/significant-modify of a `.vue` file goes through the **vue-expert** agent. Produce the analysis, then hand the accepted findings to vue-expert to apply, or to `/optimize` for whole-codebase dead-code removal. +- **Not a bug hunt.** Correctness bugs belong to `/code-review`. Stay on performance and reuse. + +## Procedure + +1. **Scope the target.** One component, a folder (`views/` or `components/`), or the whole client. If unscoped, default to `client/src/views/*.vue` and `client/src/components/*.vue`. +2. **Measure first.** For each target: total lines and the template / script / style split (`grep -n -E '^