- examples/fixture-invoicing/: FastAPI + Vue + Postgres demo (4-entity invoice fixture)
- cfg/sample/: wraps the fixture (managed.repos points at examples/)
- ctrl/kind-{up,down,status}.sh + per-room k8s render in soleprint/ctrl/k8s/
- build.py: relative repo paths, resilient rmtree, optional k8s render hook
- cfg/.gitignore: stop ignoring sample/ and standalone/ template rooms
Manifests render cleanly but kind cluster has not been run end-to-end yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
2.2 KiB
Vue
94 lines
2.2 KiB
Vue
<template>
|
|
<h2>Invoices</h2>
|
|
|
|
<div class="card">
|
|
<h3>Create invoice</h3>
|
|
<div class="actions">
|
|
<input v-model="draft.number" placeholder="Number (e.g. DEMO-2026-004)" />
|
|
<select v-model="draft.customer_id">
|
|
<option :value="null" disabled>Customer…</option>
|
|
<option v-for="c in customers" :key="c.id" :value="c.id">
|
|
{{ c.name }}
|
|
</option>
|
|
</select>
|
|
<button @click="add" :disabled="!draft.number || !draft.customer_id">
|
|
Create
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<table v-if="invoices.length">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Number</th>
|
|
<th>Customer</th>
|
|
<th>Issued</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="inv in invoices" :key="inv.id">
|
|
<td>{{ inv.id }}</td>
|
|
<td>
|
|
<router-link :to="`/invoices/${inv.id}`" class="link">
|
|
{{ inv.number }}
|
|
</router-link>
|
|
</td>
|
|
<td>{{ customerName(inv.customer_id) }}</td>
|
|
<td>{{ inv.issued_at?.slice(0, 10) }}</td>
|
|
<td><span :class="['status', inv.status]">{{ inv.status }}</span></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p v-else class="muted">No invoices yet.</p>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, reactive, computed } from "vue";
|
|
import { api } from "../api.js";
|
|
|
|
const invoices = ref([]);
|
|
const customers = ref([]);
|
|
const draft = reactive({ number: "", customer_id: null });
|
|
|
|
const customerById = computed(() =>
|
|
Object.fromEntries(customers.value.map((c) => [c.id, c]))
|
|
);
|
|
|
|
function customerName(id) {
|
|
return customerById.value[id]?.name || `#${id}`;
|
|
}
|
|
|
|
async function load() {
|
|
[invoices.value, customers.value] = await Promise.all([
|
|
api.listInvoices(),
|
|
api.listCustomers(),
|
|
]);
|
|
}
|
|
|
|
async function add() {
|
|
await api.createInvoice({
|
|
number: draft.number,
|
|
customer_id: draft.customer_id,
|
|
status: "draft",
|
|
});
|
|
draft.number = "";
|
|
draft.customer_id = null;
|
|
await load();
|
|
}
|
|
|
|
onMounted(load);
|
|
</script>
|
|
|
|
<style scoped>
|
|
.actions input, .actions select {
|
|
padding: 8px 10px;
|
|
border: 1px solid #334;
|
|
border-radius: 4px;
|
|
background: #0d1b2a;
|
|
color: var(--text);
|
|
min-width: 200px;
|
|
}
|
|
</style>
|