Applicatie afmaken en GUI-flow herstellen
Voegt boodschappenlijsten, productbeheer, realtime synchronisatie en browsercache-updates toe. Herstelt login- en sessiegedrag en maakt database-seeding herhaalbaar.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -52,6 +52,10 @@ Inloggen
|
||||
|
||||
</form>
|
||||
|
||||
<p class="text-secondary mt-3">
|
||||
Nog geen account? <a href="/register">Registreren</a>
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
<div class="container-xl">
|
||||
|
||||
<%- include("../partials/flash") %>
|
||||
<%- body %>
|
||||
|
||||
</div>
|
||||
@@ -36,7 +37,8 @@
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/js/tabler.min.js"></script>
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<div class="page-header mb-4">
|
||||
<h1>Boodschappenlijsten</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<% if (lists.length === 0) { %>
|
||||
<p class="text-secondary">Je hebt nog geen boodschappenlijsten.</p>
|
||||
<% } else { %>
|
||||
<div class="list-group">
|
||||
<% lists.forEach(list => { %>
|
||||
<a href="/lists/<%= list.id %>" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between">
|
||||
<strong><%= list.name %></strong>
|
||||
<span><%= list.checked_count || 0 %>/<%= list.item_count || 0 %> gedaan</span>
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
Aangemaakt <%= new Date(list.created_at).toLocaleDateString("nl-NL") %>
|
||||
</small>
|
||||
</a>
|
||||
<% }); %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title">Nieuwe lijst</h3></div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="/lists">
|
||||
<label class="form-label" for="list-name">Lijstnaam</label>
|
||||
<input id="list-name" class="form-control mb-3" type="text" name="name"
|
||||
placeholder="bijv. Weekboodschappen" required maxlength="120">
|
||||
<button class="btn btn-primary w-100" type="submit">Lijst aanmaken</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
<div class="page-header mb-4">
|
||||
<a href="/lists" class="btn btn-link">← Terug</a>
|
||||
<h1><%= list.name %></h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<% if (items.length === 0) { %>
|
||||
<p class="text-secondary">Nog geen producten op de lijst.</p>
|
||||
<% } else { %>
|
||||
<div class="list-group">
|
||||
<% items.forEach(item => { %>
|
||||
<div class="list-group-item d-flex align-items-center">
|
||||
<form method="post" action="/lists/<%= list.id %>/items/<%= item.id %>/toggle">
|
||||
<button class="btn btn-sm me-2" type="submit" aria-label="Product afvinken">
|
||||
<%= item.checked ? "☑" : "☐" %>
|
||||
</button>
|
||||
</form>
|
||||
<div class="flex-grow-1">
|
||||
<strong<% if (item.checked) { %> style="text-decoration: line-through"<% } %>>
|
||||
<%= item.product_name %>
|
||||
</strong>
|
||||
<% if (item.amount !== 1) { %><span class="badge bg-info"><%= item.amount %></span><% } %>
|
||||
<div class="small text-secondary"><%= item.category_icon || "" %> <%= item.category_name || "Geen categorie" %></div>
|
||||
</div>
|
||||
<form class="delete-item-form" method="post" action="/lists/<%= list.id %>/items/<%= item.id %>">
|
||||
<button class="btn btn-sm btn-link text-danger" type="submit">Verwijder</button>
|
||||
</form>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title">Product toevoegen</h3></div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="/lists/<%= list.id %>/items">
|
||||
<label class="form-label" for="product-id">Product</label>
|
||||
<select id="product-id" class="form-select mb-3" name="product_id" required>
|
||||
<option value="">Kies een product</option>
|
||||
<% (products || []).forEach(product => { %>
|
||||
<option value="<%= product.id %>"><%= product.name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
<label class="form-label" for="amount">Hoeveelheid</label>
|
||||
<input id="amount" class="form-control mb-3" type="number" name="amount"
|
||||
value="1" min="0.5" step="0.5" required>
|
||||
<button class="btn btn-primary w-100" type="submit">Toevoegen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.querySelectorAll(".delete-item-form").forEach((form) => {
|
||||
form.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
if (!confirm("Product verwijderen?")) return;
|
||||
const response = await fetch(form.action, { method: "DELETE" });
|
||||
if (response.ok) window.location.reload();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,12 @@
|
||||
<% if (success && success.length) { %>
|
||||
<div class="alert alert-success" role="alert"><%= success[0] %></div>
|
||||
<% } %>
|
||||
<% if (error && error.length) { %>
|
||||
<div class="alert alert-danger" role="alert"><%= error[0] %></div>
|
||||
<% } %>
|
||||
<% if (info && info.length) { %>
|
||||
<div class="alert alert-info" role="alert"><%= info[0] %></div>
|
||||
<% } %>
|
||||
<% if (warning && warning.length) { %>
|
||||
<div class="alert alert-warning" role="alert"><%= warning[0] %></div>
|
||||
<% } %>
|
||||
@@ -12,6 +12,12 @@
|
||||
href="https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/css/tabler.min.css"
|
||||
rel="stylesheet">
|
||||
|
||||
<script>
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register("/service-worker.js", { updateViaCache: "none" });
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<link
|
||||
href="/css/style.css"
|
||||
|
||||
@@ -8,6 +8,16 @@
|
||||
|
||||
</a>
|
||||
|
||||
<% if (user) { %>
|
||||
<div class="navbar-nav flex-row ms-auto">
|
||||
<a class="nav-link" href="/">Dashboard</a>
|
||||
<a class="nav-link" href="/lists">Lijsten</a>
|
||||
<a class="nav-link" href="/products">Producten</a>
|
||||
<a class="nav-link" href="/household">Huishouden</a>
|
||||
<a class="nav-link text-danger" href="/logout">Uitloggen</a>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+39
-57
@@ -8,64 +8,46 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<% if (products.length === 0) { %>
|
||||
<p class="text-secondary">Nog geen producten.</p>
|
||||
<% } else { %>
|
||||
<div class="list-group">
|
||||
<% products.forEach(product => { %>
|
||||
<div class="list-group-item">
|
||||
<strong><%= product.category_icon || "" %> <%= product.name %></strong>
|
||||
<div class="text-secondary"><%= product.category_name || "Geen categorie" %></div>
|
||||
</div>
|
||||
<% }); %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title">Nieuw product</h3></div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="/products">
|
||||
<label class="form-label" for="product-name">Productnaam</label>
|
||||
<input id="product-name" class="form-control mb-3" type="text" name="name"
|
||||
placeholder="bijv. Melk" maxlength="120" required>
|
||||
|
||||
<div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
|
||||
<% if(products.length === 0) { %>
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
Nog geen producten.
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<% } %>
|
||||
|
||||
|
||||
|
||||
<div class="list-group">
|
||||
|
||||
|
||||
<% products.forEach(product => { %>
|
||||
|
||||
|
||||
<div class="list-group-item">
|
||||
|
||||
|
||||
<strong>
|
||||
|
||||
<%= product.name %>
|
||||
|
||||
</strong>
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
<span class="text-secondary">
|
||||
|
||||
<%= product.category_icon || "" %>
|
||||
|
||||
<%= product.category_name || "Geen categorie" %>
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<% }) %>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<label class="form-label" for="category-id">Categorie</label>
|
||||
<select id="category-id" class="form-select mb-3" name="category_id">
|
||||
<option value="">Geen categorie</option>
|
||||
<% (categories || []).forEach(category => { %>
|
||||
<option value="<%= category.id %>"><%= category.icon || "" %> <%= category.name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
|
||||
<button class="btn btn-primary w-100" type="submit">Product toevoegen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user