EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
Orders.jsx
Go to the documentation of this file.
1import { useEffect, useState } from 'react';
2import { useNavigate } from 'react-router-dom';
3import MainLayout from '../components/Layout/MainLayout';
4// import removed: TenantSelector
5import { apiFetch } from '../lib/api';
6
7export default function Orders() {
8 const token = localStorage.getItem('token');
9 let tenantId = '';
10 if (token) {
11 try {
12 const payload = token.split('.')[1];
13 const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
14 tenantId = decoded.tenant_id || decoded.tenantId || '';
15 } catch {}
16 }
17 const navigate = useNavigate();
18 const [items, setItems] = useState([]);
19 const [loading, setLoading] = useState(true);
20 const [error, setError] = useState(null);
21 const [isMSP, setIsMSP] = useState(false);
22 const [tenantsMap, setTenantsMap] = useState({});
23
24 useEffect(() => {
25 fetchData(tenantId);
26 }, [tenantId]);
27
28 async function fetchData(fetchTenantId = tenantId) {
29 setLoading(true);
30 try {
31 const token = localStorage.getItem('token');
32 let url = '/products/reorder';
33 if (tenantId) {
34 url += `?tenant_id=${tenantId}`;
35 }
36 const res = await apiFetch(url, {
37 headers: { Authorization: `Bearer ${token}` }
38 });
39 if (!res.ok) throw new Error('Failed to load reorder list');
40 const data = await res.json();
41 setItems(data.products || []);
42 setError(null);
43 } catch (err) {
44 console.error(err);
45 setError(err.message || 'Failed to load');
46 } finally {
47 setLoading(false);
48 }
49 }
50
51 useEffect(() => {
52 (async () => {
53 try {
54 const token = localStorage.getItem('token');
55 if (!token) return;
56 const res = await apiFetch('/tenants', { headers: { Authorization: `Bearer ${token}` } });
57 if (res.ok) {
58 setIsMSP(true);
59 const data = await res.json();
60 const map = {};
61 (data.tenants || data || []).forEach(t => { if (t.tenant_id) map[t.tenant_id] = t.name; });
62 setTenantsMap(map);
63 } else {
64 setIsMSP(false);
65 }
66 } catch (e) {
67 // Silently fail - 403 is expected for non-MSP users
68 setIsMSP(false);
69 }
70 })();
71 }, []);
72
73 return (
74 <MainLayout>
75
76 <div className="page-content">
77 <div className="page-header">
78 <div className="header-content">
79 <h2>Orders</h2>
80 <div className="header-actions">
81 <button className="btn" onClick={fetchData}>
82 <span className="material-symbols-outlined">refresh</span>
83 Refresh
84 </button>
85 <button
86 className="btn primary"
87 onClick={() => {
88 if (!items || items.length === 0) return;
89 // For MVP, pass all items to PO form; future: group by supplier
90 const lines = items.map(p => ({
91 product_id: p.product_id,
92 sku: p.sku || p.upc || p.ean || '',
93 name: p.name,
94 description: p.name,
95 needed_qty: p.needed_qty,
96 price_retail: p.price_retail || p.price_ex_tax || 0
97 }));
98 navigate('/purchase-orders/new', { state: { lines } });
99 }}
100 disabled={!items || items.length === 0}
101 >
102 <span className="material-symbols-outlined">local_shipping</span>
103 Create Purchase Order
104 </button>
105 </div>
106 </div>
107 </div>
108 {error && <div className="error-message">{error}</div>}
109 {loading ? (
110 <div className="loading">Loading...</div>
111 ) : (
112 <div>
113 {items.length === 0 ? (
114 <div className="empty-state">
115 <span className="material-symbols-outlined" style={{ fontSize: 48, color: '#888' }}>inventory_2</span>
116 <div style={{ marginTop: 8 }}>All stocked products are above minimum levels.</div>
117 </div>
118 ) : (
119 <table className="data-table">
120 <thead>
121 <tr>
122 {isMSP && <th>Tenant</th>}
123 <th>Name</th>
124 <th>Supplier</th>
125 <th>Current Stock</th>
126 <th>Min Stock</th>
127 <th>Needed Qty</th>
128 <th>Price</th>
129 </tr>
130 </thead>
131 <tbody>
132 {items.map(p => (
133 <tr key={p.product_id}>
134 {isMSP && (
135 <td title={p.tenant_id ? `Tenant #${p.tenant_id}` : ''}>
136 {p.tenant_id === 1 ? 'Root Tenant' : (p.tenant_name || tenantsMap[p.tenant_id] || (p.tenant_id ? `#${p.tenant_id}` : '-'))}
137 </td>
138 )}
139 <td>{p.name}</td>
140 <td>{p.supplier || '-'}</td>
141 <td>{p.stock_quantity}</td>
142 <td>{p.min_stock}</td>
143 <td>
144 <span className="status-badge status-warning">{p.needed_qty}</span>
145 </td>
146 <td>${Number(p.price_retail || p.price_ex_tax || 0).toFixed(2)}</td>
147 </tr>
148 ))}
149 </tbody>
150 </table>
151 )}
152 </div>
153 )}
154 </div>
155 </MainLayout>
156 );
157}