- Add _pendingLoginRedirect flag in router.js to handle auth:expired
events that fire during active navigation (isNavigating=true).
Previously the navigate('/login') call was silently dropped, leaving
the user stuck on the dashboard with an error toast.
- Bump SHELL_CACHE v33→v34 and PAGES_CACHE v28→v29 to force cache
invalidation on iOS devices still running the pre-CSRF-fix code.
- Add err.status to dashboard error log for easier diagnostics.
When adding ingredients in the meal editor, each ingredient now has a
category dropdown. Categories are stored on the ingredient and applied
automatically when transferring to the shopping list, so items appear
pre-grouped by category without manual re-sorting.
Users can now show/hide widgets and reorder them via a settings button
in the greeting header. Configuration is persisted server-side in
sync_config (dashboard_widgets key) and shared across all family members.
- Greeting widget gets a settings icon button opening a customize modal
- Modal lists all widgets (tasks, calendar, shopping, meals, notes,
weather) with toggle switches and up/down reorder buttons
- Reset to default layout available in the modal
- GET /preferences now returns dashboard_widgets; PUT accepts it
- All 10 locales updated with new i18n keys
Six tabs (General, Meals, Budget, Shopping, Calendar, Account) replace
the flat single-page layout. Active tab persists via sessionStorage.
Calendar tab auto-activates on OAuth redirect. Tab bar is sticky.
All labels translated in de/en/es/it/sv.
- New optional recipe_url field in the meal modal (below Notes)
- Link icon appears on meal cards when a URL is set, opens in new tab
- DB migration v6: ALTER TABLE meals ADD COLUMN recipe_url TEXT
- API: recipe_url supported in POST /meals and PUT /meals/:id
- i18n: new keys recipeUrlLabel, recipeUrlPlaceholder, openRecipe (de, en, sv, it)
- New DB table shopping_categories (migration v5) seeds 9 default
categories with Lucide icons and sort_order
- Backend CRUD routes: GET/POST/PUT/DELETE /shopping/categories
plus PATCH /shopping/categories/reorder
- Category validation now uses DB instead of hardcoded constant;
items of deleted category are moved to the next available one
- Frontend shopping page loads categories from API, dropdown and
grouping reflect custom order dynamically
- Settings -> Shopping section: list categories with up/down buttons,
click-to-rename, delete with confirmation; add new categories inline
- i18n keys added in de/en/sv/it
Adds a small button on each kanban card that cycles the task status
(open → in_progress → done → open) without requiring drag-and-drop.
Useful for touch devices and kiosk browsers (e.g. Fully Kiosk Browser)
where drag-and-drop is unavailable. All four locales updated.
- Shopping category dropdown options now use CATEGORY_LABELS() for
translated display text instead of raw German internal keys
- rrule-ui.js now imports t() from /i18n.js; all hardcoded German
strings (freq options, weekday labels, form labels, unit labels)
replaced with i18n keys under the new 'rrule' namespace
- Added 'rrule' section to de.json and en.json with 22 new keys
Fixes#21
- tasks.js: add maybeShowSwipeHint (long loop, max 3x) - matches shopping.js pattern
- tasks.js: vibrate(15) on task status toggle
- oikos-locale-picker: show disabled/loading state for both reload and setLocale paths
- dashboard: show success toast after weather refresh (all 4 locales)
- dashboard: add semi-transparent FAB backdrop to signal open mode
- Locale-Picker: disable select + fade before location.reload() (system mode)
gives visual feedback before the page jumps
- Extract _validateField() helper from wireBlurValidation to avoid duplication
- Add validateAll(formContainer): validates all required fields on demand,
marks inline errors, focuses first invalid field
- tasks.js: call validateAll() at submit to catch untouched required fields
Add household-wide currency preference for the budget section.
Users can select from 13 currencies (EUR, USD, GBP, SEK, NOK, DKK,
CHF, PLN, CZK, HUF, JPY, AUD, CAD) in Settings → Budget.
- preferences API (GET/PUT) now includes currency field
- budget page loads currency from preferences on render
- formatAmount() uses locale-aware Intl.NumberFormat with chosen currency
- settings page gains a Budget section with a currency select
- all three locales (de, en, it) updated with new i18n keys
- inputmode=decimal on budget amount input for correct decimal keyboard
- inputmode=numeric on rrule interval input for numeric keyboard
- autocomplete attributes on contacts form (name, tel, email, street-address)
Users can now toggle which meal types (breakfast, lunch, dinner, snack)
are displayed in the meal planner via a new Settings section. Preference
is stored household-wide in sync_config and applied as a filter on the
meals page. Includes preferences API, i18n (DE/EN/IT), and Settings UI.
View mode (list/kanban) is now saved to localStorage and restored on
page load. URL parameter ?view=kanban takes precedence, enabling tablet
kiosk setups to default to Kanban view. Toggle buttons reflect the
active view correctly on initial render.
Closes#17
New tasks default to "none" priority instead of "medium". Tasks with no
priority hide the badge in list and dashboard views, reducing visual noise
for routine items. Includes DB migration v4 and i18n keys (de, en, it).
Closes#15
Native browser prompt() is unreliable on mobile browsers and PWAs,
often requiring multiple clicks to close. Replace all prompt() calls
with custom promptModal() and selectModal() functions that use the
existing modal system with proper focus management and animations.
Affected pages: shopping (create/rename list), tasks (add subtask),
meals (choose shopping list).
Fixes#12
Show shopping lists with open items directly on the dashboard.
Each list displays a progress bar, the first few unchecked items,
and a "+N more" overflow indicator. Widget only appears when there
are lists with open items.
Backend: new shoppingLists query in /api/v1/dashboard (up to 3 lists,
6 open items each). Frontend: renderShoppingLists() widget following
existing widget pattern. CSS: compact list/progress/item styles.
i18n: shoppingMore key added to de/en/it.
Requested in discussion #9
Category group headers in tasks and bar chart labels / transaction meta
in budget were showing raw German database keys instead of going through
CATEGORY_LABELS() i18n mapping.
Closes#11
- Rename #page-content to #main-content so skip-to-content link
targets the semantic <main> landmark
- Add sr-only priority labels to dashboard task items for screen
readers (WCAG 1.4.1 color-not-only)
- Replace hardcoded hex in greeting gradient with accent tokens
so dark mode themes the banner correctly
- Replace hardcoded gap: 2px with --space-0h token
- Bump version to 0.7.2
- Extract shared esc() utility (public/utils/html.js) replacing 8
duplicate escHtml() functions across all page modules
- Apply HTML escaping to all user-controlled data in innerHTML
templates: titles, names, locations, descriptions, colors, notes
content, weather data, autocomplete suggestions
- Remove user-scalable=no and maximum-scale=1 from viewport meta
tag, restoring pinch-to-zoom for WCAG 1.4.4 compliance
- Bump version to 0.7.1
Fix stored XSS in tasks (titles/subtasks) and settings (member list)
by applying escHtml(). Harden trust proxy to loopback default, add
OAuth state parameter for Google Calendar CSRF protection, sanitize
CSV export against formula injection, invalidate sessions on user
deletion, restrict usernames to alphanumeric chars, and require admin
role for calendar sync triggers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all — with - in all source files (JS, CSS, HTML, JSON,
Markdown) for consistency and readability.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cleanup() set dragging = null, then onUp accessed dragging.slot,
.mealId, .sourceDate, .sourceType on the now-null reference.
Fix: destructure all needed values before calling cleanup().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
External image requests to openweathermap.org fail silently in Chrome
Android PWA standalone mode. Icons are now proxied via
GET /api/v1/weather/icon/:code, making them same-origin — cacheable by
the service worker and free of CORS/CSP issues.
Tightened CSP: removed openweathermap.org from imgSrc (no longer needed).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Admin can now enter CalDAV URL, Apple-ID and app-specific password
directly in Settings; credentials are tested live before saving and
stored in sync_config (take precedence over .env); disconnect clears
DB-stored credentials without server restart. Auto-sync interval
(15 min, configurable via SYNC_INTERVAL_MINUTES) was already in place.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pointer Events-based drag & drop (touch + mouse compatible):
- Ghost element follows pointer; drops on empty slots move the meal,
drops on occupied slots swap both meals via concurrent PUT requests
- prefers-reduced-motion: no ghost animation, interaction still works
- Suppress-click guard prevents accidental edit modal after drag
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
expandRecurringEvents() iterates from the event's original start date,
generating all occurrences within the requested window using the existing
nextOccurrence() service (max 1000 iterations). The SQL query is extended
to also fetch recurring events that started before the window. Event
duration is preserved across instances. Virtual instances carry
is_recurring_instance=1 and are shown with a repeat icon in the agenda
view. /upcoming expands across a 90-day forward window.
Closes BL-01.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>