Commit Graph

1006 Commits

Author SHA1 Message Date
Dries Peeters 2b1e043580 fix(timer): support duration-only entries and stable filtering
- Add duration-only manual entry flow with explicit duration override support.
- Harden manual-entry task loading and base-path compatibility.
- Fix time entries filter flakiness and add filtered PDF export.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-03 22:21:27 +01:00
Dries Peeters 69bef9741a Version bump v4.17.2
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-03 20:27:52 +01:00
Dries Peeters 1e780f5bd0 fix(timer): stabilize time entry UX and filtering
Fixes manual time entry task loading, adds a worked-time helper, makes Time Entries filters reliable, and adds CSV export for the current filtered view.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-03 20:27:52 +01:00
Dries Peeters 5a33186e5f Version bump v4.17.1 2026-02-02 20:31:10 +01:00
Dries Peeters 1529a2d498 fix(oidc): resolve redirect loop with Authelia and improve callback diagnostics
- Add issuer fallback from OIDC_ISSUER when userinfo lacks iss (fixes Authelia)
- Fallback to unverified id_token decode for iss when ID token parsing failed
- Wrap authorize_access_token() in dedicated try/except; log token_exchange_failed
  and suggest session cookie/proxy checks when state or PKCE validation fails
- Log reason=... before every redirect to login in callback for easier debugging
- Add 'Redirect loop / callback returns to login' troubleshooting to OIDC_SETUP.md

Fixes #486

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 20:30:26 +01:00
Dries Peeters 791ed5b90a Update README.md 2026-02-02 19:37:07 +01:00
Dries Peeters 80e74905e8 Version bump 4.17.0 2026-02-02 19:34:28 +01:00
Dries Peeters 55ecc7d037 feat(mobile): add connection diagnostics and clear error messages for login
- Add connection diagnostics module that classifies failures (DNS lookup,
  connection refused, TLS/certificate, timeout, HTTP errors, captive portal)
- Show user-friendly summary and actionable checklist on login failure
- Add Details bottom sheet with copyable technical report; Copy button in error box
- Fix token validation: use validateTokenRaw so network/TLS errors are not
  reported as 'Invalid username or password'; only persist server URL and
  token after validation succeeds
- Cover connection methods (HTTPS/HTTP, DNS, IPv6, self-signed cert trust flow)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 19:33:46 +01:00
Dries Peeters 3c01fb34c8 feat(modules): lock client selection and make Clients admin-only
- Add settings.locked_client_id and admin UI to select locked client
- Allow disabling Clients for non-admins while keeping admin access
- Gate Clients UI routes and API endpoints when module is disabled
- Auto-select and enforce the locked client across filters and form submissions

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 19:20:48 +01:00
Dries Peeters 807e6370ee feat(roles): add per-role module visibility (hide modules by role)
Admins can hide whole app modules (Analytics, Finance & Expenses, CRM, etc.)
per role so users in that role neither see them in the nav nor access them
by URL/API.

- Add Role.hidden_module_ids (JSON denylist) and migration
- Extend ModuleRegistry.is_enabled() with role-based hide check; module is
  hidden only if ALL of the user's roles hide it (super admins bypass)
- Add Module visibility section to role create/edit form with checkboxes
  by category; persist via hidden_modules form field
- Add tests for registry hide/allow semantics and route decorator 403

Closes #484

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 19:18:28 +01:00
Dries Peeters 0779909198 feat(reports): export detailed user time entries to Excel
Add a new User Report export that outputs one row per time entry with selectable columns (date/user/project/task/duration/notes by default), while keeping the existing summary/overtime export.

Refs: #483
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 17:59:59 +01:00
Dries Peeters 3b6bcc890b fix(nav): hide sidebar folder when all modules in category are disabled
Add has_enabled_modules(category) helper and wrap Finance & Expenses
folder so it only renders when at least one FINANCE module is enabled.
Fixes empty folder visible after disabling all modules in a category.

Closes #481

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 17:58:55 +01:00
Dries Peeters e15f0e2154 fix(oidc): avoid login loop by storing id_token server-side instead of in session cookie
When the IdP issues a large id_token (e.g. with groups claim), storing it in Flask's cookie session can exceed ~4KB and cause the browser to drop the cookie, leading to redirect loops between /auth/oidc/callback and /login.

Store id_token in Redis/in-memory cache; keep only a small reference key (oidc_id_token_key) in the session cookie. On logout, resolve id_token from cache for RP-initiated logout; support legacy session for backwards compatibility. Add regression test for oversized id_token and update OIDC logout tests.

Fixes #486

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 17:24:45 +01:00
Dries Peeters 5e5ebbb263 Version Bump V4.16.1 2026-02-02 15:04:34 +01:00
Dries Peeters e63bfc3a75 Update local.properties 2026-02-02 15:03:39 +01:00
Dries Peeters 579dc6f7c0 feat(mobile): design system, timer/projects UX, and unified logo
Design system and theme:
- Add google_fonts and Inter TextTheme; extend AppTheme with
  navigationBarTheme, bottomSheetTheme, chipTheme, dividerTheme
- Add app_tokens.dart (AppSpacing, AppRadii, AppDurations)

Timer UX:
- Remove nested NavigationBar from TimerScreen; single focused timer view
- Replace Start Timer dialog with modal bottom sheet (project search,
  task picker, notes)
- Redesign timer card (chips, FilledButton stop, AnimatedSwitcher)
- TimerWidget: use Timer.periodic for tick; dispose correctly

Projects UX:
- Replace search TextField with Material 3 SearchBar; add RefreshIndicator
- Project rows: avatar initial, client chip, Start timer action
- Start timer from project opens sheet with project pre-selected

Logo and app icon:
- Use assets/icon/app_icon.png as in-app logo on splash and login
- Regenerate Android launcher icons from same asset; iOS config in
  flutter_launcher_icons_ios.yaml (remove_alpha_ios)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 14:43:19 +01:00
Dries Peeters 74c75c1b96 fix: correct invalid .gitignore glob for mobile/.android/
Replace trailing backslash (mobile\\.android\\) with forward slash
(mobile/.android/) so ripgrep and other tools can search the repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 14:42:47 +01:00
Dries Peeters 689700d260 fix(timer): load tasks when selecting project on Timer and Log Time
- Use /api/projects/<id>/tasks instead of /api/tasks?project_id= so task
  loading matches the working Edit Logged Time flow.
- Add credentials: 'same-origin' and response.ok checks for reliable
  session auth and error handling.
- Render JS-embedded i18n strings with |tojson to avoid breakage in
  non-English locales.

Fixes #480

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 14:11:02 +01:00
Dries Peeters 7160ce88e5 Update Podfile 2026-02-01 17:11:28 +01:00
Dries Peeters 84a4508ace Update workflow 2026-02-01 17:03:07 +01:00
Dries Peeters 930740fd08 Update Workflows 2026-02-01 16:59:47 +01:00
Dries Peeters 9b2e7cea4a Update .gitignore 2026-02-01 16:54:27 +01:00
Dries Peeters df70ac0835 Docs: update READMEs and desktop renderer
- desktop/README and docs/mobile-desktop-apps: minor updates
- desktop renderer index.html: small adjustments

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-01 16:51:21 +01:00
Dries Peeters 94fc19f6f2 Build: add icon generation to CI and scripts, bump version to 4.16.0
- Run app icon and launcher icon generation in build-mobile workflow
- Add generate-mobile-icon scripts (Python/Pillow, ImageMagick, Inkscape)
- BUILD.md: document icon requirements and troubleshooting
- setup.py: version 4.16.0

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-01 16:51:13 +01:00
Dries Peeters 9066089a34 Mobile: theme, login, sync, and launcher icons
- Align app theme with webapp (AppColors, light/dark ColorScheme); move to core/theme
- Add app config, theme mode provider, empty_state and error_view widgets
- Improve API client, sync service, and repository for time entries
- Add login screen, settings (theme toggle, logout), SSL utils for dev certs
- Android/iOS project config, Gradle wrapper, and generated launcher icons
- Add flutter_launcher_icons and icon assets (app_icon.png, README)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-01 16:51:05 +01:00
Dries Peeters 0e7656134e Backend: add API auth login and fix time entry duration calculation
- Add POST /api/v1/auth/login (rate-limited) returning API token for mobile
- Fix time entry duration when DB returns timezone-aware datetimes (_naive_dt)
- Add _parse_date_range helper; expose timezone in API info
- Extend time entries API tests

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-01 16:50:42 +01:00
Dries Peeters 0c999fd780 Version bump v4.15.1 2026-01-31 08:06:13 +01:00
Dries Peeters fe53c926ed feat(calendar): user-configurable colors for calendar item types
- Add User preferences: calendar_color_events, calendar_color_tasks,
  calendar_color_time_entries (nullable hex)
- Calendar API: attach color to each event/task/time_entry and return
  typeColors; view_calendar passes type_colors to template
- PATCH /api/preferences: accept and validate calendar color fields
- Migration 117: add the three user columns

Events, tasks, and time entries can now be distinguished by user-chosen
colors (defaults: blue, amber, green). Frontend color pickers and
legend are on the calendar page (already in previous commit).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-01-31 07:48:09 +01:00
Dries Peeters 560bb0aec8 feat(kanban,tasks): multi-select filters and Kanban toolbar fixes (#464)
- Tasks list: add form_id to Project/Assigned To multi-select so Apply triggers AJAX filter refresh
- Tasks export: support project_ids and assigned_to_ids (multi-select) using same parse_ids logic as list view
- Page header: overflow-visible and z-20 so Kanban filter dropdowns appear above the board
- Kanban toolbar: align Add task/Manage Columns with dropdowns (items-end) and consistent button styling

fix(calendar): entry click opens modal near item, time entries link to /timer/edit/ (#475)

- Open popup modal with basic details and 'Go to all details' for all entry types (time entry, event, task) in both timer and custom calendar
- Position modal near the clicked item instead of centered
- Ensure time entries (registered time) always navigate to /timer/edit/: infer type from item_type, type, and props so wrong item_type does not send users to /calendar/event/ (404)
- Make time entries clickable in custom calendar day view (remove pointer-events: none)
- Timer calendar: show correct detail URL and modal title per type; hide Delete/Duplicate for non-time-entry types

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-01-31 07:47:49 +01:00
Dries Peeters 0480b1d1c5 Version bump 4.15.0 2026-01-30 17:42:05 +01:00
Dries Peeters 34a32af665 Fix syntax error in task_service and multi_select for ORM objects
- task_service.py: Remove stray fragment and duplicate lines at end of
  list_tasks that caused SyntaxError (unmatched ')') and gunicorn
  worker boot failure.

- multi_select.html: Resolve 'User' object has no attribute 'get' on
  Kanban board by supporting both dict-like and ORM items. Use Jinja
  mapping test and getattr for object attributes so Assigned To filter
  works with User instances.
2026-01-30 17:41:36 +01:00
Dries Peeters 8be9d82c16 feat: auto-select and gray out client when only one exists (#467)
When a single-client team has only one active client, pre-fill and disable
the client selection across manual time logging, project creation, and
similar forms to reduce friction.

- Add reusable client_select macro for single vs multi-client rendering
- Pass only_one_client and single_client to all relevant forms
- Invalidate dashboard cache on client create/archive/activate/delete
- Restore single client when project selection is cleared (timer forms)
- Add tests for single-client manual entry form
2026-01-30 17:26:14 +01:00
Dries Peeters 5da5c8373c feat(kanban,gantt): quick add task from board and Gantt views (#465)
- Add '+ Add task' button in Kanban and Gantt headers linking to create
  with project (and status for Kanban) pre-filled
- Support 'next' redirect after task create; validate to allowed paths
  (/kanban, /gantt, /tasks, /projects) to avoid open redirects
- Honor Initial Status on create: TaskService and form now pass status
  through; create form pre-fills status from query and hidden 'next'
- Per-column '+' on Kanban to add task into that column (status pre-set)
- Return user to same Kanban/Gantt view after creating a task
2026-01-30 17:25:56 +01:00
Dries Peeters 1f75754879 inventory: add tests, UX hint, and docs for return/waste devaluation (fixes #385)
Stock devaluation for return and waste movements was already implemented via valuation layers (stock lots). This change hardens and documents it:

- Add route tests: return with devaluation creates devalued lot; waste with devaluation consumes from devalued lot.

- Add API tests for POST /api/v1/inventory/movements with devalue_enabled (return and waste).

- Add discoverability hint on Record Movement form when type is Return or Waste.

- Document stock devaluation in INVENTORY_MANAGEMENT_PLAN.md (subsection 5.3) and list devaluation in movement_type.
2026-01-30 16:50:20 +01:00
Dries Peeters fe0a31c583 feat(invoices): improve UX for time-based invoice items (Issue #469)
- Show Project and Task instead of Stock Item and Warehouse for items from time entries
- Make quantity read-only for time-based items (billed hours) to prevent accidental edits
- Preserve time_entry_ids when saving invoice edits (fixes lost linkage)
- Enforce quantity from time entries on save for defense in depth
- Add task_name_from_time_entries property to InvoiceItem
- Add translations for new UI strings
2026-01-30 16:49:54 +01:00
Dries Peeters 940e1c8170 Fix manual time entry timezone (Issue #471)
Manual time entries were interpreted in the application timezone instead of the user's timezone, so users in a different timezone (e.g. GMT+1) saw times adjusted to the server timezone (e.g. GMT+0) on the calendar.

- Add parse_user_local_datetime() in timezone utils to parse form date/time as the user's local time and return naive datetime in app timezone for storage.
- Use parse_user_local_datetime() in the manual entry form so submitted times are stored correctly and display at the right slot in the calendar.

Calendar events and drag-created entries were already correct; only the manual entry form path is changed. Other callers of parse_local_datetime are unchanged.
2026-01-30 15:59:25 +01:00
Dries Peeters f117a0cb58 fix(calendar): show overlapping entries side-by-side and week as whole blocks
Day and week view: assign overlap columns so multiple time entries/events in the same timeframe render next to each other instead of stacking (fixes #472). Week view: render events as single blocks spanning their full duration (top/height in px) instead of per-hour segments.

- Add assignOverlapColumnsByTime/ByPosition and columnStyle() for lane layout.

- Day view: merge events + time entries, assign columns, set left/width per column.

- Week view: replace 24-row table with full-day columns (1440px); add renderWeekDayBlocks() for whole-block layout; style week-event-block like day cards.
2026-01-30 15:59:12 +01:00
Dries Peeters de1153f9ef Version bump: v4.14.2 2026-01-28 19:19:31 +01:00
Dries Peeters 828a78edd3 fix(dashboard): make Start Timer button open modal and submit correctly
- Parse project_id, client_id, task_id, template_id safely in timer.start_timer to avoid 500 when form sends empty strings.
- Sync Toast UI Editor notes into hidden textarea before programmatic submit so notes are included in the POST.
- Add inline onclick on Start Timer button so the modal opens regardless of script load order or DOMContentLoaded.
- Keep script-based close and form handling; add direct click binding, modal content stopPropagation, and aria-hidden for robustness.
2026-01-28 19:17:39 +01:00
Dries Peeters d4359dd87b Task report: include incomplete tasks and fix PostgreSQL query
Show all tasks that have time entries in the selected date range, regardless of task status (addresses GitHub discussion #450). Add Status column and display completed_at as '-' when missing. Apply same logic to Excel export. Fix PostgreSQL 'ORDER BY must appear in SELECT list' when using DISTINCT by resolving distinct task IDs in a subquery, then loading tasks with desired ordering.
2026-01-28 19:05:46 +01:00
Dries Peeters ee5a9b948f feat(timer): allow multiple image selection in time log notes
- Enable selecting and uploading multiple images at once when adding
  images to time entry notes (manual entry, edit entry, dashboard)
- Replace ToastUI getUI() usage with DOM queries to find toolbar and
  image button (getUI is not available in this editor version)
- Use setAttribute('multiple') and explicit toolbar lookup for reliable
  multiple-file picker on desktop and mobile
- Insert images via getMarkdown/setMarkdown so they render correctly
  in WYSIWYG mode instead of insertText (which did not render images)
- Add retry logic and multiple selectors for image button interception
- Add file type validation (PNG, JPG, GIF, WebP) and user feedback
- Use regular function expressions and safety checks to avoid syntax
  errors in older runtimes

Addresses GitHub Discussion #455 (Add image/picture insertion on time
log note) - client requested ability to add multiple photos at once.
2026-01-28 18:36:02 +01:00
Dries Peeters 02bdbe2411 Version bump to 4.14.1 2026-01-28 08:52:41 +01:00
Dries Peeters 73afe58c48 feat: Add multiple image upload support and mobile improvements for markdown editors
- Implement multiple image upload functionality in ToastUI markdown editors
  - Replace single image upload with bulk upload capability
  - Intercept image button click to use custom file input with multiple attribute
  - Upload multiple images via new bulk upload endpoint
  - Insert all uploaded images as markdown at cursor position
  - Include fallback to single image upload if interception fails

- Add mobile-responsive styles for ToastUI Editor modals
  - Adjust modal sizing and positioning for mobile devices
  - Make file inputs and buttons touch-friendly (min-height: 44px)
  - Prevent iOS zoom on input focus (font-size: 16px)
  - Improve modal accessibility on small screens

- Apply changes to dashboard, manual entry, and edit timer pages
- Normalize line endings for calendar.js and calendar.html
2026-01-28 08:49:50 +01:00
Dries Peeters db2cbc30e3 Fix connection error popup button text and styling
- Add recovery option for connection errors (status 0) to show Refresh button
- Change 'Refresh Page' to 'Refresh' for better clarity and consistency
- Improve button layout with vertical flex layout for clearer hierarchy
- Enhance Retry button styling (larger padding, medium font weight)
- Update recovery button styling to match error toast theme
- Remove redundant dark mode styles for recovery buttons
2026-01-28 08:45:11 +01:00
Dries Peeters fe928d3954 Fix time entry alignment in weekly calendar view (#458)
Time entries were not aligning properly with timestamp labels in the
weekly calendar view due to timezone conversion issues. Times were
being sent from the API in application timezone format without proper
conversion to the user's local timezone.

Changes:
- Added convert_time_for_calendar() helper function to convert times
  from application timezone to user's local timezone
- Applied timezone conversion to time entries (start and end times)
- Applied timezone conversion to calendar events (non-all-day events only)
- FullCalendar with timeZone: 'local' now receives correctly formatted
  times that align with the time slot labels

This ensures that time entries appear at the correct position matching
their actual timestamps in the weekly view.
2026-01-28 08:44:45 +01:00
Dries Peeters bf62fa33f8 fix(import): CSV/Harvest task created_by + CSRF for time-entries upload
- Set created_by=user_id when creating tasks during CSV and Harvest import
  (fixes NOT NULL constraint failed: tasks.created_by, closes #459)
- Send CSRF token with time-entries CSV upload FormData so /api/import/csv
  passes Flask-WTF validation (fixes 'The CSRF token is missing')
2026-01-28 08:44:32 +01:00
Dries Peeters d7f3683a11 fix(calendar): align time entries with timestamps in weekly view
Fixes #458 - Time entries now properly align with their start and end
timestamps in both FullCalendar and custom calendar weekly views.

FullCalendar (timer calendar):
- Add explicit timeZone: 'local' to ensure consistent date interpretation
- Normalize time entry ISO format to YYYY-MM-DDTHH:mm:ss (no microseconds)

Custom calendar week view:
- Fix data loading: merge events, tasks, and time_entries from separate
  API arrays into unified structure with extendedProps.item_type
- Implement proper spanning: entries now span across multiple hour cells
  based on their actual start/end times
- Add precise positioning: entries align with minute-level accuracy using
  percentage-based top/height calculations
- Apply same spanning logic to calendar events with duration
- Add hour labels column (00:00-23:00) for better visual reference

Time entries and events now correctly display their full duration and
align with both start and end timestamps in the weekly grid view.
2026-01-28 07:33:58 +01:00
Dries Peeters da66384700 fix(calendar): align time entries with timestamps in day view
Fix issue where time entries were stacking at the top of the day view
instead of being positioned according to their start and end times.

Changes:
- Convert day-events-container from flexbox to absolute positioning
- Set container height to 1440px (24 hours × 60px/hour)
- Calculate absolute top position based on entry start time
- Calculate entry height based on duration (end - start)
- Apply positioning to both time entries and calendar events
- Handle edge cases: active timers, entries spanning midnight, overlapping entries

Fixes #457
2026-01-28 07:32:45 +01:00
Dries Peeters 93e21bd256 Version Bump to 4.14.0 2026-01-27 21:51:13 +01:00
Dries Peeters 5f72ae3ef5 chore: Update version to 4.14.0 across all documentation
- Update README.md to reflect version 4.14.0
- Update docs/FEATURES_COMPLETE.md to version 4.14.0
- Add CHANGELOG entry for version 4.14.0 with comprehensive changes
- Align all version references with setup.py (4.14.0)
2026-01-27 21:50:44 +01:00