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
35 changes: 35 additions & 0 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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: |
Please review this pull request and provide feedback on:
- Code quality and best practices
- Potential bugs or issues
- Performance considerations
- Security concerns
- Test coverage

Be constructive and specific in your feedback. Use inline comments
to highlight specific lines where relevant.
38 changes: 38 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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 mentions @claude in a comment, issue, or review
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 # Required for Claude to read CI results on PRs
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 CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ npm install && npm run dev
- Status: green/blue/yellow/red
- Charts: Custom SVG, CSS Grid for layouts
- No emojis in UI

## Code Style
- Always document non-obvious logic changes with comments
3 changes: 3 additions & 0 deletions client/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<router-link to="/orders" :class="{ active: $route.path === '/orders' }">
{{ t('nav.orders') }}
</router-link>
<router-link to="/restocking" :class="{ active: $route.path === '/restocking' }">
Restocking
</router-link>
<router-link to="/spending" :class="{ active: $route.path === '/spending' }">
{{ t('nav.finance') }}
</router-link>
Expand Down
15 changes: 15 additions & 0 deletions client/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,20 @@ export const api = {
async getPurchaseOrderByBacklogItem(backlogItemId) {
const response = await axios.get(`${API_BASE_URL}/purchase-orders/${backlogItemId}`)
return response.data
},

async getRestockingRecommendations() {
const response = await axios.get(`${API_BASE_URL}/restocking/recommendations`)
return response.data
},

async submitRestockingOrder(items, budgetUsed) {
const response = await axios.post(`${API_BASE_URL}/restocking/orders`, { items, budget_used: budgetUsed })
return response.data
},

async getRestockingOrders() {
const response = await axios.get(`${API_BASE_URL}/restocking/orders`)
return response.data
}
}
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
84 changes: 83 additions & 1 deletion client/src/views/Orders.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,49 @@
<div v-if="loading" class="loading">{{ t('common.loading') }}</div>
<div v-else-if="error" class="error">{{ error }}</div>
<div v-else>

<!-- Submitted restocking orders appear at the top so they're immediately visible -->
<div v-if="restockingOrders.length > 0" class="card restocking-section">
<div class="card-header">
<h3 class="card-title">Submitted Orders <span class="section-badge">{{ restockingOrders.length }}</span></h3>
</div>
<div class="table-container">
<table class="orders-table">
<thead>
<tr>
<th class="col-order-number">Order #</th>
<th class="col-items">Items</th>
<th class="col-status">Status</th>
<th class="col-date">Order Date</th>
<th class="col-date">Expected Delivery</th>
<th style="width:110px">Lead Time</th>
<th class="col-value">Total Value</th>
</tr>
</thead>
<tbody>
<tr v-for="order in restockingOrders" :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">{{ order.items.length }} item{{ order.items.length !== 1 ? 's' : '' }}</summary>
<div class="items-dropdown">
<div v-for="item in order.items" :key="item.sku" class="item-entry">
<span class="item-name">{{ item.name }}</span>
<span class="item-meta">Qty: {{ item.quantity }} @ ${{ item.unit_cost }}</span>
</div>
</div>
</details>
</td>
<td class="col-status"><span class="badge warning">Processing</span></td>
<td class="col-date">{{ formatDate(order.order_date) }}</td>
<td class="col-date">{{ formatDate(order.expected_delivery) }}</td>
<td><span class="lead-time">{{ order.lead_time_days }} days</span></td>
<td class="col-value"><strong>${{ order.total_value.toLocaleString() }}</strong></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="stats-grid">
<div class="stat-card success">
<div class="stat-label">{{ t('status.delivered') }}</div>
Expand Down Expand Up @@ -95,6 +138,7 @@ export default {
const loading = ref(true)
const error = ref(null)
const orders = ref([])
const restockingOrders = ref([])

// Use shared filters
const {
Expand All @@ -109,14 +153,20 @@ export default {
try {
loading.value = true
const filters = getCurrentFilters()
const fetchedOrders = await api.getOrders(filters)
const [fetchedOrders, fetchedRestocking] = await Promise.all([
api.getOrders(filters),
api.getRestockingOrders(),
])

// Sort orders by order_date (earliest first)
orders.value = fetchedOrders.sort((a, b) => {
const dateA = new Date(a.order_date)
const dateB = new Date(b.order_date)
return dateA - dateB
})

// Most-recent restocking orders first
restockingOrders.value = fetchedRestocking.slice().reverse()
} catch (err) {
error.value = 'Failed to load orders: ' + err.message
} finally {
Expand Down Expand Up @@ -160,6 +210,7 @@ export default {
loading,
error,
orders,
restockingOrders,
getOrdersByStatus,
getOrderStatusClass,
formatDate,
Expand Down Expand Up @@ -276,4 +327,35 @@ export default {
font-size: 0.813rem;
color: #64748b;
}

.restocking-section {
border-color: #bfdbfe;
background: #fafbff;
}

.restocking-section .card-header {
border-bottom-color: #bfdbfe;
}

.section-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 6px;
background: #2563eb;
color: #fff;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 700;
margin-left: 0.5rem;
vertical-align: middle;
}

.lead-time {
font-size: 0.813rem;
color: #475569;
font-weight: 500;
}
</style>
Loading