Add multi-select with Shift/Ctrl toggles and marquee selection on the invoice
and quote PDF template editors, plus a shared transformer, group nudge with
snap-aware arrow keys, multi copy/paste/duplicate, align-to-page or
align-to-selection, horizontal/vertical distribute, and Ctrl/Cmd+G group /
Ctrl/Cmd+Shift+G ungroup.
Expose horizontal (left/center/right/justify) and vertical (top/middle/bottom)
text controls in the properties panel; export verticalAlign, group_id,
locked, and hidden in template JSON where set. Skip hidden elements in
ReportLab rendering; draw justified text on canvas via Paragraph; honor
verticalAlign when a text box height is present.
Introduce app/static/js/pdf_editor helpers (loaded with dynamic import from
the templates), pdf_editor.css, a layers panel with visibility and lock toggles,
smart-guide snapping, and optional ruler chrome. Document behaviour and manual
QA in docs/PDF_LAYOUT_EDITOR.md; add tests/test_pdf_template_schema.py for
optional template fields.
Closes#619.
Replace the separate plus and bolt floating controls with a single Actions menu inside #fabDock, driven by app/static/floating-actions.js. The dock stacks Actions, optional team chat, and AI Helper using shared CSS variables for spacing; the AI control is a circular FAB matching the other buttons.
Move the chat widget panel to a fixed viewport overlay so dock z-index no longer paints controls over the open panel, and lift the panel bottom when the admin version banner or mobile bottom nav applies. Fade non-actions dock children while the actions menu is open (fab-dock--menu-open).
Update README.md, docs/UI_GUIDELINES.md, and the advanced-features implementation summaries so contributors describe the floating hub instead of global-fab.js. Keep app/static/quick-actions.js aligned with the retired mount pattern for any remaining references.
Introduce a guided LDAP configuration wizard mirroring the OIDC flow:
five-step UI with server/TLS, bind, directory layout, groups and
AUTH_METHOD, then optional connection test and .env / Docker Compose
generation for copy-paste deployment.
- Refactor LDAPService.test_connection to accept an optional config
mapping so the wizard can test draft values without merging live env
secrets; keep POST /admin/ldap/test on current_app.config.
- Add GET /admin/ldap/setup-wizard plus POST endpoints for test,
validate, and generate-config (manage_settings, rate limited).
- Surface an LDAP card with status badge and wizard link on the
integrations list for admins and manage_settings users.
- Add tests for validate, generate, and wizard test delegation.
Add app/static/manifest.json (TimeTracker / Tracker, indigo theme) and PNG install icons via scripts/generate_pwa_icons.py.
Replace inline Flask service worker with app/static/js/sw.js served at /service-worker.js for full-site scope. Cache name timetracker-v1: cache-first for /static, network-first for HTML and non-v1 /api, no interception of /api/v1/* (preserves Authorization).
Add public GET /offline and offline.html for SW navigation fallback; redirect /manifest.webmanifest to the static manifest.
Wire base.html (manifest link, theme-color #4F46E5, SW registration) and pwa-enhancements.js (ready/update/push without duplicate registration). Remove legacy app/static/service-worker.js and manifest.webmanifest.
Tests: service worker and offline routes, manifest redirect, TestPWA expectations; drop duplicate test_enhanced_ui app/client fixtures in favor of conftest.
Docs: ASSETS.md, BUILD_CONFIGURATION.md, implementation notes, and incomplete-features analysis updated for new paths.
Global quick actions (FAB):
- Add a fixed primary FAB with an animated popover (Start Timer, Log Time,
New Task) for authenticated users in base.html.
- Start Timer uses the dashboard control when present, otherwise opens the
dashboard with #start-timer so the existing start modal appears.
- Hide the FAB from the md breakpoint up while the header floating timer is
active (floating-timer-bar.js toggles body.fab-hide-desktop-timer-active).
Time entries overview:
- Make Notes and Duration editable in the table where permissions allow;
save via same-origin PUT/PATCH /api/entry/<id> (time-entries-inline-edit.js).
- Register PATCH on the session update_entry route alongside PUT.
Dashboard:
- Add a this-week vs last-week hours chart fed by GET /api/reports/week-comparison
and dashboard-enhancements.js.
Documentation:
- Extend UI_GUIDELINES (FAB, inline edit, file reference) and ARCHITECTURE
(session JSON endpoints) to match the new behavior.
Add an authenticated-only bottom bar below the md breakpoint with
Heroicon-style tabs for Dashboard, Timer, Time entries, Projects, and
More. More opens a slide-up sheet (backdrop, close, Escape) for
Invoices, Clients, Reports, and user Settings, gated by module flags
where applicable.
Align shell layout to Tailwind md (768px): sidebar hidden md:flex,
main md:ml-64 / md:ml-16 when collapsed, mobile hamburger md:hidden,
RTL mainContent margin reset at 767px. Main column uses pb-16 on
small screens so content clears the bar; bar and sheet use pb-safe
(env safe-area) with a Tailwind safelist and @layer utilities rule.
Remove the legacy six-slot FAB bottom nav from base.html.
Docs: README UI overview, CHANGELOG [Unreleased], UI_GUIDELINES layout
and file reference.
Adds a Tailwind-styled global command palette (Ctrl/Cmd+K) with fuzzy search and a Start Timer flow that picks a project then POSTs /api/timer/start with CSRF.
Updates docs to reflect the new shortcut.
Adopt an indigo brand palette with slate neutrals, add semantic colors, and introduce base/component layers (buttons, cards, badges). Move Inter loading to the Tailwind input CSS and update brand guidelines accordingly.
Introduce a web-first AI helper with admin-configurable providers (Ollama or hosted OpenAI-compatible), server-side context building, and confirmed write actions. Expose the feature via session /api/ai/* endpoints and scoped /api/v1/ai/* endpoints.
Harden security by requiring a strong SECRET_KEY for Docker Compose, adding optional settings encryption-at-rest (Fernet), and introducing TOTP-based 2FA plus password reset flows. Update admin UI, API docs, and install documentation.
Smart notifications (opt-in under user settings): NotificationService builds candidates from the user's local day and active timers; GET /api/notifications and POST /api/notifications/dismiss; migration 150 adds user columns and user_smart_notification_dismissals. /api/summary/today uses the same local-day totals. Client polls from smart-notifications.js; toastManager.show gains onDismiss for server dismiss sync. Config and env.example document SMART_NOTIFY_* variables.
Value dashboard: StatsService with Redis-backed caching, GET /api/stats/value-dashboard, dashboard template and dashboard-enhancements polling alongside existing widgets.
API v1 token search now uses apply_project_scope and apply_client_scope on queries; scope_filter adds apply_project_scope; tests extended for the new helper.
Add a support modal with usage stats, tier and license links, share control, and offline-safe outbound CTAs. Surface support from the header, sidebar, user menu, dashboard card, and settings "Support & Community" section without hiding entry points when a supporter license is active.
Introduce UsageStatsService and a persisted users.support_stats_reports_generated counter incremented on key report exports and custom report views. Add SupportPromptService for session-scoped soft toasts (after export, dashboard milestones, long session via POST /donate/request-soft-prompt).
Wire consent-aware track_event names support.* and mirror funnel rows in DonationInteraction; fix has_recent_donation_click to treat link_clicked as a recent click. Document events and SUPPORT_* / migration notes in docs.
Tests: tests/test_support_services.py for prompt and usage stats behavior.
Add VersionService to fetch and cache the latest GitHub release, compare it to the installed semver (APP_VERSION when valid, else setup.py), and expose admin-only GET /api/version/check and POST /api/version/dismiss on the legacy /api blueprint (session or Bearer token).
Persist per-user dismissal in users.dismissed_release_version (Alembic 148) and show a non-blocking update card in base.html for administrators. Add packaging for semver parsing and tests for comparison, service, and routes.
Document configuration in docs/admin/deployment/VERSION_MANAGEMENT.md and endpoints in docs/api/REST_API.md and docs/API.md.
Quotes (#583):
- Add requires_approval, approval_level, and can_be_sent; wire create form
- Migrations 145 (approval columns) and 146 (quote_items.position)
- Order Quote.items by position; set positions on create/edit/duplicate/API
- Fix view template approval branch (not_required); add web regression test
Invoices / PEPPOL:
- Use the same Factur-X embed and PDF/A-3 normalization for export and
email attachments; Associated File Data + text/xml metadata
- CII/UBL validators, pdfa3, zugferd, and invoice_pdf_postprocess helper
- Bundle compact sRGB ICC (app/resources/icc/); INVOICE_SRGB_ICC_PATH override
- Package data in setup.py; extend PEPPOL_EINVOICING.md and tests
Remove default form-input shadow and top margin inside tinted invoice and
quote line rows. The row already has a border and hover shadow; inputs
were doubling that edge so the top of each row looked overly heavy.
- Allow schedule edits (project, task, start/end, break) for users with
edit_own_time_entries on their own entries in API update_entry and
timer edit; scope project lists for subcontractors; admin-only source
dropdown on edit timer form.
- App shell: min-width/overflow fixes, header layout, compact bottom nav
on very narrow viewports (#573), dashboard timer block responsive layout.
- Invoice and quote edit: min-w-0 on grids/cells; scoped stronger neutral
borders for .form-input on #editInvoiceForm and #quote-form (#574).
- queueForOffline now saves url, method, headers, body (replay-safe for localStorage);
legacy items with options only still replayed via fallback
- processOfflineQueue builds fetch options from stored method/body so replayed
requests send the same payload when back online
- Make queueForOffline async and await it in handleFetchResponse/handleFetchException
- Add tests asserting queue stores method/body and replay uses them
- Dashboard: Pause/Resume buttons, break and Paused badge, elapsed uses break-adjusted duration
- Timer page: Pause/Resume/Stop, break display
- Floating bar: paused state, Resume on click when paused; use server current_duration when paused
- Manual entry: Break field (HH:MM), Suggest button using default break rules
- Edit time entry: Break field (HH:MM) for admins
- Add form-input-error and disabled state to form-input in input.css
- Add empty_state_compact and loading_overlay macros to components/ui.html
- Migrate tasks/overdue.html from Bootstrap (_components.html) to Tailwind
(page_header, empty_state, alert from ui.html; consistent cards and grid)
Dashboard:
- Add time-by-project chart (last 7 days) with Chart.js horizontal bar; link to Summary report
Summary report:
- Add time-by-project (last 30d) bar chart and daily trend (14d) line chart
- Add one-page PDF export (today/week/month hours + top projects table)
Post-timer flow:
- After stop, show toast "Logged Xh on [Project]" with action link "View time entries"
- Toast manager: optional actionLink/actionLabel for action links in toasts
- Session carries timer_stopped_toast to dashboard; no duplicate flash
Remind to log:
- User setting "Remind me to log time at end of day" + time picker (Settings)
- Hourly job: send one email per day if user has <0.5h logged that day (user TZ)
- Migration 135: notification_remind_to_log, reminder_to_log_time on users
Unify buttons, cards, headers, toasts, and form treatments across the app so screens feel consistent and are easier to scan on desktop and mobile. Update the broader template set to use the shared UI primitives and responsive spacing patterns introduced in this refresh.
- Group Chat, Timer, and Help in header as round icon buttons
- Vertically aligned, evenly spaced (gap-2), consistent w-10 h-10
- Header timer: one-click start/stop from any page via floating-timer-bar.js
- Fix timer manual entry URL (use /timer/manual, not /timer/manual_entry)
- Add Help button linking to help page
- Update FEATURES_COMPLETE (Header Quick Access, One-Click Timers)
- Update help page Time Tracking section with header timer tip
- Update CHANGELOG
Replace the single-page setup (telemetry + optional Google Calendar) with
a guided wizard that collects all base settings before completion.
Wizard steps:
1. Welcome - intro and Next
2. Region & time - timezone, date/time format, currency (Settings)
3. Company - name, address, email, optional phone/website (Settings)
4. System - allow self-registration, rounding minutes, single active
timer, idle timeout (Settings)
5. Integrations (optional) - Google Calendar OAuth; can skip
6. Privacy & finish - telemetry opt-in; Complete Setup submits form
Backend (app/routes/setup.py):
- GET: pass settings and timezones to template for prefilling
- POST: validate timezone, date_format, currency, rounding_minutes,
idle_timeout_minutes; persist all fields to Settings and
mark_setup_complete(telemetry_enabled)
- Default timezone/currency to UTC/EUR when missing (keeps tests passing)
Frontend:
- initial_setup.html: 6 wizard steps, progress bar (Step X of 6),
Back/Next and submit on last step
- setup-wizard.js: step navigation, progress update, optional
client-side validation for step 2 (timezone, currency required)
Docs updated: TELEMETRY_QUICK_START.md, GETTING_STARTED.md,
TELEMETRY_IMPLEMENTATION_SUMMARY.md.
- Add /reports/time-entries page listing all time entries (billed and unbilled)
- Columns: Date, Start, Stop, Duration, Project, Task, Notes, Billed, Client
- Filters: date range, user, project, client, task, billed (all/yes/no)
- Export to Excel and CSV with same filters; add Billed column to excel export
- Resolve client from entry.client or project.client in export
- Add Time Entries Report card to Reports index
- Load Flatpickr dark theme when app is in dark mode; sync on theme toggle.
- Apply form-input class to Flatpickr alt input so visible date field gets dark styles.
Refs #
Co-authored-by: Cursor <cursoragent@cursor.com>
When the API returns a merged events list (single 'events' array with
item_type), the calendar module now splits by item_type so time entries
populate this.timeEntries and render with Time Entry category (clock
icon, time-entry color, correct modal and links).
- loadEvents(): detect merged response and split data.events by
extendedProps.item_type into events, tasks, time_entries
- renderDayEvents(), renderWeekDayBlocks(), renderMonthCellEvents():
use item_type for block/badge type and showEventDetails() so
time entries show as Time Entry, not Event
Refs #517
Co-authored-by: Cursor <cursoragent@cursor.com>
- Respect user date format and week start: Flatpickr on user-date-input
fields, get_resolved_week_start_day for calendar/pickers, Week Starts On
from My Settings. Update CALENDAR_FEATURES_README.
- Fix time entry notes not saving on edit: sync Toast UI editor to hidden
textarea on all forms so the submitted form gets the current note (edit_timer).
- Time entries PDF export: wrap Task, Client, and Project columns like Notes
so long text breaks across lines (Issue #489).
Co-authored-by: Cursor <cursoragent@cursor.com>
- Settings: add date_format and time_format (model, migration 119, admin UI)
- Use user date/time prefs in templates, calendar, and PDF export
- Expense: eager-load client instead of category in repo, service, and API
- Mobile: clarify server URL and certificate docs, bump to 4.18.0, improve connection diagnostics
- Ignore mobile/android/.gradle/
Co-authored-by: Cursor <cursoragent@cursor.com>
Performance:
- Fix N+1 queries in reports.py with joinedload for TimeEntry.project,
TimeEntry.user, TimeEntry.task, and Project.client across 6 query locations
- Replace per-task time_entries loops with batch UPDATE queries in tasks.py
- Use efficient subquery for favorite project IDs in projects.py
Architecture:
- Add get_by_id() and get_by_name() methods to ProjectService and ClientService
- Route project/client lookups through service layer in timer.py, projects.py,
and clients.py instead of direct Model.query calls
Security:
- Add sanitize_input() with length limits to form inputs in clients.py,
projects.py, timer.py, issues.py, and auth.py
- Add email format validation for client creation
- Warn at startup when SECRET_KEY uses the default value or is too short
in ProductionConfig
- Replace 7 bare except: pass clauses with specific exception types
(OSError, IOError, TypeError, ValueError) in admin.py, settings.py,
and invoice.py
Authorization:
- Migrate all @admin_required decorators to @admin_or_permission_required()
with granular permissions (manage_roles, manage_kanban, manage_webhooks,
manage_api_tokens, manage_integrations, access_admin) across permissions.py,
kanban.py, webhooks.py, and admin.py (28 routes total)
Frontend:
- Remove 40+ console.log debug statements across 18 JS files
- Replace 42 inline onclick/onchange handlers in base.html with delegated
event listeners using data-dropdown and data-no-propagation attributes
- Migrate 6 inline handlers in time_entries_overview.html to addEventListener
- Extract shared typing detection into typing-utils.js, eliminating 5
duplicate isTyping() implementations across keyboard shortcut files
- Add missing aria-label attributes to icon-only buttons
Dependencies:
- Migrate from pytz to stdlib zoneinfo (Python 3.9+) across all 6 files
that used pytz; replace pytz with tzdata in requirements.txt
- Separate dev/test dependencies into requirements-dev.txt
- Configure RotatingFileHandler (10MB, 5 backups) for app and JSON logs
Co-authored-by: Cursor <cursoragent@cursor.com>
Use openKeyboardShortcutsModal when available in shortcut manager. Remove duplicate keyboard-shortcuts-enhanced.js and toast-notifications.css from base. Only open shortcuts modal on Shift+? when advanced manager is not loaded to avoid double handling.
Co-authored-by: Cursor <cursoragent@cursor.com>
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>
- 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>
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.
- 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
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
- 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.
- Add app/static/css/gantt-chart.css to override Frappe Gantt:
- Card-style container, grid, and popup using app palette
- Primary (#4A90E2) for projects and today column; tasks #10b981
- Dark mode via .dark for grid, text, bars, arrows, and tooltip
- Themed today highlight, bar labels, and dependency arrows
- In gantt/view.html: link gantt-chart.css, drop inline overrides
- Gantt options: bar_height 24, bar_corner_radius 6, padding 20
- Add Project.color (hex) and migration for projects.color
- Projects create/edit: Gantt color field with Pickr (swatch + hex input),
Pickr theme CSS and gantt-color-picker.js for init and sync
- Gantt API: include color in JSON for projects and tasks (tasks use project color)
- Gantt view: set custom_class from color, inject CSS for .bar and .bar-progress,
fix selectors for .gantt .bar-wrapper and :hover/.active overrides; add
fallback styles for gantt-project and gantt-task
- Integrate new logo assets across all templates
- Update base template with improved favicon and meta tags
- Add logo support to login, admin, and inventory templates
- Update web manifest with new branding
- Enhance PDF layouts with logo support
- Add setup wizard system for guided integration configuration
- Create wizard templates for all integration providers:
* Asana, GitHub, GitLab, Jira, Microsoft Teams
* Outlook Calendar, QuickBooks, Trello, Xero
- Add wizard_base.html template with common wizard functionality
- Implement setup_wizard route with provider detection
- Update integration list and manage pages with wizard links
- Add has_setup_wizard() helper to check wizard availability
- Create integration_wizard.js for wizard JavaScript functionality
- Improve UX with step-by-step guided setup process
- Add multi-step OIDC setup wizard with progress indicator
- Implement test connection endpoint with DNS and metadata validation
- Add configuration validation endpoint
- Create interactive wizard UI with step-by-step guidance
- Update OIDC debug page to link to setup wizard
- Support testing connection before finalizing configuration
- Add comprehensive error handling and user feedback
- Add offline-indicator.html component with sync queue panel
- Integrate offline indicator into base template
- Enhance offline-sync.js updateUI method for better integration
- Add visual feedback with status icons and colors
- Display pending sync items count and status
- Improve user experience for offline functionality
The offline indicator provides real-time feedback about sync status
and allows users to view pending sync operations.
- Fix tasks not being created when projects are created from templates
* Remove invalid status parameter from task creation (TaskService doesn't accept it)
* Fix estimated_hours conversion from string to float/None
* Add proper error handling for task creation failures
* Refresh template from database before reading tasks
- Fix missing tasks section in template edit form
* Add tasks section to edit template form with full CRUD functionality
* Display existing tasks when editing templates
* Allow adding, editing, and removing tasks in templates
- Fix tasks not being saved when creating/editing templates
* Fix JavaScript form handling to properly collect tasks from form
* Create tasks input field on page load and update on submit
* Add capture phase event listener to ensure tasks are set before form submission
* Ensure tasks are always saved as a list (never None)
- Fix [object Object] error in toast notifications
* Add safeguards to ensure flash messages are always strings
* Add object-to-string conversion in toast notification system
- Improve error handling and validation
* Add validation to ensure tasks is always a list
* Improve JSON parsing with better error handling
* Add safeguards for flash message handling
- Offline sync: Implement task and project sync functionality
* Add syncTasks() and syncProjects() methods with full CRUD support
* Implement saveTaskOffline(), saveProjectOffline() helper methods
* Add createTaskOffline() and createProjectOffline() public API methods
* Update markAsSynced() to handle tasks and projects correctly
* Support both create and update operations for offline entities
- Enhanced UI: Complete form auto-save initialization
* Add comprehensive error handling and validation
* Implement storage quota error handling with fallback
* Improve indicator with error states and better visual feedback
* Add beforeunload handler to save state on page unload
* Prevent duplicate initialization with proper tracking
* Add CSRF token handling and form method detection
* Use passive event listeners for better performance
- Error handling: Implement browser fallbacks for older browsers
* Add polyfillFetch() using XMLHttpRequest for fetch API fallback
* Implement polyfillLocalStorage() with in-memory storage
* Add sessionStorage fallback support
* Initialize fallbacks early in error handler setup
- Smart notifications: Complete check methods with error handling
* Enhance checkIdleTime() with duplicate prevention and better event handling
* Improve checkDeadlines() with validation, rate limiting, and error handling
* Enhance checkDailySummary() with once-per-day sending and better formatting
* Add comprehensive error handling to startBackgroundTasks()
* Implement proper network error handling and response validation
All implementations include proper error handling, edge case coverage,
performance optimizations, and browser compatibility considerations.