v0.15.0 — Inventory depth (HomeBox parity) + Locations page
Rounds out Inventory toward HomeBox, and gives Locations a real home. Inventory: - Attach manuals, receipts, and warranty docs (any file), not just photos — attachment uploader now has a kind picker; WARRANTY kind added - Richer purchase/warranty: purchasedFrom, warrantyDetails, lifetimeWarranty, insured (item dialog + detail facts + header badge) - Schedule recurring maintenance from an item (creates a Task linked via itemId); item detail shows its maintenance with overdue flagged. Task create now accepts itemId/animalId Locations: - New Locations page + nav: browse/add/rename/move/delete the whole place tree (LocationManager over the existing /api/locations routes); shows plant/item counts per place. The spot to merge near-dupes before dropping legacy zone Verified end-to-end against a running server (new fields round-trip, maintenance task links to item, MANUAL attachment stored). tsc + 33 tests + build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,11 @@ export type ItemFields = {
|
||||
serial: string;
|
||||
value: string;
|
||||
purchaseDate: string;
|
||||
purchaseFrom: string;
|
||||
warrantyExpiry: string;
|
||||
warrantyDetails: string;
|
||||
lifetimeWarranty: boolean;
|
||||
insured: boolean;
|
||||
barcode: string;
|
||||
notes: string;
|
||||
labelIds: string[];
|
||||
@@ -42,7 +46,8 @@ export type ItemFields = {
|
||||
export function blankItem(): ItemFields {
|
||||
return {
|
||||
name: "", description: "", quantity: "1", unit: "", locationId: "", parentItemId: "",
|
||||
brand: "", modelNumber: "", serial: "", value: "", purchaseDate: "", warrantyExpiry: "",
|
||||
brand: "", modelNumber: "", serial: "", value: "", purchaseDate: "", purchaseFrom: "",
|
||||
warrantyExpiry: "", warrantyDetails: "", lifetimeWarranty: false, insured: false,
|
||||
barcode: "", notes: "", labelIds: [],
|
||||
};
|
||||
}
|
||||
@@ -109,7 +114,11 @@ function ItemDialog({
|
||||
serial: f.serial.trim() || undefined,
|
||||
value: f.value ? Number(f.value) : null,
|
||||
purchaseDate: f.purchaseDate || null,
|
||||
purchaseFrom: f.purchaseFrom.trim() || undefined,
|
||||
warrantyExpiry: f.warrantyExpiry || null,
|
||||
warrantyDetails: f.warrantyDetails.trim() || undefined,
|
||||
lifetimeWarranty: f.lifetimeWarranty,
|
||||
insured: f.insured,
|
||||
barcode: f.barcode.trim() || undefined,
|
||||
notes: f.notes.trim() || undefined,
|
||||
labelIds: f.labelIds,
|
||||
@@ -197,7 +206,26 @@ function ItemDialog({
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label>Warranty until</Label>
|
||||
<Input type="date" value={f.warrantyExpiry} onChange={(e) => set("warrantyExpiry", e.target.value)} />
|
||||
<Input type="date" value={f.warrantyExpiry} onChange={(e) => set("warrantyExpiry", e.target.value)} disabled={f.lifetimeWarranty} />
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 space-y-1.5">
|
||||
<Label>Purchased from</Label>
|
||||
<Input value={f.purchaseFrom} onChange={(e) => set("purchaseFrom", e.target.value)} placeholder="Home Depot, Amazon, gift…" />
|
||||
</div>
|
||||
<div className="col-span-2 space-y-1.5">
|
||||
<Label>Warranty details</Label>
|
||||
<Textarea rows={2} value={f.warrantyDetails} onChange={(e) => set("warrantyDetails", e.target.value)} placeholder="Coverage, claim info…" />
|
||||
</div>
|
||||
<div className="col-span-2 flex flex-wrap gap-x-6 gap-y-2">
|
||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||
<input type="checkbox" checked={f.lifetimeWarranty} onChange={(e) => set("lifetimeWarranty", e.target.checked)} className="accent-[hsl(var(--leaf))]" />
|
||||
Lifetime warranty
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||
<input type="checkbox" checked={f.insured} onChange={(e) => set("insured", e.target.checked)} className="accent-[hsl(var(--leaf))]" />
|
||||
Insured
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 space-y-1.5">
|
||||
|
||||
Reference in New Issue
Block a user