2026-03-23 16:16:45 -05:00
|
|
|
import { createPart, recordAdjustment } from "@/lib/actions";
|
|
|
|
|
import { formatCurrency } from "@/lib/format";
|
|
|
|
|
import { getParts } from "@/lib/repository";
|
|
|
|
|
|
|
|
|
|
export default function PartsPage() {
|
|
|
|
|
const parts = getParts();
|
2026-03-23 17:16:47 -05:00
|
|
|
const activeItems = parts.filter((part) => part.kind === "part" || part.kind === "assembly");
|
2026-03-23 16:16:45 -05:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="grid">
|
|
|
|
|
<section className="two-up">
|
|
|
|
|
<article className="panel">
|
|
|
|
|
<h2 className="section-title">Create Part or Assembly</h2>
|
|
|
|
|
<p className="section-copy">Maintain a single item master for stocked components and sellable kits.</p>
|
|
|
|
|
<form action={createPart} className="form-grid">
|
|
|
|
|
<div className="form-row"><label htmlFor="sku">SKU</label><input className="input" id="sku" name="sku" required /></div>
|
|
|
|
|
<div className="form-row"><label htmlFor="name">Name</label><input className="input" id="name" name="name" required /></div>
|
|
|
|
|
<div className="form-row"><label htmlFor="description">Description</label><textarea className="textarea" id="description" name="description" /></div>
|
|
|
|
|
<div className="form-row">
|
|
|
|
|
<label htmlFor="kind">Item Type</label>
|
|
|
|
|
<select className="select" id="kind" name="kind" defaultValue="part">
|
|
|
|
|
<option value="part">Part</option>
|
|
|
|
|
<option value="assembly">Assembly</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="form-row"><label htmlFor="unitCost">Unit Cost</label><input className="input" id="unitCost" name="unitCost" type="number" min="0" step="0.01" defaultValue="0" /></div>
|
|
|
|
|
<div className="form-row"><label htmlFor="salePrice">Sale Price</label><input className="input" id="salePrice" name="salePrice" type="number" min="0" step="0.01" defaultValue="0" /></div>
|
|
|
|
|
<div className="form-row"><label htmlFor="reorderPoint">Reorder Point</label><input className="input" id="reorderPoint" name="reorderPoint" type="number" min="0" step="0.01" defaultValue="0" /></div>
|
|
|
|
|
<div className="form-row"><label htmlFor="unitOfMeasure">Unit of Measure</label><input className="input" id="unitOfMeasure" name="unitOfMeasure" defaultValue="ea" /></div>
|
|
|
|
|
<button className="button" type="submit">Save Item</button>
|
|
|
|
|
</form>
|
|
|
|
|
</article>
|
|
|
|
|
<article className="panel">
|
|
|
|
|
<h2 className="section-title">Inventory Adjustment</h2>
|
2026-03-23 17:16:47 -05:00
|
|
|
<p className="section-copy">Apply opening balances, cycle-count corrections, or manual stock adjustments. Start typing a SKU or item name to filter the inventory list.</p>
|
2026-03-23 16:16:45 -05:00
|
|
|
<form action={recordAdjustment} className="form-grid">
|
2026-03-23 17:16:47 -05:00
|
|
|
<div className="form-row">
|
|
|
|
|
<label htmlFor="adjust-sku">Inventory Item</label>
|
|
|
|
|
<input className="input" id="adjust-sku" name="sku" list="inventory-adjustment-items" placeholder="Type SKU or item name" required />
|
|
|
|
|
<datalist id="inventory-adjustment-items">
|
|
|
|
|
{activeItems.map((item) => (
|
|
|
|
|
<option key={item.id} value={item.sku}>
|
|
|
|
|
{item.name}
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</datalist>
|
|
|
|
|
</div>
|
2026-03-23 16:16:45 -05:00
|
|
|
<div className="form-row"><label htmlFor="quantityDelta">Quantity Delta</label><input className="input" id="quantityDelta" name="quantityDelta" type="number" step="0.01" required /></div>
|
|
|
|
|
<div className="form-row"><label htmlFor="adjust-unit-cost">Unit Cost</label><input className="input" id="adjust-unit-cost" name="unitCost" type="number" min="0" step="0.01" defaultValue="0" /></div>
|
|
|
|
|
<div className="form-row"><label htmlFor="notes">Notes</label><textarea className="textarea" id="notes" name="notes" /></div>
|
|
|
|
|
<button className="button secondary" type="submit">Post Adjustment</button>
|
|
|
|
|
</form>
|
|
|
|
|
</article>
|
|
|
|
|
</section>
|
|
|
|
|
<section className="panel">
|
|
|
|
|
<h2 className="section-title">Item Master</h2>
|
|
|
|
|
<div className="table-wrap">
|
|
|
|
|
<table className="table">
|
|
|
|
|
<thead><tr><th>SKU</th><th>Name</th><th>Type</th><th>On Hand</th><th>Reorder</th><th>Cost</th><th>Price</th></tr></thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{parts.length === 0 ? (
|
|
|
|
|
<tr><td colSpan={7} className="muted">Add your first part or assembly to get started.</td></tr>
|
|
|
|
|
) : (
|
|
|
|
|
parts.map((part) => (
|
|
|
|
|
<tr key={part.id}>
|
|
|
|
|
<td>{part.sku}</td><td>{part.name}</td><td>{part.kind}</td><td>{part.quantityOnHand}</td><td>{part.reorderPoint}</td><td>{formatCurrency(part.unitCost)}</td><td>{formatCurrency(part.salePrice)}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
))
|
|
|
|
|
)}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|