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:
75
src/routes/suppliers/+page.svelte
Normal file
75
src/routes/suppliers/+page.svelte
Normal 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>
|
||||
Reference in New Issue
Block a user