Initial scaffold for AvtoAmbor parts inventory

SvelteKit 2 + Svelte 4 + adapter-node, SQLite via better-sqlite3 (WAL,
foreign keys on). Bilingual EN/Тоҷикӣ throughout, locale persisted in
localStorage.

Pages: dashboard (totals, low stock, recent movements), parts list with
search and sort, part create/edit, record movement (in/out/adjust with
smart unit-price and adjust-quantity prefill), suppliers list with
inline add.

Schema: categories, suppliers, parts (with _en/_tg name+description
columns, dirams for money), stock_movements with check on movement_type.
On-hand updates are done in JS inside a transaction with the movement
insert.

Dockerized dev: docker compose, named project, bind-mounted data/ for
DB persistence. Seed contains 6 categories, 4 suppliers, 31 realistic
parts (Lada / Nexia / Opel / Toyota bias).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
David Beccue
2026-05-16 07:05:24 +05:00
commit 05be5b03aa
37 changed files with 4617 additions and 0 deletions

View File

@ -0,0 +1,75 @@
<script>
import { t } from '$lib/i18n/store.js';
import { enhance } from '$app/forms';
export let data;
export let form;
$: ({ suppliers } = data);
$: errors = form?.errors ?? {};
$: values = form?.values ?? {};
</script>
<h1>{$t('suppliers.title')}</h1>
{#if suppliers.length === 0}
<p class="muted">{$t('suppliers.no_suppliers')}</p>
{:else}
<table>
<thead>
<tr>
<th>{$t('suppliers.name')}</th>
<th>{$t('suppliers.phone')}</th>
<th>{$t('suppliers.address')}</th>
<th>{$t('suppliers.notes')}</th>
<th></th>
</tr>
</thead>
<tbody>
{#each suppliers as s}
<tr>
<td><strong>{s.name}</strong></td>
<td>{s.phone || $t('common.none')}</td>
<td>{s.address || $t('common.none')}</td>
<td>{s.notes || $t('common.none')}</td>
<td>
<form method="POST" action="?/delete" use:enhance
on:submit={(e) => { if (!confirm($t('suppliers.delete_confirm'))) e.preventDefault(); }}>
<input type="hidden" name="id" value={s.id} />
<button class="danger" type="submit">{$t('common.delete')}</button>
</form>
</td>
</tr>
{/each}
</tbody>
</table>
{/if}
<h2>{$t('suppliers.add')}</h2>
<form class="stack" method="POST" action="?/create" use:enhance>
<label>
{$t('suppliers.name')} *
<input name="name" required value={values.name ?? ''} />
{#if errors.name}<span class="field-error">{$t(errors.name)}</span>{/if}
</label>
<div class="row">
<label>
{$t('suppliers.phone')}
<input name="phone" value={values.phone ?? ''} />
</label>
<label>
{$t('suppliers.address')}
<input name="address" value={values.address ?? ''} />
</label>
</div>
<label>
{$t('suppliers.notes')}
<textarea name="notes">{values.notes ?? ''}</textarea>
</label>
<div>
<button type="submit">{$t('common.add')}</button>
</div>
</form>
<style>
.field-error { color: #8a1f1b; font-size: 0.8rem; }
</style>