added location field
This commit is contained in:
@ -5,7 +5,7 @@ import { Upload, Plus, AlertCircle, CheckCircle2 } from "lucide-react";
|
|||||||
import * as xlsx from "xlsx";
|
import * as xlsx from "xlsx";
|
||||||
|
|
||||||
export default function AddImportPage() {
|
export default function AddImportPage() {
|
||||||
const [formData, setFormData] = useState({ name: "", qrCodeId: "", quantity: 0, price: 0.0, category: "", description: "" });
|
const [formData, setFormData] = useState({ name: "", qrCodeId: "", quantity: 0, price: 0.0, category: "", location: "", description: "" });
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [status, setStatus] = useState({ type: "", message: "" });
|
const [status, setStatus] = useState({ type: "", message: "" });
|
||||||
const [importStats, setImportStats] = useState(null);
|
const [importStats, setImportStats] = useState(null);
|
||||||
@ -37,7 +37,7 @@ export default function AddImportPage() {
|
|||||||
if (!res.ok) throw new Error(data.error || "Failed to create item");
|
if (!res.ok) throw new Error(data.error || "Failed to create item");
|
||||||
|
|
||||||
setStatus({ type: "success", message: `Successfully added ${data.name}.` });
|
setStatus({ type: "success", message: `Successfully added ${data.name}.` });
|
||||||
setFormData({ name: "", qrCodeId: "", quantity: 0, price: 0.0, category: "", description: "" });
|
setFormData({ name: "", qrCodeId: "", quantity: 0, price: 0.0, category: "", location: "", description: "" });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setStatus({ type: "error", message: err.message });
|
setStatus({ type: "error", message: err.message });
|
||||||
} finally {
|
} finally {
|
||||||
@ -81,6 +81,7 @@ export default function AddImportPage() {
|
|||||||
quantity: parseInt(normalizedRow["quantity"] || normalizedRow["qty"]) || 0,
|
quantity: parseInt(normalizedRow["quantity"] || normalizedRow["qty"]) || 0,
|
||||||
price: parseFloat(normalizedRow["price"] || normalizedRow["cost"]) || 0.0,
|
price: parseFloat(normalizedRow["price"] || normalizedRow["cost"]) || 0.0,
|
||||||
category: normalizedRow["category"] || "",
|
category: normalizedRow["category"] || "",
|
||||||
|
location: normalizedRow["location"] || normalizedRow["bin"] || normalizedRow["aisle"] || "",
|
||||||
description: normalizedRow["description"] || "",
|
description: normalizedRow["description"] || "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -147,10 +148,16 @@ export default function AddImportPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem" }}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>Category</label>
|
<label>Category</label>
|
||||||
<input type="text" name="category" className="input-field" value={formData.category} onChange={handleInputChange} />
|
<input type="text" name="category" className="input-field" value={formData.category} onChange={handleInputChange} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Location</label>
|
||||||
|
<input type="text" name="location" className="input-field" value={formData.location} onChange={handleInputChange} placeholder="e.g. Aisle 4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" className="btn btn-primary mt-4" style={{ width: "100%", justifyContent: "center" }} disabled={loading}>
|
<button type="submit" className="btn btn-primary mt-4" style={{ width: "100%", justifyContent: "center" }} disabled={loading}>
|
||||||
{loading ? "Saving..." : "Add to Inventory"}
|
{loading ? "Saving..." : "Add to Inventory"}
|
||||||
@ -170,7 +177,7 @@ export default function AddImportPage() {
|
|||||||
<input type="file" accept=".xlsx, .xls, .csv" style={{ display: "none" }} onChange={handleFileUpload} disabled={loading} />
|
<input type="file" accept=".xlsx, .xls, .csv" style={{ display: "none" }} onChange={handleFileUpload} disabled={loading} />
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-4" style={{ fontSize: "0.85rem", color: "var(--text-muted)" }}>
|
<div className="mt-4" style={{ fontSize: "0.85rem", color: "var(--text-muted)" }}>
|
||||||
Make sure your file has headers like: <strong>Name, QR ID, Quantity, Price</strong>.
|
Make sure your file has headers like: <strong>Name, QR ID, Quantity, Price, Location</strong>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export async function GET() {
|
|||||||
export async function POST(req) {
|
export async function POST(req) {
|
||||||
try {
|
try {
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
const { qrCodeId, name, quantity, price, category, description } = body;
|
const { qrCodeId, name, quantity, price, category, location, description } = body;
|
||||||
|
|
||||||
const newItem = await prisma.inventoryItem.upsert({
|
const newItem = await prisma.inventoryItem.upsert({
|
||||||
where: { qrCodeId },
|
where: { qrCodeId },
|
||||||
@ -26,6 +26,7 @@ export async function POST(req) {
|
|||||||
quantity: quantity || 0,
|
quantity: quantity || 0,
|
||||||
price: price || 0.0,
|
price: price || 0.0,
|
||||||
category: category || "",
|
category: category || "",
|
||||||
|
location: location || "",
|
||||||
description: description || "",
|
description: description || "",
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
@ -34,6 +35,7 @@ export async function POST(req) {
|
|||||||
quantity: quantity || 0,
|
quantity: quantity || 0,
|
||||||
price: price || 0.0,
|
price: price || 0.0,
|
||||||
category: category || "",
|
category: category || "",
|
||||||
|
location: location || "",
|
||||||
description: description || "",
|
description: description || "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
104
app/page.js
104
app/page.js
@ -11,6 +11,11 @@ export default function Dashboard() {
|
|||||||
const [editingId, setEditingId] = useState(null);
|
const [editingId, setEditingId] = useState(null);
|
||||||
const [editForm, setEditForm] = useState({});
|
const [editForm, setEditForm] = useState({});
|
||||||
|
|
||||||
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState("");
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const itemsPerPage = 10;
|
||||||
|
|
||||||
const fetchItems = async () => {
|
const fetchItems = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
@ -46,16 +51,18 @@ export default function Dashboard() {
|
|||||||
|
|
||||||
const startEdit = (item) => {
|
const startEdit = (item) => {
|
||||||
setEditingId(item.id);
|
setEditingId(item.id);
|
||||||
setEditForm({ name: item.name, category: item.category || "", quantity: item.quantity, price: item.price });
|
setEditForm({ name: item.name, category: item.category || "", location: item.location || "", quantity: item.quantity, price: item.price });
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveEdit = async (id) => {
|
const saveEdit = async (id) => {
|
||||||
try {
|
try {
|
||||||
|
const parsedQty = parseInt(editForm.quantity, 10);
|
||||||
const payload = {
|
const payload = {
|
||||||
id,
|
id,
|
||||||
name: editForm.name,
|
name: editForm.name,
|
||||||
category: editForm.category,
|
category: editForm.category,
|
||||||
quantity: parseInt(editForm.quantity) || 0,
|
location: editForm.location,
|
||||||
|
quantity: isNaN(parsedQty) ? 0 : parsedQty,
|
||||||
price: parseFloat(editForm.price) || 0.0,
|
price: parseFloat(editForm.price) || 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,15 +85,35 @@ export default function Dashboard() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Filter and Paginate Items
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentPage(1);
|
||||||
|
}, [searchQuery, selectedCategory]);
|
||||||
|
|
||||||
|
const uniqueCategories = [...new Set(items.map(item => item.category?.trim() || "Uncategorized"))].filter(c => c).sort();
|
||||||
|
|
||||||
|
const filteredItems = items.filter(item => {
|
||||||
|
const matchesSearch = item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
item.qrCodeId.toLowerCase().includes(searchQuery.toLowerCase());
|
||||||
|
const matchesCategory = selectedCategory === "" || (item.category?.trim() || "Uncategorized") === selectedCategory;
|
||||||
|
return matchesSearch && matchesCategory;
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalPages = Math.max(1, Math.ceil(filteredItems.length / itemsPerPage));
|
||||||
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||||
|
const paginatedItems = filteredItems.slice(startIndex, startIndex + itemsPerPage);
|
||||||
|
|
||||||
const handleExport = () => {
|
const handleExport = () => {
|
||||||
if (items.length === 0) return alert("No items to export");
|
if (filteredItems.length === 0) return alert("No items to export");
|
||||||
const ws = xlsx.utils.json_to_sheet(items.map(i => ({
|
const ws = xlsx.utils.json_to_sheet(filteredItems.map(i => ({
|
||||||
"QR ID": i.qrCodeId,
|
"QR ID": i.qrCodeId,
|
||||||
"Name": i.name,
|
"Name": i.name,
|
||||||
"Quantity": i.quantity,
|
"Quantity": i.quantity,
|
||||||
"Price": i.price,
|
"Price": i.price,
|
||||||
"Category": i.category || "",
|
"Category": i.category || "",
|
||||||
"Description": i.description || ""
|
"Location": i.location || "",
|
||||||
|
"Description": i.description || "",
|
||||||
|
"Last Updated": i.updatedAt ? new Date(i.updatedAt).toLocaleString() : ""
|
||||||
})));
|
})));
|
||||||
const wb = xlsx.utils.book_new();
|
const wb = xlsx.utils.book_new();
|
||||||
xlsx.utils.book_append_sheet(wb, ws, "Inventory");
|
xlsx.utils.book_append_sheet(wb, ws, "Inventory");
|
||||||
@ -133,19 +160,49 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="table-container mt-4">
|
<div className="table-container mt-4">
|
||||||
|
<div className="flex gap-4 mb-4" style={{ alignItems: "center" }}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search by name or QR..."
|
||||||
|
className="input-field"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
className="input-field"
|
||||||
|
value={selectedCategory}
|
||||||
|
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||||
|
style={{ width: "200px" }}
|
||||||
|
>
|
||||||
|
<option value="">All Categories</option>
|
||||||
|
{uniqueCategories.map(cat => (
|
||||||
|
<option key={cat} value={cat}>{cat}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<table className="data-table">
|
<table className="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>QR ID</th>
|
<th>QR ID</th>
|
||||||
<th>Category</th>
|
<th>Category</th>
|
||||||
|
<th>Location</th>
|
||||||
<th>Quantity</th>
|
<th>Quantity</th>
|
||||||
<th>Price</th>
|
<th>Price</th>
|
||||||
|
<th>Last Updated</th>
|
||||||
<th style={{ textAlign: "right" }}>Actions</th>
|
<th style={{ textAlign: "right" }}>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{items.map(item => (
|
{paginatedItems.length === 0 ? (
|
||||||
|
<tr>
|
||||||
|
<td colSpan="8" style={{ textAlign: "center", padding: "2rem", color: "var(--text-muted)" }}>
|
||||||
|
No items match your filters.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : paginatedItems.map(item => (
|
||||||
<tr key={item.id}>
|
<tr key={item.id}>
|
||||||
<td style={{ fontWeight: 500 }}>
|
<td style={{ fontWeight: 500 }}>
|
||||||
{editingId === item.id ? (
|
{editingId === item.id ? (
|
||||||
@ -158,6 +215,11 @@ export default function Dashboard() {
|
|||||||
<input className="input-field" style={{ padding: "0.2rem 0.5rem", width: "100%" }} value={editForm.category} onChange={e => setEditForm({...editForm, category: e.target.value})} />
|
<input className="input-field" style={{ padding: "0.2rem 0.5rem", width: "100%" }} value={editForm.category} onChange={e => setEditForm({...editForm, category: e.target.value})} />
|
||||||
) : (item.category || "-")}
|
) : (item.category || "-")}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{editingId === item.id ? (
|
||||||
|
<input className="input-field" style={{ padding: "0.2rem 0.5rem", width: "100%" }} value={editForm.location} onChange={e => setEditForm({...editForm, location: e.target.value})} />
|
||||||
|
) : (item.location || "-")}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{editingId === item.id ? (
|
{editingId === item.id ? (
|
||||||
<input type="number" className="input-field" style={{ padding: "0.2rem 0.5rem", width: "80px" }} value={editForm.quantity} onChange={e => setEditForm({...editForm, quantity: e.target.value})} />
|
<input type="number" className="input-field" style={{ padding: "0.2rem 0.5rem", width: "80px" }} value={editForm.quantity} onChange={e => setEditForm({...editForm, quantity: e.target.value})} />
|
||||||
@ -172,6 +234,9 @@ export default function Dashboard() {
|
|||||||
<input type="number" step="0.01" className="input-field" style={{ padding: "0.2rem 0.5rem", width: "80px" }} value={editForm.price} onChange={e => setEditForm({...editForm, price: e.target.value})} />
|
<input type="number" step="0.01" className="input-field" style={{ padding: "0.2rem 0.5rem", width: "80px" }} value={editForm.price} onChange={e => setEditForm({...editForm, price: e.target.value})} />
|
||||||
) : `$${Number(item.price).toFixed(2)}`}
|
) : `$${Number(item.price).toFixed(2)}`}
|
||||||
</td>
|
</td>
|
||||||
|
<td className="text-muted" style={{ fontSize: "0.85rem" }}>
|
||||||
|
{item.updatedAt ? new Date(item.updatedAt).toLocaleDateString() + " " + new Date(item.updatedAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}) : "N/A"}
|
||||||
|
</td>
|
||||||
<td style={{ textAlign: "right" }}>
|
<td style={{ textAlign: "right" }}>
|
||||||
<div className="flex" style={{ gap: "0.5rem", justifyContent: "flex-end" }}>
|
<div className="flex" style={{ gap: "0.5rem", justifyContent: "flex-end" }}>
|
||||||
{editingId === item.id ? (
|
{editingId === item.id ? (
|
||||||
@ -199,6 +264,33 @@ export default function Dashboard() {
|
|||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{filteredItems.length > 0 && (
|
||||||
|
<div className="flex justify-between items-center mt-4" style={{ padding: "1rem 0 0 0" }}>
|
||||||
|
<span className="text-muted" style={{ fontSize: "0.9rem" }}>
|
||||||
|
Showing {startIndex + 1} to {Math.min(startIndex + itemsPerPage, filteredItems.length)} of {filteredItems.length} entries
|
||||||
|
</span>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<span className="flex items-center text-muted" style={{ padding: "0 0.5rem", fontSize: "0.9rem" }}>
|
||||||
|
Page {currentPage} of {totalPages}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -65,8 +65,10 @@ export default function PrintPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Printable Sheet Area */}
|
{/* Printable Sheet Area */}
|
||||||
<div className="print-container">
|
{/* Printable Sheet Area */}
|
||||||
{items.map((item, index) => (
|
{Array.from({ length: Math.ceil(items.length / 30) }, (_, pageIndex) => (
|
||||||
|
<div key={pageIndex} className="print-container">
|
||||||
|
{items.slice(pageIndex * 30, (pageIndex + 1) * 30).map((item) => (
|
||||||
<div key={item.id} className="label-cell">
|
<div key={item.id} className="label-cell">
|
||||||
<div className="qr-wrapper">
|
<div className="qr-wrapper">
|
||||||
<QRCode
|
<QRCode
|
||||||
@ -83,6 +85,8 @@ export default function PrintPage() {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
|
||||||
<style>{`
|
<style>{`
|
||||||
/* Standard View Adjustments */
|
/* Standard View Adjustments */
|
||||||
@ -125,7 +129,7 @@ export default function PrintPage() {
|
|||||||
|
|
||||||
.item-name {
|
.item-name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 0.75rem;
|
font-size: 0.55rem;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -147,6 +151,8 @@ export default function PrintPage() {
|
|||||||
body {
|
body {
|
||||||
background-color: white !important;
|
background-color: white !important;
|
||||||
-webkit-print-color-adjust: exact;
|
-webkit-print-color-adjust: exact;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-print, nav, .navbar {
|
.no-print, nav, .navbar {
|
||||||
@ -159,12 +165,13 @@ export default function PrintPage() {
|
|||||||
grid-template-columns: repeat(3, 2.625in);
|
grid-template-columns: repeat(3, 2.625in);
|
||||||
grid-template-rows: repeat(10, 1in);
|
grid-template-rows: repeat(10, 1in);
|
||||||
gap: 0;
|
gap: 0;
|
||||||
padding: 0.5in 0.21975in; /* Top/Bottom 0.5", Left/Right approx 0.22" */
|
padding: 0.5in 0.1250in; /* Top/Bottom 0.5", Left/Right approx 0.1250" */
|
||||||
width: 8.5in;
|
width: 8.5in;
|
||||||
height: 11in;
|
height: 11in;
|
||||||
background: white;
|
background: white;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
break-after:page;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print-container {
|
.print-container {
|
||||||
|
|||||||
@ -150,11 +150,19 @@ export default function ScanPage() {
|
|||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem", marginBottom: "2rem" }}>
|
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem", marginBottom: "2rem" }}>
|
||||||
<div style={{ padding: "1rem", backgroundColor: "rgba(255,255,255,0.05)", borderRadius: "8px" }}>
|
<div style={{ padding: "1rem", backgroundColor: "rgba(255,255,255,0.05)", borderRadius: "8px" }}>
|
||||||
<div className="text-muted" style={{ fontSize: "0.85rem", textTransform: "uppercase" }}>Current Quant.</div>
|
<div className="text-muted" style={{ fontSize: "0.85rem", textTransform: "uppercase" }}>Current Quant.</div>
|
||||||
<div style={{ fontSize: "2rem", fontWeight: "bold" }}>{item.quantity}</div>
|
<div style={{ fontSize: "2rem", fontWeight: "bold", color: "var(--text-main)" }}>{item.quantity}</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ padding: "1rem", backgroundColor: "rgba(255,255,255,0.05)", borderRadius: "8px" }}>
|
<div style={{ padding: "1rem", backgroundColor: "rgba(255,255,255,0.05)", borderRadius: "8px" }}>
|
||||||
<div className="text-muted" style={{ fontSize: "0.85rem", textTransform: "uppercase" }}>Price</div>
|
<div className="text-muted" style={{ fontSize: "0.85rem", textTransform: "uppercase" }}>Location</div>
|
||||||
<div style={{ fontSize: "2rem", fontWeight: "bold" }}>${Number(item.price).toFixed(2)}</div>
|
<div style={{ fontSize: "1.4rem", fontWeight: "600", color: "var(--text-main)", marginTop: "0.3rem" }}>
|
||||||
|
{item.location || "-"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ padding: "1rem", backgroundColor: "rgba(255,255,255,0.05)", borderRadius: "8px", gridColumn: "1 / -1" }}>
|
||||||
|
<div className="text-muted" style={{ fontSize: "0.85rem", textTransform: "uppercase", marginBottom: "0.5rem" }}>Last Updated</div>
|
||||||
|
<div style={{ fontSize: "1.1rem", color: "var(--text-main)" }}>
|
||||||
|
{item.updatedAt ? new Date(item.updatedAt).toLocaleString() : "Never"}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export default function Navigation() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="navbar glass">
|
<nav className="navbar glass">
|
||||||
<div className="nav-brand">InventoryPro</div>
|
<div className="nav-brand">Simpson's Fence Inventory</div>
|
||||||
<ul className="nav-links">
|
<ul className="nav-links">
|
||||||
{navItems.map((item) => (
|
{navItems.map((item) => (
|
||||||
<li key={item.href}>
|
<li key={item.href}>
|
||||||
|
|||||||
@ -18,6 +18,7 @@ model InventoryItem {
|
|||||||
quantity Int @default(0)
|
quantity Int @default(0)
|
||||||
price Float @default(0.0)
|
price Float @default(0.0)
|
||||||
category String?
|
category String?
|
||||||
|
location String?
|
||||||
description String?
|
description String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|||||||
BIN
public/Logo.jpg
Normal file
BIN
public/Logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Reference in New Issue
Block a user