Parts inventory: catalog, search, and stock tracking

FastAPI + SQLite behind a single-page frontend, deployed as a container on
mainserver behind Caddy.

One flexible parts table plus key/value specs so components, filament,
fasteners and tooling share a schema. Categories and locations are nestable
trees whose filters and sidebar counts both roll up through descendants.
Category spec templates pre-fill the properties worth recording for each kind
of part, which is what makes manual entry tolerable. Every quantity change is
logged. Search is FTS5 with prefix matching across names, MPNs, spec values,
tags, category and location.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jay
2026-08-24 09:19:58 -04:00
commit ffe508b7f3
15 changed files with 2330 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="color-scheme" content="dark">
<title>Parts Inventory</title>
<link rel="stylesheet" href="/static/app.css">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><text y='13' font-size='13'>🧰</text></svg>">
</head>
<body>
<section id="login" class="hidden">
<form id="login-form">
<h1>Parts Inventory</h1>
<p>Enter the password to continue.</p>
<input type="password" id="password" autocomplete="current-password" placeholder="Password" required>
<p id="login-error" class="hidden" style="color:var(--bad);margin:10px 0 0"></p>
<button class="primary" type="submit" style="width:100%;margin-top:14px">Unlock</button>
</form>
</section>
<section id="app" class="hidden">
<header>
<span class="brand">🧰 Parts</span>
<input class="search" id="search" type="search" placeholder="Search parts, values, MPN, tags…" autocomplete="off">
<span class="stat" id="stats"></span>
<button class="ghost small mobile-only" id="filters-btn">Filters</button>
<button class="primary" id="add-btn">+ Add</button>
<button class="ghost small" id="manage-btn" title="Categories &amp; locations"></button>
<button class="ghost small hidden" id="logout-btn" title="Log out"></button>
</header>
<div class="layout">
<aside id="sidebar">
<h3>Filters</h3>
<div class="filter" id="filter-all">All parts <span class="count" id="count-all"></span></div>
<div class="filter" id="filter-low">Low stock <span class="count" id="count-low"></span></div>
<h3>Categories</h3>
<div id="category-list"></div>
<h3>Locations</h3>
<div id="location-list"></div>
<h3>Tags</h3>
<div id="tag-list"></div>
</aside>
<main>
<div class="toolbar">
<span class="grow" id="result-summary"></span>
<select id="sort">
<option value="relevance">Best match</option>
<option value="name">Name AZ</option>
<option value="quantity">Quantity, lowest</option>
<option value="updated">Recently updated</option>
<option value="created">Recently added</option>
<option value="location">Location</option>
</select>
</div>
<div id="results"></div>
<div style="text-align:center;margin:14px 0">
<button id="more-btn" class="hidden">Load more</button>
</div>
</main>
</div>
</section>
<div id="modal-root"></div>
<script src="/static/app.js"></script>
</body>
</html>