zie vorige message
This commit is contained in:
@@ -22,6 +22,7 @@ class AuthController {
|
|||||||
const name = (req.body.name || "").trim();
|
const name = (req.body.name || "").trim();
|
||||||
const email = (req.body.email || "").trim().toLowerCase();
|
const email = (req.body.email || "").trim().toLowerCase();
|
||||||
const password = req.body.password || "";
|
const password = req.body.password || "";
|
||||||
|
const inviteCode = (req.body.household_code || "").trim();
|
||||||
|
|
||||||
if (name.length < 2 || !/^\S+@\S+\.\S+$/.test(email) || password.length < 8) {
|
if (name.length < 2 || !/^\S+@\S+\.\S+$/.test(email) || password.length < 8) {
|
||||||
req.flash("error", "Vul een naam, geldig e-mailadres en een wachtwoord van minimaal 8 tekens in.");
|
req.flash("error", "Vul een naam, geldig e-mailadres en een wachtwoord van minimaal 8 tekens in.");
|
||||||
@@ -45,6 +46,15 @@ class AuthController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const invitedHousehold = inviteCode
|
||||||
|
? HouseholdService.findByInviteCode(inviteCode)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (inviteCode && !invitedHousehold) {
|
||||||
|
req.flash("error", "De huishoudcode bestaat niet.");
|
||||||
|
return res.redirect("/register");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const passwordHash = await bcrypt.hash(
|
const passwordHash = await bcrypt.hash(
|
||||||
password,
|
password,
|
||||||
@@ -73,10 +83,14 @@ class AuthController {
|
|||||||
const userId = result.lastInsertRowid;
|
const userId = result.lastInsertRowid;
|
||||||
|
|
||||||
|
|
||||||
|
if (invitedHousehold) {
|
||||||
|
HouseholdService.addMember(invitedHousehold.id, userId);
|
||||||
|
} else {
|
||||||
HouseholdService.createForUser(
|
HouseholdService.createForUser(
|
||||||
userId,
|
userId,
|
||||||
`${name} huishouden`
|
`${name} huishouden`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
req.session.userId = userId;
|
req.session.userId = userId;
|
||||||
|
|||||||
@@ -54,6 +54,15 @@ class ShoppingListController {
|
|||||||
res.redirect(`/lists/${list.id}`);
|
res.redirect(`/lists/${list.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetItems(req, res) {
|
||||||
|
const list = ShoppingListRepository.findById(req.params.id, req.user.household_id);
|
||||||
|
if (!list) return res.status(404).render("errors/404");
|
||||||
|
ShoppingListRepository.resetItems(list.id);
|
||||||
|
this.broadcastUpdate(req, list.id);
|
||||||
|
req.flash("success", "Alle producten staan weer open.");
|
||||||
|
res.redirect(`/lists/${list.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
deleteItem(req, res) {
|
deleteItem(req, res) {
|
||||||
const list = ShoppingListRepository.findById(req.params.id, req.user.household_id);
|
const list = ShoppingListRepository.findById(req.params.id, req.user.household_id);
|
||||||
if (!list) return res.status(404).json({ error: "Lijst niet gevonden" });
|
if (!list) return res.status(404).json({ error: "Lijst niet gevonden" });
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -47,6 +47,14 @@ body {
|
|||||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
|
|
||||||
|
.list-check-button {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 1.35rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,6 @@
|
|||||||
"background_color": "#ffffff",
|
"background_color": "#ffffff",
|
||||||
"theme_color": "#0066cc",
|
"theme_color": "#0066cc",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
|
||||||
"src": "/icons/icon.svg",
|
|
||||||
"sizes": "any",
|
|
||||||
"type": "image/svg+xml",
|
|
||||||
"purpose": "any"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"src": "/icons/icon.svg",
|
"src": "/icons/icon.svg",
|
||||||
"sizes": "any",
|
"sizes": "any",
|
||||||
|
|||||||
@@ -62,6 +62,14 @@ class ShoppingListRepository {
|
|||||||
`).run(itemId, listId);
|
`).run(itemId, listId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetItems(listId) {
|
||||||
|
return db.prepare(`
|
||||||
|
UPDATE shopping_list_items
|
||||||
|
SET checked = 0
|
||||||
|
WHERE list_id = ?
|
||||||
|
`).run(listId);
|
||||||
|
}
|
||||||
|
|
||||||
deleteItem(listId, itemId) {
|
deleteItem(listId, itemId) {
|
||||||
return db.prepare(`
|
return db.prepare(`
|
||||||
DELETE FROM shopping_list_items WHERE id = ? AND list_id = ?
|
DELETE FROM shopping_list_items WHERE id = ? AND list_id = ?
|
||||||
|
|||||||
@@ -167,6 +167,12 @@ router.post(
|
|||||||
ShoppingListController.toggleItem.bind(ShoppingListController)
|
ShoppingListController.toggleItem.bind(ShoppingListController)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/lists/:id/items/reset",
|
||||||
|
auth,
|
||||||
|
ShoppingListController.resetItems.bind(ShoppingListController)
|
||||||
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/lists/:id/items/:itemId",
|
"/lists/:id/items/:itemId",
|
||||||
auth,
|
auth,
|
||||||
|
|||||||
@@ -3,6 +3,23 @@ const db = require("../config/database");
|
|||||||
|
|
||||||
class HouseholdService {
|
class HouseholdService {
|
||||||
|
|
||||||
|
findByInviteCode(code) {
|
||||||
|
const match = /^HOUSEHOLD_(\d+)$/.exec((code || "").trim().toUpperCase());
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
return db.prepare(
|
||||||
|
"SELECT id FROM households WHERE id = ?"
|
||||||
|
).get(Number.parseInt(match[1], 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
addMember(householdId, userId) {
|
||||||
|
return db.prepare(`
|
||||||
|
INSERT INTO household_members (household_id, user_id, role)
|
||||||
|
VALUES (?, ?, 'MEMBER')
|
||||||
|
`).run(householdId, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
createForUser(userId, name) {
|
createForUser(userId, name) {
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -55,6 +55,13 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="household-code">Huishoudcode (optioneel)</label>
|
||||||
|
<input id="household-code" type="text" class="form-control" name="household_code"
|
||||||
|
placeholder="bijv. HOUSEHOLD_1" autocomplete="off">
|
||||||
|
<div class="form-hint">Vul de code in als je bij een bestaand huishouden wilt aansluiten.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-footer">
|
<div class="form-footer">
|
||||||
|
|
||||||
<button class="btn btn-primary w-100">
|
<button class="btn btn-primary w-100">
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
<label class="form-label">Lid uitnodigen</label>
|
<label class="form-label">Lid uitnodigen</label>
|
||||||
|
|
||||||
<p class="text-secondary small">Delen van deze code met anderen:</p>
|
<p class="text-secondary small">Deel deze code. De ander vult hem in bij het registreren:</p>
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
|
|
||||||
<button class="btn btn-outline-primary" onclick="navigator.clipboard.writeText('HOUSEHOLD_<%= user.household_id %>')">
|
<button class="btn btn-outline-primary" onclick="navigator.clipboard.writeText('HOUSEHOLD_<%= user.household_id %>')">
|
||||||
|
|
||||||
Kopieren
|
Kopiëren
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -1,6 +1,12 @@
|
|||||||
<div class="page-header mb-4">
|
<div class="page-header mb-4">
|
||||||
<a href="/lists" class="btn btn-link">← Terug</a>
|
<a href="/lists" class="btn btn-outline-secondary mb-3">← Terug naar lijsten</a>
|
||||||
<h1><%= list.name %></h1>
|
<div class="d-flex flex-wrap align-items-center justify-content-between gap-3">
|
||||||
|
<h1 class="mb-0"><%= list.name %></h1>
|
||||||
|
<form method="post" action="/lists/<%= list.id %>/items/reset"
|
||||||
|
onsubmit="return confirm('Alle producten weer openzetten?');">
|
||||||
|
<button class="btn btn-outline-primary" type="submit">Alles weer openzetten</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -14,7 +20,9 @@
|
|||||||
<% items.forEach(item => { %>
|
<% items.forEach(item => { %>
|
||||||
<div class="list-group-item d-flex align-items-center">
|
<div class="list-group-item d-flex align-items-center">
|
||||||
<form method="post" action="/lists/<%= list.id %>/items/<%= item.id %>/toggle">
|
<form method="post" action="/lists/<%= list.id %>/items/<%= item.id %>/toggle">
|
||||||
<button class="btn btn-sm me-2" type="submit" aria-label="Product afvinken">
|
<button class="btn btn-sm me-2 list-check-button" type="submit"
|
||||||
|
aria-label="Product <%= item.checked ? 'weer openzetten' : 'afvinken' %>"
|
||||||
|
<% if (item.checked) { %>onclick="return confirm('Product weer openzetten?');"<% } %>>
|
||||||
<%= item.checked ? "☑" : "☐" %>
|
<%= item.checked ? "☑" : "☐" %>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<meta name="theme-color" content="#206bc4">
|
||||||
|
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||||
|
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" href="/icons/icon.svg">
|
||||||
|
|
||||||
|
|
||||||
<title>
|
<title>
|
||||||
<%= title || "PantryHub" %>
|
<%= title || "PantryHub" %>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
<nav class="navbar-nav ms-auto">
|
<nav class="navbar-nav ms-auto">
|
||||||
<a class="nav-link" href="/">Dashboard</a>
|
<a class="nav-link" href="/">Dashboard</a>
|
||||||
<a class="nav-link" href="/lists">Lijsten</a>
|
<a class="nav-link" href="/lists">Lijsten</a>
|
||||||
<a class="nav-link" href="/menu">Weekmenu</a>
|
|
||||||
<a class="nav-link" href="/products">Producten</a>
|
<a class="nav-link" href="/products">Producten</a>
|
||||||
<a class="nav-link" href="/categories">Categorieën</a>
|
<a class="nav-link" href="/categories">Categorieën</a>
|
||||||
<a class="nav-link" href="/household">Huishouden</a>
|
<a class="nav-link" href="/household">Huishouden</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user