Commit Graph

117 Commits

Author SHA1 Message Date
Ulas c59338fe2c chore: repository hygiene — P2 cleanup
- .gitignore: add coverage/ and data/ patterns
- .dockerignore: exclude screenshots, tests, scripts, .github, docs
  assets from build context for faster Docker builds
- Delete docs/social-preview.html (one-time generator, no longer needed)
- Delete public/locales/.gitkeep (directory has de.json and en.json)
- scripts/seed-demo.js: replace hardcoded absolute path with portable
  resolve(__dirname, '..', 'data', 'oikos.db') default
- Add .github/PULL_REQUEST_TEMPLATE.md with summary, changes, checklist

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:19:27 +02:00
Ulas ef51c43032 fix: meals drag & drop crash — destructure dragging before cleanup()
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>
2026-04-01 10:11:24 +02:00
Ulas be8801aef7 fix: proxy weather icons through server to fix PWA standalone on Android
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>
2026-04-01 09:57:48 +02:00
Ulas af69431eac fix: weather icons in PWA + locale picker dropdown
- SW: skip cross-origin asset requests (opaque responses caused weather
  icons to break in PWA standalone mode on Android)
- Replace oikos-locale-picker radio buttons with <select> dropdown
- Add settings.localeLabel i18n key (de + en)
- Bump SW cache to v22

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:42:06 +02:00
Ulas cd9f26911b fix: improve PWA manifest and SW cache for Android standalone mode
- Add id field and display_override to manifest.json for reliable
  Chrome Android PWA recognition
- Serve manifest.json with application/manifest+json MIME type
- Add /i18n.js and locale files to SW app shell cache (were missing)
- Bump SW cache version to v21

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:07:57 +02:00
Ulas dd940ab05d fix: replace placeholder icons with house logo for PWA install
Android was showing only a blue circle because maskable icons had no
visible content after the adaptive icon mask was applied. All icons now
use the actual Oikos house logo from docs/logo.svg. Maskable variants
use full-bleed background with logo within the 80% safe zone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:02:55 +02:00
Ulas 752d0f919f fix: resolve nested i18n keys via dot notation in t()
The locale JSON files use nested structure (e.g. {"nav":{"tasks":"…"}}),
but t() did a flat lookup, always falling back to the raw key string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:47:59 +02:00
Ulas 431d6af356 feat: add oikos-locale-picker component and language settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:29:13 +02:00
Ulas 26a8434369 fix: replace hardcoded Uhr suffix and use getLocale() in budget 2026-03-31 23:27:44 +02:00
Ulas 66a9bdfa44 feat: replace manual date formatting with formatDate/formatTime from i18n
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:24:21 +02:00
Ulas 3aefca0a44 feat: i18n navigation labels
Replace all hardcoded German strings in router.js (navItems labels,
aria-labels, skip-link, error/toast messages) with t() calls. Add a
locale-changed event listener that re-renders sidebar and bottom-nav
items on language switch.
2026-03-31 23:19:05 +02:00
Ulas fe70cbf979 feat: i18n web components with locale-changed listener
Replace hardcoded German strings in modal.js and oikos-install-prompt.js
with t() calls; wire locale-changed event listener for live re-render on
locale switch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:15:51 +02:00
Ulas a21fe6afdd fix: replace hardcoded Fehler fallbacks with t(common.unknownError)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:13:41 +02:00
Ulas 29e334c114 fix: i18n contact categories and budget month names 2026-03-31 23:07:09 +02:00
Ulas 26bbd61e1d feat: i18n notes, contacts, budget, settings pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 22:57:45 +02:00
Ulas e6c6b0a4fc feat: add missing calendar keys to locales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 22:52:51 +02:00
Ulas 2f89e623b2 feat: i18n shopping, meals, calendar pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 22:50:16 +02:00
Ulas 752f6ee24d feat: add missing tasks keys to locales 2026-03-31 22:33:54 +02:00
Ulas f6a4879dd0 feat: i18n login, dashboard, tasks pages 2026-03-31 22:31:57 +02:00
Ulas af8f9ccb56 feat: initialize i18n before first route render 2026-03-31 21:53:14 +02:00
Ulas ad921e1637 feat: add de.json and en.json locale files
Extracts all German UI strings from public/pages/, public/components/,
and public/router.js into 482 i18n keys organized by module prefix.
English translations added for all keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 21:51:18 +02:00
Ulas 9bf8f2abbb fix: improve i18n robustness (null guards, race condition, error resilience) 2026-03-31 21:43:55 +02:00
Ulas 3bec77db3b feat: add i18n module (public/i18n.js) 2026-03-31 21:41:20 +02:00
Ulas 3df20fc78b chore: remove unused files and duplicate assets
Delete orphaned Python screenshot generators, social-preview template,
and duplicate icon files in public/assets/ (superseded by public/icons/).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 17:46:56 +02:00
Ulas fe41f84a62 fix: add missing toolbar stripe to meals and --module-accent to settings 2026-03-31 15:14:21 +02:00
Ulas c6551166a9 fix: add missing dark-mode overrides for shopping, notes, contacts, budget, settings module tokens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:10:42 +02:00
Ulas 6f22ef8268 feat: list items get module accent border-left stripe
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:00:41 +02:00
Ulas 4ac9cd16d1 fix: add missing --module-accent declaration to calendar-page 2026-03-31 14:58:16 +02:00
Ulas 3bf2d2bbc0 feat: toolbar top-border stripe uses module accent colour 2026-03-31 14:54:10 +02:00
Ulas 7f41ee380a refactor: move --active-module-accent update into navigate() to avoid duplicate ROUTES.find
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:31:57 +02:00
Ulas 6a018867e0 feat: active nav tab uses module accent colour 2026-03-31 13:22:16 +02:00
Ulas cd017c4d0d fix: escape user input in shopping renderItem to prevent XSS 2026-03-31 12:53:00 +02:00
Ulas 0e035af492 feat: swipe gestures on shopping list items (toggle + delete) 2026-03-31 12:49:29 +02:00
Ulas 33bef8eb3f feat: wrap shopping items in swipe-row 2026-03-31 12:47:37 +02:00
Ulas dc2e874cb2 feat: add shopping swipe CSS (delete reveal, mobile button hide) 2026-03-31 12:46:26 +02:00
Ulas c74f482a0e refactor: move shared swipe CSS from tasks.css to layout.css
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 12:43:35 +02:00
Ulas fd94c58fea fix: hide FAB when virtual keyboard is open on mobile
Uses visualViewport resize event to detect keyboard state (viewport height
< 75% of window height). Sets body.keyboard-visible class; CSS hides
.fab and .page-fab via visibility:hidden on screens < 1024px.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 12:26:59 +02:00
Ulas 9446173247 fix: toast notifications no longer overlap bottom nav on mobile
Introduced --nav-bottom-height token (56px scroll + 12px dots indicator = 68px)
so that toast-container bottom and app-content padding-bottom both account for
the full nav-bottom height including the page-dots indicator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 12:24:12 +02:00
Ulas 4fe4f6cb38 feat: BL-07–BL-10 — notes search, weather refresh, vCard import/export, PWA offline page
- Notes: client-side full-text search bar (filters title + content)
- Dashboard: weather refresh button + 30-min auto-refresh interval
- Contacts: vCard 3.0 export per contact (GET /:id/vcard); vCard import
  via file input with client-side parser (FN, TEL, EMAIL, ADR, NOTE, CATEGORIES)
- PWA: /offline.html served when network unavailable; cached in app-shell (sw v20)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:35:03 +02:00
Ulas d866d32336 feat: Apple CalDAV credentials form + connect/disconnect UI (BL-04)
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>
2026-03-31 10:27:07 +02:00
Ulas 6fd209ba5e feat: meals drag & drop between slots and days (BL-03)
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>
2026-03-31 10:23:39 +02:00
Ulas 6a860f2c13 feat(calendar): expand recurring events in GET /calendar and /upcoming
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>
2026-03-31 10:17:39 +02:00
Ulas 82e5b2cd85 feat(budget): auto-generate recurring entry instances per month
Adds schema migration v3 (recurrence_parent_id column + budget_recurrence_skipped
table). On every GET /api/v1/budget, the server checks all recurring originals
(is_recurring=1, no parent) and creates missing instances for the requested month
using the same day-of-month (clamped to the last day). Deleted instances are
recorded in budget_recurrence_skipped so they are not recreated on the next visit.
Generated instances are shown with a ↩ indicator in the transaction list.

Closes BL-05.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 10:13:37 +02:00
Ulas 26d3d12a22 feat(budget): add month-over-month comparison to summary cards
Each summary card (Einnahmen, Ausgaben, Saldo) now shows a trend line
comparing the current month to the previous one. The previous month's
summary is fetched in parallel via the existing /budget/summary endpoint,
so there is no extra round-trip latency. Positive deltas render in green
(▲), negative in red (▼), unchanged in neutral grey (—).

Closes BL-02.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 22:54:04 +02:00
Ulas 3e25339c86 fix: resolve event-listener leaks and CSS gaps found in code quality audit
- notes.js (Critical): move grid click listener from renderGrid() to
  render() — was re-registered on every save/pin/delete, causing
  multiple API calls per user action after several interactions
- dashboard.js (Major): introduce AbortController (_fabController) so
  the anonymous document click listener from initFab() is cancelled on
  each new render() cycle; also remove the redundant initFab() call on
  the skeleton render
- layout.css (Major): extend .label selector to include .form-label,
  covering usage in notes.js and settings.js without a mass-rename
- test-modal-utils.js (Major): 12 unit tests for wireBlurValidation,
  btnSuccess, btnError; registered as test:modal-utils in package.json
- notes.js (Minor): add btnError() shake feedback to save error handler
- calendar.js (Minor): add popup.isConnected guard to closePopup so
  the listener self-removes correctly after navigation without a click

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 22:26:49 +02:00
Ulas 36de5fa477 feat: merge UX polish v0.2.0 (14 tasks, 4 layers) 2026-03-30 22:02:03 +02:00
Ulas 0ac2769fac feat: blur-triggered inline validation and submit button feedback
Task 13: wireBlurValidation() activates error/valid state on required
fields after blur. Task 14: btnSuccess() shows a checkmark for 700ms
then closes the modal; btnError() triggers a shake animation on failure.
Both wired into the tasks form submit handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:22:36 +02:00
Ulas 1684215da8 feat: bottom sheet modal on mobile with swipe-to-close and Enter-key form navigation
- Add sheet drag handle (::before pseudo-element) and closing animation (sheet-out keyframe) for mobile < 768px in layout.css
- Add prefers-reduced-motion support disabling all modal animations
- Refactor closeModal() to extract _doClose() and play slide-out animation on mobile before removing the overlay
- Add _wireSheetSwipe() for touch drag-to-dismiss (threshold 80px)
- Extend trapFocus() Enter handler: advances focus through inputs/selects and triggers primary button on last field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:19:25 +02:00
Ulas b9ec36611d feat: consistent vibration feedback via vibrate() utility across modules
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 21:18:44 +02:00
Ulas 499ce2cd29 feat: delay install prompt, reset counter on dismiss, scroll inputs into view
- Install prompt now waits for 2 interactions before showing
- Dismiss duration reduced from 30d to 7d
- Interaction counter reset on dismiss (clean slate after 7d)
- Virtual keyboard: focused inputs scroll into view in modals (300ms delay)
2026-03-30 21:16:36 +02:00