Commit graph

49 commits

Author SHA1 Message Date
61d677d811 Fix logout not showing login screen
Symfony's logout responds with a redirect, causing fetch to parse HTML
as JSON and reject — .finally() ensures the UI always transitions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 22:55:22 +02:00
ccd05b1660 Extend session lifetime to 7 days
Overrides PHP's default session.gc_maxlifetime (typically 1h on servers)
so sessions survive inactivity without relying on remember-me.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 22:23:38 +02:00
da6eed8803 Allow viewing past days + enforce edit cutoff server-side
- Clicking any past day dot now opens a stats panel (read-only for days older than yesterday)
- Entry form and delete buttons hidden for non-editable days
- Backend silently restores locked offsets (< yesterday) on PATCH, preventing backdated edits
- Negative buffer no longer shows green: badge and progress bar are amber/red when buf < 0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 19:04:36 +02:00
526d851eef Add implementation plan for JS unit tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 19:27:54 +02:00
6b927fc984 Add spec for JS unit tests (goals.js + i18n.js)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 16:59:29 +02:00
157559e9aa Fix cache-buster to hash new entry point
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 16:16:20 +02:00
bbafaabb62 Remove monolithic app.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:46:22 +02:00
49e6398cba Switch to ES module entry point
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 12:46:19 +02:00
d8162c3e2d Add app.js entry point module 2026-05-08 11:48:06 +02:00
b34cd3d402 Add render.js module 2026-05-08 11:46:43 +02:00
c026f54163 Add sheets.js module 2026-05-08 11:43:58 +02:00
b7de2cb300 Add auth.js module 2026-05-08 11:42:08 +02:00
6e54d4c499 Add ui.js module 2026-05-08 11:40:48 +02:00
fa68017d49 Add goals.js module 2026-05-08 11:39:47 +02:00
7f92cc32b7 Add api.js module 2026-05-08 11:38:43 +02:00
01a7a4cb59 Add i18n.js module 2026-05-08 11:36:44 +02:00
f2fb10b753 Add state.js module 2026-05-08 11:34:19 +02:00
eb7cfad6bc Add implementation plan for JS module split
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:32:50 +02:00
d28f87a3c4 Add design spec for JS module split
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 11:22:44 +02:00
11767f147a Always set remember-me cookie on login
Without always_remember_me: true, the RememberMeBadge was never
activated (Symfony default requires an explicit _remember_me field
in the request). Users were logged out after PHP session expiry
(~24min) instead of the intended 48h.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 22:46:59 +02:00
99304a6174 Fix session-expiry redirect not showing login form
When Symfony returns a non-JSON body (empty or HTML) for a 401 on
protected API routes, res.json() throws a SyntaxError with no .status,
so showLogin() was never called. Now the HTTP status is preserved
through JSON parse failures so the 401 check works correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 20:03:21 +02:00
f4b1e81065 Extend remember-me lifetime to 48h
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:48:20 +02:00
7ea6aeb98c Add API loading indicator and session-expiry auto-redirect
- Thin animated bar at page top shows while any fetch is in-flight
- api() centrally redirects to login on 401 (except login endpoint)
- Logo click triggers manual goal refresh
- cursor:pointer on logo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 12:48:01 +02:00
bd0190cfde Always floor buffer value to avoid fractional display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 19:12:35 +02:00
4e1028e1a1 Keep screen awake via Wake Lock API while stopwatch is running
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 09:53:41 +02:00
e9995bb5d2 Change log button label from text to '+' to save space
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:26:35 +02:00
61d07d09b9 Fix Quick-Buchen visibility: always show on negative buffer, hide when buf >= daily
Rules:
- buf < 0 (behind) → always visible to catch up
- buf >= daily (more than a day ahead) → hidden
- otherwise → visible only if today not yet reached daily goal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:26:05 +02:00
88cc05e999 Fix stopwatch fill button not updating addAmt state
Setting inp.value programmatically bypasses the oninput handler that
syncs addAmt[k], so addSet read 0. Firing the input event after fill
ensures the value is registered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:23:41 +02:00
c9e8f69c3f Invite improvements: cap pending at 10, sort by status, hide old expired
- Max 10 pending invites per user (400 if exceeded)
- List sorted: pending → used → expired
- Expired invites hidden after 30 days
- Frontend shows error toast from server message on invite creation failure
- Tests: testInviteMaxTenPending, testInviteListSortOrder, testInviteExpiredHiddenAfter30Days

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:22:16 +02:00
950c3bcfc5 Hide goal from quick-book when buffer covers a full day
If the accumulated buffer (buf) is >= the daily target, the goal is
already covered for today and doesn't need to appear in Quick-Buchen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:13:20 +02:00
462401d41a Add admin access control tests
- 401 for unauthenticated requests
- 403 for authenticated non-admin users
- 200 with full user list for admin (looks up existing ADMIN_EMAIL user,
  skips gracefully if not present in DB)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:11:57 +02:00
ffdc983553 Remove legacy root app.js, update docs
- Delete root app.js (pre-Symfony legacy copy, no longer served anywhere)
- CLAUDE.md: remove sync instruction, drop outdated pending-cleanup items,
  add AdminController to key files, fix i18n note (DE/EN/PL), fix Twig cache note
- docs/api.md: document locale/is_admin in /api/me, PATCH locale, new
  GET /api/admin/users endpoint
- docs/structure.md: add AdminController, clean up legacy table descriptions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:09:17 +02:00
eea6119e36 Add admin user list view for simon@kuehn.de
- ADMIN_EMAIL env var controls who has admin access
- GET /api/admin/users returns all users (id, email, username, registered);
  returns 403 for non-admins
- GET /api/me now includes is_admin flag
- Menu shows "Nutzer/Users/Użytkownicy" button for admins that opens a
  table with name, email, and registration date for all users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:06:14 +02:00
b537066a19 Fix registration: auto-login after signup, atomic token consumption
Registration failed visibly because the controller returned successfully
(user created, invite consumed) but the JS then called loadGoals() without
an authenticated session — causing a 401 that surfaced as an error to the user.

- Add Security::login() after user creation so the session is established
  immediately, matching the documented "registers + auto-logs in" behavior
- Wrap user persist and invite consumption in a single DB transaction so
  the invite token can never be consumed if user creation fails
- Add 12 integration tests covering auto-login, locale updates, logout,
  partial goal updates, invite isolation, and various edge cases (110 assertions total)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 09:58:21 +02:00
9d4c710d2f Add DE/EN/PL i18n with browser-language detection and per-user override
- STRINGS object in app.js with all UI strings in de/en/pl
- tr() function, ldoc() locale-aware date formatting
- tpl() auto-translates data-t/data-ph/data-val attributes on clone
- app.html.twig: data-t attributes on all template static text, language picker in data menu
- locale CHAR(2) column on users table; GET /api/me returns locale; PATCH /api/me accepts locale
- setLocale() persists to API + localStorage; applyLocale() reads user.locale → localStorage → navigator.language

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 13:34:41 +02:00
8729b0d1ed Add weekly goal field to new goal form
Tagesziel and Wochenziel sync automatically (weekly = daily * 7). Only daily is stored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 11:08:23 +02:00
a8f6692de4 Add stopwatch fill button and asset cache-busting
- Stopwatch ⏱ button appears in add/quick-book rows when sw >= 1s, fills input with floor(seconds)
- AppController passes md5 hashes of app.js/style.css to template for automatic cache-busting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 11:00:54 +02:00
f0cbe5b5d0 Add header stopwatch and asset cache-busting
- Stopwatch between logo and menu: tap to start, tap to stop, tap to reset
- AppController passes md5-based version hash for app.js and style.css to prevent browser caching issues

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 10:35:29 +02:00
80e418f8b7 Add PHPUnit integration tests, remove legacy pre-Symfony files, fix password reset
- Delete legacy root files (api.php, index.php, app.js, style.css, logo.png, include/)
- Install symfony/test-pack, add 34 integration tests covering auth, goals, invites, register, password reset
- Fix bug: users_resets.selector was varchar(20) but controller generates 24-char selectors; widen to varchar(64)
- Remove doctrine dbname_suffix from test env (tests run against live DB, cleanup via tearDown)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 10:18:21 +02:00
6503466344 Run deploy commands as www-data for correct file ownership
SSH key copied to /var/www/.ssh/ for www-data GitHub access.
All git, composer, and console commands now run as www-data so
var/cache/ files are always owned correctly without a manual chown.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 20:39:27 +02:00
d7edb30998 Run doctrine:migrations:migrate on deploy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 20:01:36 +02:00
3425bbd171 Add Doctrine migration to clean up legacy tables and align schema
Creates rememberme_token table, drops legacy delight-im/auth tables,
aligns column types with Doctrine entity definitions. Adds UserReset
entity so users_resets is managed by Doctrine instead of raw SQL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 20:01:19 +02:00
039007d462 deploy: fix var/ permissions after cache:clear 2026-04-29 15:59:58 +02:00
807bcb32b9 deploy: fix RewriteBase /dd/ for Alias deployment 2026-04-29 15:58:13 +02:00
f0857c1bd9 composer: remove post-install scripts, cache:clear handled by deploy.sh 2026-04-29 15:55:38 +02:00
57fdc81278 deploy: set COMPOSER_ALLOW_SUPERUSER=1 for root installs 2026-04-29 15:55:00 +02:00
a1b7d13921 composer: replace symfony-cmd with php bin/console in post-install scripts 2026-04-29 15:54:38 +02:00
4cb07cd6a2 deploy: replace rsync with git pull + composer install 2026-04-29 15:53:49 +02:00
fd473f00af Initial commit: Dudi habit tracker
Symfony 8 SPA with Doctrine ORM, Symfony Security, vanilla JS frontend.
Migrated from plain PHP (delight-im/auth + raw SQL) to full Symfony stack.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 15:40:57 +02:00