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>
- 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>
- desktop/README and docs/mobile-desktop-apps: minor updates
- desktop renderer index.html: small adjustments
Co-authored-by: Cursor <cursoragent@cursor.com>
- 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>
- 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>
- 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>
- 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.
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
- 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
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.
- 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
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.
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.
- 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.
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.
- 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.
- 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
- 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
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.
- 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')
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.
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
- 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)
- Fix version consistency: Update all documentation to reflect version 4.13.2
- Add Technology Stack section with complete tech overview
- Enhance Quick Start section with prerequisites and troubleshooting links
- Add System Requirements section with minimum/recommended specs
- Improve documentation organization by use case (users, admins, developers)
- Add comprehensive feature documentation links throughout README
- Enhance Features section with guide links and better categorization
- Update documentation index (docs/README.md) with missing links
- Add Contributing Quick Reference section
- Fix broken links and improve navigation
- Polish all documentation for clarity and consistency
This update makes the documentation more accessible, better organized,
and easier to navigate for all user types (new users, administrators,
developers, and troubleshooters).
Implement image/picture insertion capability in time log notes by
replacing plain textareas with ToastUI Editor (markdown editor with
image upload support), similar to task descriptions.
Changes:
- Replace textarea with markdown editor in manual time entry form
- Replace textarea with markdown editor in edit time entry form
- Replace textarea with markdown editor in dashboard timer start form
- Update all display views to render markdown with images
- Add image upload support via existing /api/uploads/images endpoint
- Support dark mode theme switching in editors
Features:
- Drag-and-drop or toolbar button image upload
- Full markdown support (headings, bold, italic, lists, links, code, tables, images)
- Backward compatible with existing plain text notes
- Images stored in app/static/uploads/editor/ directory
This addresses GitHub discussion #455 and allows users to add visual
proof/documentation to time entries, particularly useful for
maintenance tasks and general work tracking.
Files modified:
- app/templates/timer/manual_entry.html
- templates/timer/edit_timer.html
- app/templates/main/dashboard.html
- app/templates/timer/view_timer.html
- app/templates/timer/_time_entries_list.html
- app/templates/tasks/view.html
- app/templates/projects/time_entries_overview.html
- app/templates/clients/view.html
- app/templates/client_portal/approval_detail.html
- Add _sqlite_path_from_url() and use it for SQLite path resolution in
wait_for_database, cleanup, and verify_core_tables.
- run_migrations: pre/post probe and alembic/core checks work for both
PostgreSQL and SQLite (sqlite_master, etc.).
- cleanup_corrupted_database_state: support SQLite by removing corrupted DB file
when appropriate so migrations can recreate it; keep PostgreSQL table-drop behavior.
- verify_core_tables: for SQLite, list all tables, log path and clearer errors
when core tables or alembic_version are missing.
- Listen for before_cursor_execute on Engine instead of Session (SQLAlchemy 2).
- Only increment query count when has_request_context() and g.query_count exist,
avoiding errors outside request context. Remove unused Session import.
- Wrap DB-backed email settings read in app.app_context() so it works when
create_app() runs in gunicorn workers without a request context.
- Aligns SQLite and PostgreSQL behavior on worker startup.
- Add isDashboardPage() and skip initRealTimeUpdates/updateDashboardData when not on dashboard.
- Throttle updateDashboardData to at most once per 5 seconds (MIN_UPDATE_INTERVAL_MS).
- Clear existing realTimeUpdateInterval before creating a new one to avoid stacked intervals.
- TimeTrackingService.update_entry() now calls calculate_duration() when
start_time or end_time is changed so duration_seconds stays correct.
- Add test_update_entry_recalculates_duration_when_start_end_edited (fixes#451).
- Make InstallationConfig config dir overridable via INSTALLATION_CONFIG_DIR
so tests and CI use a writable path instead of /data (fixes PermissionError
on redirect to /admin/settings after logo upload).
- Set INSTALLATION_CONFIG_DIR in conftest before app import and in
ci-comprehensive.yml for integration-tests and full-test-suite jobs.
- In Settings.get_settings(), add _session_in_flush() and a re-entrancy
guard to skip add+commit when called during another commit's flush,
fixing ResourceClosedError in currency_display test setup.
- Update test_installation_config fixture to set INSTALLATION_CONFIG_DIR
so it continues to use its temp dir with the new env-based behavior.
- Client: call flag_modified() after mutating custom_fields in
set_custom_field() and remove_custom_field() so SQLAlchemy persists
JSON changes (in-place dict updates are not tracked by default).
Fixes test_count_clients_with_value_ignores_empty and
test_count_clients_with_value_ignores_other_fields.
- ClientNote: add ondelete=CASCADE to client_id FK so schema from
db.create_all() matches migration 024 and notes are deleted when
client is deleted. Fixes test_client_note_cascade_delete.
- Add cascade='all, delete-orphan' to the notes backref so client notes
are removed when a client is deleted.
- In test_client_has_notes_relationship use client.notes.count() instead
of len(client.notes) since the backref uses lazy='dynamic'.
- test_client_portal_dashboard_requires_access: expect 302 redirect to
client portal login instead of 403. The client portal 403 handler
redirects authenticated non-portal users to login by design.
- Run routes unit group with -n 0 in CI to avoid SQLite 'database is
locked' errors from audit logging under pytest-xdist parallel workers;
fixes client_portal and admin client-portal test failures.