Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Claude Code Review

on:
pull_request:
types: [opened, synchronize]

jobs:
claude-review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Review this pull request. Focus on correctness, potential bugs,
security issues, and test coverage. Be concise and specific, and
point to file:line where possible. Leave inline comments via the
repository's review tools.
39 changes: 39 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Claude Code

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]

jobs:
claude:
# Only run when someone actually mentions @claude, to avoid spending
# Actions minutes and API tokens on every comment/issue event.
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
3 changes: 3 additions & 0 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<router-link to="/demand" :class="{ active: $route.path === '/demand' }">
{{ t('nav.demandForecast') }}
</router-link>
<router-link to="/restocking" :class="{ active: $route.path === '/restocking' }">
{{ t('nav.restocking') }}
</router-link>
<router-link to="/reports" :class="{ active: $route.path === '/reports' }">
Reports
</router-link>
Expand Down
5 changes: 5 additions & 0 deletions client/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 31 additions & 1 deletion client/src/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
orders: 'Orders',
finance: 'Finance',
demandForecast: 'Demand Forecast',
restocking: 'Restocking',
companyName: 'Catalyst Components',
subtitle: 'Inventory Management System'
},
Expand Down Expand Up @@ -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: '{days} days',
table: {
orderNumber: 'Order Number',
orderId: 'Order ID',
Expand All @@ -125,7 +128,8 @@ export default {
totalValue: 'Total Value',
status: 'Status',
expectedDelivery: 'Expected Delivery',
actualDelivery: 'Actual Delivery'
actualDelivery: 'Actual Delivery',
leadTime: 'Lead Time'
}
},

Expand Down Expand Up @@ -204,11 +208,37 @@ export default {
shipped: 'Shipped',
processing: 'Processing',
backordered: 'Backordered',
submitted: 'Submitted',
inStock: 'In Stock',
lowStock: 'Low Stock',
adequate: 'Adequate'
},

// Restocking
restocking: {
title: 'Restocking',
description: 'Set a budget and place restock orders based on the demand forecast',
budgetLabel: 'Available Budget',
budgetHelp: 'Drag to set how much you can spend. Recommendations update automatically.',
recommended: 'Recommended Restock',
itemsRecommended: 'Items Recommended',
totalCost: 'Total Cost',
budgetRemaining: 'Budget Remaining',
placeOrder: 'Place Order',
placing: 'Placing order...',
orderPlaced: 'Order {orderNumber} submitted. Expected delivery {date}.',
noRecommendations: 'Budget is too low to restock any item. Increase the budget to see recommendations.',
table: {
item: 'Item',
trend: 'Trend',
gap: 'Forecast Gap',
quantity: 'Restock Qty',
unitCost: 'Unit Cost',
lineCost: 'Line Cost',
leadTime: 'Lead Time'
}
},

// Trends
trends: {
increasing: 'increasing',
Expand Down
32 changes: 31 additions & 1 deletion client/src/locales/ja.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
orders: '注文',
finance: '財務',
demandForecast: '需要予測',
restocking: '補充',
companyName: '触媒コンポーネンツ',
subtitle: '在庫管理システム'
},
Expand Down Expand Up @@ -106,12 +107,14 @@ export default {
title: '注文',
description: '顧客注文の表示と管理',
allOrders: 'すべての注文',
submittedOrders: '送信済み注文',
totalOrders: '総注文数',
totalRevenue: '総収益',
avgOrderValue: '平均注文額',
onTimeDelivery: '定時配達',
itemsCount: '{count}件',
quantity: '数量',
leadTimeDays: '{days}日',
table: {
orderNumber: '注文番号',
orderId: '注文ID',
Expand All @@ -125,7 +128,8 @@ export default {
totalValue: '合計金額',
status: 'ステータス',
expectedDelivery: '予定配達日',
actualDelivery: '実際の配達日'
actualDelivery: '実際の配達日',
leadTime: 'リードタイム'
}
},

Expand Down Expand Up @@ -204,11 +208,37 @@ export default {
shipped: '出荷済み',
processing: '処理中',
backordered: 'バックオーダー',
submitted: '送信済み',
inStock: '在庫あり',
lowStock: '在庫僅少',
adequate: '適量'
},

// Restocking
restocking: {
title: '補充',
description: '予算を設定し、需要予測に基づいて補充注文を行います',
budgetLabel: '利用可能な予算',
budgetHelp: 'ドラッグして使用可能額を設定します。推奨は自動的に更新されます。',
recommended: '推奨補充',
itemsRecommended: '推奨品目数',
totalCost: '合計費用',
budgetRemaining: '残り予算',
placeOrder: '注文する',
placing: '注文を送信中...',
orderPlaced: '注文 {orderNumber} を送信しました。予定配達日 {date}。',
noRecommendations: '予算が不足しているため、補充できる品目がありません。予算を増やしてください。',
table: {
item: '品目',
trend: '傾向',
gap: '予測ギャップ',
quantity: '補充数量',
unitCost: '単価',
lineCost: '小計',
leadTime: 'リードタイム'
}
},

// Trends
trends: {
increasing: '増加',
Expand Down
4 changes: 3 additions & 1 deletion client/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -16,7 +17,8 @@ const router = createRouter({
{ path: '/orders', component: Orders },
{ path: '/demand', component: Demand },
{ path: '/spending', component: Spending },
{ path: '/reports', component: Reports }
{ path: '/reports', component: Reports },
{ path: '/restocking', component: Restocking }
]
})

Expand Down
69 changes: 68 additions & 1 deletion client/src/views/Orders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,54 @@
</div>
</div>

<div v-if="getOrdersByStatus('Submitted').length > 0" class="card submitted-card">
<div class="card-header">
<h3 class="card-title">{{ t('orders.submittedOrders') }} ({{ getOrdersByStatus('Submitted').length }})</h3>
</div>
<div class="table-container">
<table class="orders-table">
<thead>
<tr>
<th class="col-order-number">{{ t('orders.table.orderNumber') }}</th>
<th class="col-items">{{ t('orders.table.items') }}</th>
<th class="col-status">{{ t('orders.table.status') }}</th>
<th class="col-date">{{ t('orders.table.orderDate') }}</th>
<th class="col-date">{{ t('orders.table.expectedDelivery') }}</th>
<th class="col-lead">{{ t('orders.table.leadTime') }}</th>
<th class="col-value">{{ t('orders.table.totalValue') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="order in getOrdersByStatus('Submitted')" :key="order.id">
<td class="col-order-number"><strong>{{ order.order_number }}</strong></td>
<td class="col-items">
<details class="items-details">
<summary class="items-summary">
{{ t('orders.itemsCount', { count: order.items.length }) }}
</summary>
<div class="items-dropdown">
<div v-for="(item, idx) in order.items" :key="idx" class="item-entry">
<span class="item-name">{{ translateProductName(item.name) }}</span>
<span class="item-meta">{{ t('orders.quantity') }}: {{ item.quantity }} @ {{ currencySymbol }}{{ item.unit_price }}</span>
</div>
</div>
</details>
</td>
<td class="col-status">
<span :class="['badge', getOrderStatusClass(order.status)]">
{{ t(`status.${order.status.toLowerCase()}`) }}
</span>
</td>
<td class="col-date">{{ formatDate(order.order_date) }}</td>
<td class="col-date">{{ formatDate(order.expected_delivery) }}</td>
<td class="col-lead">{{ t('orders.leadTimeDays', { days: getLeadTime(order) }) }}</td>
<td class="col-value"><strong>{{ currencySymbol }}{{ order.total_value.toLocaleString() }}</strong></td>
</tr>
</tbody>
</table>
</div>
</div>

<div class="card">
<div class="card-header">
<h3 class="card-title">{{ t('orders.allOrders') }} ({{ orders.length }})</h3>
Expand Down Expand Up @@ -138,11 +186,20 @@ export default {
'Delivered': 'success',
'Shipped': 'info',
'Processing': 'warning',
'Backordered': 'danger'
'Backordered': 'danger',
'Submitted': 'info'
}
return statusMap[status] || 'info'
}

// Delivery lead time in whole days, derived from the gap between the order
// date and its expected delivery date.
const getLeadTime = (order) => {
const ordered = new Date(order.order_date)
const expected = new Date(order.expected_delivery)
return Math.round((expected - ordered) / 86400000)
}

const formatDate = (dateString) => {
const { currentLocale } = useI18n()
const locale = currentLocale.value === 'ja' ? 'ja-JP' : 'en-US'
Expand All @@ -162,6 +219,7 @@ export default {
orders,
getOrdersByStatus,
getOrderStatusClass,
getLeadTime,
formatDate,
currencySymbol,
translateProductName,
Expand Down Expand Up @@ -203,6 +261,15 @@ export default {
width: 120px;
}

.col-lead {
width: 110px;
}

/* Submitted (restocking) orders get a subtle accent to set them apart */
.submitted-card {
border-left: 4px solid #2563eb;
}

/* Items details styling */
.items-details {
position: relative;
Expand Down
Loading