fix(shopping): requireOnline-Guards + 2-space indent
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 3m14s
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 3m14s
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
export function formatQuantity(q: number | null): string {
|
export function formatQuantity(q: number | null): string {
|
||||||
if (q === null || q === undefined) return '';
|
if (q === null || q === undefined) return '';
|
||||||
const rounded = Math.round(q);
|
const rounded = Math.round(q);
|
||||||
if (Math.abs(q - rounded) < 0.01) return String(rounded);
|
if (Math.abs(q - rounded) < 0.01) return String(rounded);
|
||||||
// auf max. 2 Nachkommastellen, trailing Nullen raus
|
// auf max. 2 Nachkommastellen, trailing Nullen raus
|
||||||
return q.toFixed(2).replace(/\.?0+$/, '');
|
return q.toFixed(2).replace(/\.?0+$/, '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import type { ShoppingListRow as Row } from '$lib/server/shopping/repository';
|
import type { ShoppingListRow as Row } from '$lib/server/shopping/repository';
|
||||||
import { shoppingCartStore } from '$lib/client/shopping-cart.svelte';
|
import { shoppingCartStore } from '$lib/client/shopping-cart.svelte';
|
||||||
import { confirmAction } from '$lib/client/confirm.svelte';
|
import { confirmAction } from '$lib/client/confirm.svelte';
|
||||||
|
import { requireOnline } from '$lib/client/require-online';
|
||||||
|
|
||||||
let snapshot = $state<ShoppingListSnapshot>({ recipes: [], rows: [], uncheckedCount: 0 });
|
let snapshot = $state<ShoppingListSnapshot>({ recipes: [], rows: [], uncheckedCount: 0 });
|
||||||
let loading = $state(true);
|
let loading = $state(true);
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onToggleRow(row: Row, next: boolean) {
|
async function onToggleRow(row: Row, next: boolean) {
|
||||||
|
if (!requireOnline('Abhaken')) return;
|
||||||
const method = next ? 'POST' : 'DELETE';
|
const method = next ? 'POST' : 'DELETE';
|
||||||
await fetch('/api/shopping-list/check', {
|
await fetch('/api/shopping-list/check', {
|
||||||
method,
|
method,
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onServingsChange(recipeId: number, servings: number) {
|
async function onServingsChange(recipeId: number, servings: number) {
|
||||||
|
if (!requireOnline('Portionen-Aenderung')) return;
|
||||||
await fetch(`/api/shopping-list/recipe/${recipeId}`, {
|
await fetch(`/api/shopping-list/recipe/${recipeId}`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'content-type': 'application/json' },
|
headers: { 'content-type': 'application/json' },
|
||||||
@@ -44,18 +47,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onRemoveRecipe(recipeId: number) {
|
async function onRemoveRecipe(recipeId: number) {
|
||||||
|
if (!requireOnline('Rezept-Entfernung')) return;
|
||||||
await fetch(`/api/shopping-list/recipe/${recipeId}`, { method: 'DELETE' });
|
await fetch(`/api/shopping-list/recipe/${recipeId}`, { method: 'DELETE' });
|
||||||
await load();
|
await load();
|
||||||
void shoppingCartStore.refresh();
|
void shoppingCartStore.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearChecked() {
|
async function clearChecked() {
|
||||||
|
if (!requireOnline('Erledigte entfernen')) return;
|
||||||
await fetch('/api/shopping-list/checked', { method: 'DELETE' });
|
await fetch('/api/shopping-list/checked', { method: 'DELETE' });
|
||||||
await load();
|
await load();
|
||||||
void shoppingCartStore.refresh();
|
void shoppingCartStore.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearAll() {
|
async function clearAll() {
|
||||||
|
if (!requireOnline('Liste leeren')) return;
|
||||||
const ok = await confirmAction({
|
const ok = await confirmAction({
|
||||||
title: 'Einkaufsliste leeren?',
|
title: 'Einkaufsliste leeren?',
|
||||||
message: 'Alle Rezepte und abgehakten Zutaten werden entfernt. Das lässt sich nicht rückgängig machen.',
|
message: 'Alle Rezepte und abgehakten Zutaten werden entfernt. Das lässt sich nicht rückgängig machen.',
|
||||||
|
|||||||
@@ -2,27 +2,27 @@ import { describe, it, expect } from 'vitest';
|
|||||||
import { formatQuantity } from '../../src/lib/quantity-format';
|
import { formatQuantity } from '../../src/lib/quantity-format';
|
||||||
|
|
||||||
describe('formatQuantity', () => {
|
describe('formatQuantity', () => {
|
||||||
it('renders null as empty string', () => {
|
it('renders null as empty string', () => {
|
||||||
expect(formatQuantity(null)).toBe('');
|
expect(formatQuantity(null)).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders whole numbers as integer', () => {
|
it('renders whole numbers as integer', () => {
|
||||||
expect(formatQuantity(400)).toBe('400');
|
expect(formatQuantity(400)).toBe('400');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders near-integer as integer (epsilon 0.01)', () => {
|
it('renders near-integer as integer (epsilon 0.01)', () => {
|
||||||
expect(formatQuantity(400.001)).toBe('400');
|
expect(formatQuantity(400.001)).toBe('400');
|
||||||
expect(formatQuantity(399.999)).toBe('400');
|
expect(formatQuantity(399.999)).toBe('400');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders fractional with up to 2 decimals, trailing zeros trimmed', () => {
|
it('renders fractional with up to 2 decimals, trailing zeros trimmed', () => {
|
||||||
expect(formatQuantity(0.5)).toBe('0.5');
|
expect(formatQuantity(0.5)).toBe('0.5');
|
||||||
expect(formatQuantity(0.333333)).toBe('0.33');
|
expect(formatQuantity(0.333333)).toBe('0.33');
|
||||||
expect(formatQuantity(1.1)).toBe('1.1');
|
expect(formatQuantity(1.1)).toBe('1.1');
|
||||||
expect(formatQuantity(1.1)).toBe('1.1');
|
expect(formatQuantity(1.1)).toBe('1.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles zero', () => {
|
it('handles zero', () => {
|
||||||
expect(formatQuantity(0)).toBe('0');
|
expect(formatQuantity(0)).toBe('0');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user