From 016fe5ead0dcab54753d31c4497e3eecf836d273 Mon Sep 17 00:00:00 2001 From: Dries Peeters Date: Fri, 12 Sep 2025 10:03:40 +0200 Subject: [PATCH] feat(ui): refresh templates and dashboards; improve admin and error pages - Update global layout and styles: `app/templates/base.html`, `app/static/base.css` - Modernize analytics dashboards (web + mobile) - Revamp auth pages: login, profile, edit profile - Refresh error pages: 400/403/404/500 and generic - Polish main dashboard and search - Enhance tasks views: create/edit/view, kanban, my/overdue - Update clients, projects, invoices, and reports pages - Refine timer pages (timer/edit/manual_entry) - Tweak admin routes and templates - Update license server util and integration docs - Refresh README and help/about content Notes: - UI-focused changes; no database migrations included. --- README.md | 18 + app/routes/admin.py | 40 +- app/static/base.css | 23 +- app/templates/analytics/dashboard.html | 81 ++-- app/templates/analytics/mobile_dashboard.html | 38 +- app/templates/auth/edit_profile.html | 28 +- app/templates/auth/login.html | 4 +- app/templates/auth/profile.html | 24 +- app/templates/base.html | 87 ++-- app/templates/errors/400.html | 16 +- app/templates/errors/403.html | 18 +- app/templates/errors/500.html | 10 +- app/templates/errors/generic.html | 6 +- app/templates/main/dashboard.html | 136 ++++--- app/templates/main/search.html | 47 ++- app/templates/tasks/_kanban.html | 28 +- app/templates/tasks/create.html | 122 +++--- app/templates/tasks/edit.html | 114 +++--- app/templates/tasks/my_tasks.html | 90 ++--- app/templates/tasks/overdue.html | 70 ++-- app/templates/tasks/view.html | 122 +++--- app/utils/license_server.py | 26 +- docs/LICENSE_SERVER_INTEGRATION.md | 77 ++-- templates/admin/create_user.html | 30 +- templates/admin/dashboard.html | 64 +-- templates/admin/license_status.html | 375 +++++++++--------- templates/admin/restore.html | 32 +- templates/admin/settings.html | 151 +++---- templates/admin/system_info.html | 24 +- templates/admin/user_form.html | 72 ++-- templates/clients/edit.html | 68 ++-- templates/clients/list.html | 21 +- templates/clients/view.html | 15 +- templates/errors/400.html | 16 +- templates/errors/403.html | 18 +- templates/errors/404.html | 10 +- templates/errors/500.html | 10 +- templates/invoices/create.html | 124 +++--- templates/invoices/edit.html | 80 ++-- templates/invoices/generate_from_time.html | 90 ++--- templates/invoices/list.html | 36 +- templates/invoices/view.html | 118 +++--- templates/main/about.html | 70 ++-- templates/main/help.html | 198 ++++----- templates/projects/create.html | 2 +- templates/projects/list.html | 27 +- templates/projects/view.html | 2 +- templates/reports/index.html | 68 ++-- templates/reports/project_report.html | 90 ++--- templates/reports/summary.html | 26 +- templates/reports/task_report.html | 52 +-- templates/reports/user_report.html | 72 ++-- templates/timer/edit_timer.html | 18 +- templates/timer/manual_entry.html | 9 +- templates/timer/timer.html | 154 +++---- 55 files changed, 1811 insertions(+), 1556 deletions(-) diff --git a/README.md b/README.md index 919dfac..76e8096 100644 --- a/README.md +++ b/README.md @@ -407,6 +407,24 @@ Detailed documentation is available in the `docs/` directory: - **Troubleshooting**: Common issues and solutions - **Deployment**: Setup and deployment instructions +### Metrics Server and Privacy + +This application can optionally communicate with a metrics server to help improve reliability and features. No license is required and the app works without it. + +- What is sent: + - App identifier and version + - Anonymous instance ID (UUID) + - Basic system info: OS, version, architecture, hostname, local IP, Python version + - Aggregate usage events (e.g., feature used). No time entry data or personal content +- Controls: + - Toggle analytics in Admin → System Settings → Privacy & Analytics + - View status in Admin → Metrics Status +- Configuration (env vars are optional and have sensible defaults): + - `METRICS_SERVER_URL` (or legacy `LICENSE_SERVER_BASE_URL`) + - `METRICS_SERVER_API_KEY` (or legacy `LICENSE_SERVER_API_KEY`) + - `METRICS_HEARTBEAT_SECONDS` (or legacy `LICENSE_HEARTBEAT_SECONDS`) + - `METRICS_SERVER_TIMEOUT_SECONDS` (or legacy `LICENSE_SERVER_TIMEOUT_SECONDS`) + ## 🚀 Deployment ### Docker Deployment diff --git a/app/routes/admin.py b/app/routes/admin.py index 1e73588..1d07d37 100644 --- a/app/routes/admin.py +++ b/app/routes/admin.py @@ -82,6 +82,18 @@ def admin_dashboard(): recent_entries=recent_entries ) +# Compatibility alias for code/templates that might reference 'admin.dashboard' +@admin_bp.route('/admin/dashboard') +@login_required +@admin_required +def admin_dashboard_alias(): + """Alias endpoint so url_for('admin.dashboard') remains valid. + + Some older references may use the endpoint name 'admin.dashboard'. + Redirect to the canonical admin dashboard endpoint. + """ + return redirect(url_for('admin.admin_dashboard')) + @admin_bp.route('/admin/users') @login_required @admin_required @@ -445,7 +457,7 @@ def system_info(): @login_required @admin_required def license_status(): - """Show license server client status""" + """Show metrics server client status""" try: from app.utils.license_server import get_license_client client = get_license_client() @@ -454,17 +466,17 @@ def license_status(): settings = Settings.get_settings() return render_template('admin/license_status.html', status=status, settings=settings) else: - flash('License server client not initialized', 'warning') - return redirect(url_for('admin.dashboard')) + flash('Metrics server client not initialized', 'warning') + return redirect(url_for('admin.admin_dashboard')) except Exception as e: - flash(f'Error getting license status: {e}', 'error') - return redirect(url_for('admin.dashboard')) + flash(f'Error getting metrics status: {e}', 'error') + return redirect(url_for('admin.admin_dashboard')) @admin_bp.route('/license-test') @login_required @admin_required def license_test(): - """Test license server communication""" + """Test metrics server communication""" try: from app.utils.license_server import get_license_client, send_usage_event client = get_license_client() @@ -475,11 +487,11 @@ def license_test(): # Test usage event usage_sent = send_usage_event("admin_test", {"admin": current_user.username}) - flash(f'Server Health: {"✓ Healthy" if server_healthy else "✗ Not Responding"}, Usage Event: {"✓ Sent" if usage_sent else "✗ Failed"}', 'info') + flash(f'Metrics Server: {"✓ Healthy" if server_healthy else "✗ Not Responding"}, Usage Event: {"✓ Sent" if usage_sent else "✗ Failed"}', 'info') else: - flash('License server client not initialized', 'warning') + flash('Metrics server client not initialized', 'warning') except Exception as e: - flash(f'Error testing license server: {e}', 'error') + flash(f'Error testing metrics server: {e}', 'error') return redirect(url_for('admin.license_status')) @@ -487,18 +499,18 @@ def license_test(): @login_required @admin_required def license_restart(): - """Restart the license server client""" + """Restart the metrics server client""" try: from app.utils.license_server import get_license_client, start_license_client client = get_license_client() if client: if start_license_client(): - flash('License server client restarted successfully', 'success') + flash('Metrics server client restarted successfully', 'success') else: - flash('Failed to restart license server client', 'error') + flash('Failed to restart metrics server client', 'error') else: - flash('License server client not initialized', 'warning') + flash('Metrics server client not initialized', 'warning') except Exception as e: - flash(f'Error restarting license server client: {e}', 'error') + flash(f'Error restarting metrics server client: {e}', 'error') return redirect(url_for('admin.license_status')) diff --git a/app/static/base.css b/app/static/base.css index f3e2e47..c1d6a27 100644 --- a/app/static/base.css +++ b/app/static/base.css @@ -1347,6 +1347,14 @@ main { gap: 0.75rem; } +/* Prevent very long app names from breaking the layout */ +.navbar-brand span { + max-width: 220px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .navbar-brand img { transition: var(--transition); } @@ -1620,6 +1628,7 @@ h6 { font-size: 1rem; } pointer-events: auto; background-clip: padding-box; min-width: 200px; + max-width: clamp(220px, 90vw, 360px); padding: 0.5rem 0; } @@ -1653,6 +1662,10 @@ h6 { font-size: 1rem; } display: flex; align-items: center; font-weight: 500; + white-space: normal; /* allow wrapping for long translations */ + word-break: break-word; /* break long words/URLs if needed */ + hyphens: auto; /* hyphenate when possible */ + line-height: 1.25; } .dropdown-item:hover { @@ -1757,7 +1770,15 @@ h6 { font-size: 1rem; } gap: 0.5rem; } -.dropdown-item i { width: 1rem; text-align: center; } +.dropdown-item i { width: 1rem; text-align: center; flex: 0 0 auto; } + +/* Ensure user name in navbar doesn't overflow */ +.navbar .nav-item.dropdown .nav-link span { + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} /* Enhanced Mobile Components */ .mobile-stack { diff --git a/app/templates/analytics/dashboard.html b/app/templates/analytics/dashboard.html index 879e104..3e98c38 100644 --- a/app/templates/analytics/dashboard.html +++ b/app/templates/analytics/dashboard.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Analytics Dashboard - {{ app_name }}{% endblock %} +{% block title %}{{ _('Analytics Dashboard') }} - {{ app_name }}{% endblock %} {% block content %}
@@ -9,16 +9,16 @@
{% set actions %} {% endset %} - {{ page_header('fas fa-chart-line', 'Analytics Dashboard', 'Key metrics and insights', actions) }} + {{ page_header('fas fa-chart-line', _('Analytics Dashboard'), _('Key metrics and insights'), actions) }}
@@ -29,7 +29,7 @@

-

-

Total Hours

+

{{ _('Total Hours') }}

@@ -38,7 +38,7 @@

-

-

Billable Hours

+

{{ _('Billable Hours') }}

@@ -47,7 +47,7 @@

-

-

Active Projects

+

{{ _('Active Projects') }}

@@ -56,7 +56,7 @@

-

-

Avg Daily Hours

+

{{ _('Avg Daily Hours') }}

@@ -68,7 +68,7 @@
- Daily Hours Trend + {{ _('Daily Hours Trend') }}
@@ -82,7 +82,7 @@
- Billable vs Non-Billable + {{ _('Billable vs Non-Billable') }}
@@ -100,7 +100,7 @@
- Hours by Project + {{ _('Hours by Project') }}
@@ -114,7 +114,7 @@
- Weekly Trends + {{ _('Weekly Trends') }}
@@ -132,7 +132,7 @@
- Hours by Time of Day + {{ _('Hours by Time of Day') }}
@@ -146,7 +146,7 @@
- Project Efficiency + {{ _('Project Efficiency') }}
@@ -165,7 +165,7 @@
- User Performance + {{ _('User Performance') }}
@@ -182,11 +182,32 @@ {% endblock %} +{% block extra_head %} + + +{% endblock %} + {% block extra_js %} + {% endblock %} diff --git a/app/templates/tasks/_kanban.html b/app/templates/tasks/_kanban.html index 1439461..112d90f 100644 --- a/app/templates/tasks/_kanban.html +++ b/app/templates/tasks/_kanban.html @@ -1,9 +1,9 @@ {# Reusable Kanban board for tasks. Expects `tasks` in context. #} {% set kanban_statuses = [ - {'key': 'todo', 'label': 'To Do', 'icon': 'fas fa-list-check', 'accent': 'secondary'}, - {'key': 'in_progress', 'label': 'In Progress', 'icon': 'fas fa-spinner', 'accent': 'warning'}, - {'key': 'review', 'label': 'Review', 'icon': 'fas fa-user-check', 'accent': 'info'}, - {'key': 'done', 'label': 'Done', 'icon': 'fas fa-check-circle', 'accent': 'success'} + {'key': 'todo', 'label': _('To Do'), 'icon': 'fas fa-list-check', 'accent': 'secondary'}, + {'key': 'in_progress', 'label': _('In Progress'), 'icon': 'fas fa-spinner', 'accent': 'warning'}, + {'key': 'review', 'label': _('Review'), 'icon': 'fas fa-user-check', 'accent': 'info'}, + {'key': 'done', 'label': _('Done'), 'icon': 'fas fa-check-circle', 'accent': 'success'} ] %}
@@ -12,7 +12,7 @@
- Kanban Board + {{ _('Kanban Board') }}
@@ -43,11 +43,11 @@ {{ task.priority_display }} {% if current_user.active_timer and current_user.active_timer.task_id == task.id %} - Active + {{ _('Active') }} {% endif %} {% if task.is_overdue %} - Overdue + {{ _('Overdue') }} {% endif %}
@@ -143,7 +143,13 @@ const board = document.getElementById('kanbanBoard'); if (!board) return; - const statusLabels = { todo: 'To Do', in_progress: 'In Progress', review: 'Review', done: 'Done', cancelled: 'Cancelled' }; + const statusLabels = { + todo: '{{ _('To Do') }}', + in_progress: '{{ _('In Progress') }}', + review: '{{ _('Review') }}', + done: '{{ _('Done') }}', + cancelled: '{{ _('Cancelled') }}' + }; const updateUrlTemplate = "{{ url_for('tasks.api_update_status', task_id=0) }}"; // will replace 0 with actual id function showAlert(kind, msg) { @@ -220,13 +226,13 @@ body: JSON.stringify({ status: targetStatus }) }); if (!resp.ok) { - throw new Error('Failed to update status'); + throw new Error('{{ _('Failed to update status') }}'); } const data = await resp.json(); if (!data.success) { throw new Error(data.error || 'Update rejected'); } - showAlert('success', 'Task moved to ' + (statusLabels[targetStatus] || targetStatus)); + showAlert('success', '{{ _('Task moved to') }} ' + (statusLabels[targetStatus] || targetStatus)); } catch (err) { // revert if (originalParent) originalParent.appendChild(card); @@ -236,7 +242,7 @@ statusBadge.textContent = statusLabels[originalStatus] || originalStatus; } updateCounts(); - showAlert('error', err.message || 'Could not update task'); + showAlert('error', err.message || '{{ _('Could not update task') }}'); } }); }); diff --git a/app/templates/tasks/create.html b/app/templates/tasks/create.html index 6b0c051..deb9c61 100644 --- a/app/templates/tasks/create.html +++ b/app/templates/tasks/create.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Create Task - Time Tracker{% endblock %} +{% block title %}{{ _('Create Task') }} - Time Tracker{% endblock %} {% block extra_css %} @@ -19,8 +19,8 @@
-

Create New Task

-

Add a new task to your project to break down work into manageable components

+

{{ _('Create New Task') }}

+

{{ _('Add a new task to your project to break down work into manageable components') }}

@@ -34,7 +34,7 @@
- Task Information + {{ _('Task Information') }}
@@ -42,65 +42,65 @@
- Choose a clear, descriptive name that explains what needs to be done + value="{{ request.form.get('name', '') }}" placeholder="{{ _('Enter a descriptive task name') }}" required> + {{ _('Choose a clear, descriptive name that explains what needs to be done') }}
- +
- Optional: Add context, requirements, or specific instructions for the task + {{ _('Optional: Add context, requirements, or specific instructions for the task') }}
- Select the project this task belongs to + {{ _('Select the project this task belongs to') }}
@@ -109,45 +109,45 @@
- Optional: Set a deadline for this task + {{ _('Optional: Set a deadline for this task') }}
- Optional: Estimate how long this task will take + {{ _('Optional: Estimate how long this task will take') }}
- Optional: Assign this task to a team member + {{ _('Optional: Assign this task to a team member') }}
- Cancel + {{ _('Cancel') }}
@@ -161,7 +161,7 @@
- Task Creation Tips + {{ _('Task Creation Tips') }}
@@ -171,8 +171,8 @@
- Clear Naming - Use action verbs and be specific about what needs to be done + {{ _('Clear Naming') }} + {{ _('Use action verbs and be specific about what needs to be done') }}
@@ -183,8 +183,8 @@
- Realistic Estimates - Consider complexity and dependencies when estimating time + {{ _('Realistic Estimates') }} + {{ _('Consider complexity and dependencies when estimating time') }}
@@ -195,8 +195,8 @@
- Set Deadlines - Due dates help prioritize work and track progress + {{ _('Set Deadlines') }} + {{ _('Due dates help prioritize work and track progress') }}
@@ -207,8 +207,8 @@
- Priority Matters - Use priority levels to help team members focus on what's most important + {{ _('Priority Matters') }} + {{ _('Use priority levels to help team members focus on what\'s most important') }}
@@ -219,35 +219,35 @@
- Priority Guide + {{ _('Priority Guide') }}
- Low - Non-urgent, can be done later + {{ _('Low') }} + {{ _('Non-urgent, can be done later') }}
- Medium - Normal priority, standard timeline + {{ _('Medium') }} + {{ _('Normal priority, standard timeline') }}
- High - Important, needs attention soon + {{ _('High') }} + {{ _('Important, needs attention soon') }}
- Urgent - Critical, immediate attention required + {{ _('Urgent') }} + {{ _('Critical, immediate attention required') }}
@@ -257,35 +257,35 @@
- Status Guide + {{ _('Status Guide') }}
- To Do - Task is planned but not started + {{ _('To Do') }} + {{ _('Task is planned but not started') }}
- In Progress - Work has begun on the task + {{ _('In Progress') }} + {{ _('Work has begun on the task') }}
- Review - Task is ready for review/testing + {{ _('Review') }} + {{ _('Task is ready for review/testing') }}
- Done - Task is completed successfully + {{ _('Done') }} + {{ _('Task is completed successfully') }}
diff --git a/app/templates/tasks/edit.html b/app/templates/tasks/edit.html index a1fb6de..157a16a 100644 --- a/app/templates/tasks/edit.html +++ b/app/templates/tasks/edit.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Edit Task - {{ task.name }} - Time Tracker{% endblock %} +{% block title %}{{ _('Edit Task') }} - {{ task.name }} - Time Tracker{% endblock %} {% block extra_css %} @@ -19,8 +19,8 @@
-

Edit Task

-

Update task details and settings for "{{ task.name }}"

+

{{ _('Edit Task') }}

+

{{ _('Update task details and settings for "%(task)s"', task=task.name) }}

@@ -34,7 +34,7 @@
- Task Information + {{ _('Task Information') }}
@@ -42,65 +42,65 @@
- Choose a clear, descriptive name that explains what needs to be done + value="{{ task.name }}" placeholder="{{ _('Enter a descriptive task name') }}" required> + {{ _('Choose a clear, descriptive name that explains what needs to be done') }}
- +
- Optional: Add context, requirements, or specific instructions for the task + {{ _('Optional: Add context, requirements, or specific instructions for the task') }}
- Select the project this task belongs to + {{ _('Select the project this task belongs to') }}
@@ -109,45 +109,45 @@
- Optional: Set a deadline for this task + {{ _('Optional: Set a deadline for this task') }}
- Optional: Estimate how long this task will take + {{ _('Optional: Estimate how long this task will take') }}
- Optional: Assign this task to a team member + {{ _('Optional: Assign this task to a team member') }}
- Cancel + {{ _('Cancel') }}
@@ -161,26 +161,26 @@
- Current Task Info + {{ _('Current Task Info') }}
- Current Status + {{ _('Current Status') }} {{ task.status_display }}
- Current Priority + {{ _('Current Priority') }} {{ task.priority_display }}
- Project + {{ _('Project') }}
@@ -191,7 +191,7 @@ {% if task.assigned_user %}
- Currently Assigned To + {{ _('Currently Assigned To') }}
@@ -203,7 +203,7 @@ {% if task.due_date %}
- Current Due Date + {{ _('Current Due Date') }}
@@ -217,24 +217,24 @@ {% if task.estimated_hours %}
- Current Estimate + {{ _('Current Estimate') }}
- {{ task.estimated_hours }} hours + {{ task.estimated_hours }} {{ _('hours') }}
{% endif %} {% if task.total_hours > 0 %}
- Actual Hours + {{ _('Actual Hours') }}
- {{ task.total_hours }} hours + {{ task.total_hours }} {{ _('hours') }}
{% endif %} @@ -245,23 +245,23 @@
- Quick Actions + {{ _('Quick Actions') }}
@@ -271,7 +271,7 @@
- Edit Tips + {{ _('Edit Tips') }}
@@ -281,8 +281,8 @@
- Status Changes - Changing status may affect time tracking and progress calculations + {{ _('Status Changes') }} + {{ _('Changing status may affect time tracking and progress calculations') }}
@@ -293,8 +293,8 @@
- Due Date Updates - Consider team workload when adjusting deadlines + {{ _('Due Date Updates') }} + {{ _('Consider team workload when adjusting deadlines') }}
@@ -305,8 +305,8 @@
- Assignment Changes - Notify team members when reassigning tasks + {{ _('Assignment Changes') }} + {{ _('Notify team members when reassigning tasks') }}
@@ -484,7 +484,7 @@ document.addEventListener('DOMContentLoaded', function() { previewStyle: 'vertical', usageStatistics: false, hideModeSwitch: false, - placeholder: 'Provide detailed information about the task, requirements, and any specific instructions...', + placeholder: '{{ _('Provide detailed information about the task, requirements, and any specific instructions...') }}', theme: theme, toolbarItems: [ ['heading', 'bold', 'italic', 'strike'], @@ -560,7 +560,7 @@ document.addEventListener('DOMContentLoaded', function() { // Show loading state const submitBtn = form.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; - submitBtn.innerHTML = 'Updating...'; + submitBtn.innerHTML = '{{ _('Updating...') }}'; submitBtn.disabled = true; // Re-enable after a delay (in case of validation errors) diff --git a/app/templates/tasks/my_tasks.html b/app/templates/tasks/my_tasks.html index 7cac2c6..27f32ce 100644 --- a/app/templates/tasks/my_tasks.html +++ b/app/templates/tasks/my_tasks.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}My Tasks - Time Tracker{% endblock %} +{% block title %}{{ _('My Tasks') }} - Time Tracker{% endblock %} {% block content %}
@@ -10,10 +10,10 @@
{% set actions %} - New Task + {{ _('New Task') }} {% endset %} - {{ page_header('fas fa-user', 'My Tasks', 'Tasks assigned to or created by you • ' ~ (tasks|length) ~ ' total', actions) }} + {{ page_header('fas fa-user', _('My Tasks'), _('Tasks assigned to or created by you') ~ ' • ' ~ (tasks|length) ~ ' ' ~ _('total'), actions) }}
@@ -30,7 +30,7 @@
-
To Do
+
{{ _('To Do') }}
{{ tasks|selectattr('status', 'equalto', 'todo')|list|length }}
@@ -47,7 +47,7 @@
-
In Progress
+
{{ _('In Progress') }}
{{ tasks|selectattr('status', 'equalto', 'in_progress')|list|length }}
@@ -64,7 +64,7 @@
-
Review
+
{{ _('Review') }}
{{ tasks|selectattr('status', 'equalto', 'review')|list|length }}
@@ -81,7 +81,7 @@
-
Completed
+
{{ _('Completed') }}
{{ tasks|selectattr('status', 'equalto', 'done')|list|length }}
@@ -94,44 +94,44 @@
- Filter My Tasks + {{ _('Filter My Tasks') }}
- +
+ value="{{ search }}" placeholder="{{ _('Task name or description') }}">
- +
- +
- +
- +
- Clear + {{ _('Clear') }}
@@ -278,7 +278,7 @@ {% if task.assigned_to == current_user.id %} Assigned to me {% else %} - Created by me + {{ _('Created by me') }} {% endif %}
@@ -288,7 +288,7 @@ {% if task.estimated_hours and task.total_hours > 0 %}
- Progress + {{ _('Progress') }} {{ task.progress_percentage }}%
@@ -306,17 +306,17 @@ diff --git a/app/templates/tasks/overdue.html b/app/templates/tasks/overdue.html index 793996e..51df4d7 100644 --- a/app/templates/tasks/overdue.html +++ b/app/templates/tasks/overdue.html @@ -1,25 +1,25 @@ {% extends "base.html" %} -{% block title %}Overdue Tasks - Time Tracker{% endblock %} +{% block title %}{{ _('Overdue Tasks') }} - Time Tracker{% endblock %} {% block content %}
{% from "_components.html" import page_header %} {% set actions %} - Back to Tasks + {{ _('Back to Tasks') }} {% endset %} - {{ page_header('fas fa-exclamation-triangle', 'Overdue Tasks', 'Requires immediate attention • ' ~ (tasks|length) ~ ' items', actions) }} + {{ page_header('fas fa-exclamation-triangle', _('Overdue Tasks'), _('Requires immediate attention') ~ ' • ' ~ (tasks|length) ~ ' ' ~ _('items'), actions) }} @@ -31,7 +31,7 @@
- Overdue + {{ _('Overdue') }} {{ task.priority_display }} @@ -56,21 +56,21 @@ {% if task.assigned_user %} {{ task.assigned_user.display_name }} {% else %} - Unassigned + {{ _('Unassigned') }} {% endif %}
- Due: {{ task.due_date.strftime('%Y-%m-%d') }} + {{ _('Due:') }} {{ task.due_date.strftime('%Y-%m-%d') }} {% if task.estimated_hours %}
- Est: {{ task.estimated_hours }}h + {{ _('Est:') }} {{ task.estimated_hours }}h {% endif %} {% if task.total_hours > 0 %}
- Actual: {{ task.total_hours }}h + {{ _('Actual:') }} {{ task.total_hours }}h {% endif %}
@@ -89,16 +89,16 @@ @@ -134,35 +134,57 @@ {% else %}
-

No Overdue Tasks!

-

Great job! All tasks are currently on schedule.

+

{{ _('No Overdue Tasks!') }}

+

{{ _('Great job! All tasks are currently on schedule.') }}

- View All Tasks + {{ _('View All Tasks') }}
{% endif %}
+ diff --git a/app/templates/tasks/view.html b/app/templates/tasks/view.html index 709f775..128aea8 100644 --- a/app/templates/tasks/view.html +++ b/app/templates/tasks/view.html @@ -7,7 +7,7 @@ @@ -49,23 +49,23 @@ {% if current_user.active_timer and current_user.active_timer.task_id == task.id %}
{% elif not current_user.active_timer %} - Start Timer + {{ _('Start Timer') }} {% endif %} {% if current_user.is_admin or task.created_by == current_user.id %} - Edit Task + {{ _('Edit Task') }} {% endif %} - Back to Tasks + {{ _('Back to Tasks') }}
@@ -84,7 +84,7 @@
- Description + {{ _('Description') }}
@@ -109,7 +109,7 @@
{{ task.project.name }}
-

{{ task.project.description or 'No description available' }}

+

{{ task.project.description or _('No description available') }}

@@ -119,7 +119,7 @@
- Time Tracking + {{ _('Time Tracking') }}
@@ -128,7 +128,7 @@
{{ task.estimated_hours }}
- Estimated Hours + {{ _('Estimated Hours') }}
{% endif %} @@ -137,7 +137,7 @@
{{ task.total_hours }}
- Actual Hours + {{ _('Actual Hours') }}
{% endif %} @@ -146,7 +146,7 @@
{{ task.progress_percentage }}%
- Progress + {{ _('Progress') }}
{% endif %} @@ -154,7 +154,7 @@
{{ task.time_entries.count() }}
- Time Entries + {{ _('Time Entries') }}
@@ -162,8 +162,8 @@ {% if task.estimated_hours and task.total_hours > 0 %}
- Progress Overview - {{ task.progress_percentage }}% Complete + {{ _('Progress Overview') }} + {{ task.progress_percentage }}% {{ _('Complete') }}
- Recent Time Entries + {{ _('Recent Time Entries') }}
@@ -190,11 +190,11 @@ - - - - - + + + + + @@ -213,11 +213,11 @@
DateDurationNotesUserActions{{ _('Date') }}{{ _('Duration') }}{{ _('Notes') }}{{ _('User') }}{{ _('Actions') }}
+ class="btn btn-sm btn-action btn-action--edit touch-target" title="{{ _('Edit entry') }}"> {% if current_user.is_admin or entry.user_id == current_user.id %} - @@ -231,13 +231,13 @@
{% if task.time_entries.count() > 10 %}
- Showing 10 of {{ task.time_entries.count() }} entries + {{ _('Showing') }} 10 {{ _('of') }} {{ task.time_entries.count() }} {{ _('entries') }}
@@ -253,19 +253,19 @@
- Task Details + {{ _('Task Details') }}
- Status + {{ _('Status') }} {{ task.status_display }}
- Priority + {{ _('Priority') }} {{ task.priority_display }} @@ -273,7 +273,7 @@ {% if task.assigned_user %}
- Assigned To + {{ _('Assigned To') }}
@@ -284,7 +284,7 @@ {% endif %}
- Created By + {{ _('Created By') }}
@@ -295,7 +295,7 @@ {% if task.due_date %}
- Due Date + {{ _('Due Date') }}
@@ -309,7 +309,7 @@ {% if task.started_at %}
- Started + {{ _('Started') }}
@@ -321,7 +321,7 @@ {% if task.completed_at %}
- Completed + {{ _('Completed') }}
@@ -343,35 +343,35 @@
{% if task.status == 'todo' %} - {% elif task.status == 'in_progress' %}
- -
{% elif task.status == 'review' %}
- -
{% elif task.status == 'done' %} - {% endif %} {% if task.status != 'done' and task.status != 'cancelled' %} - {% endif %} @@ -383,7 +383,7 @@
- Task Timeline + {{ _('Task Timeline') }}
@@ -392,7 +392,7 @@
{{ task.created_at.strftime('%b %d, %Y') }} -

Task created

+

{{ _('Task created') }}

@@ -401,7 +401,7 @@
{{ task.started_at.strftime('%b %d, %Y') }} -

Task started

+

{{ _('Task started') }}

{% endif %} @@ -411,7 +411,7 @@
{{ task.completed_at.strftime('%b %d, %Y') }} -

Task completed

+

{{ _('Task completed') }}

{% endif %} @@ -420,7 +420,7 @@
{{ task.updated_at.strftime('%b %d, %Y') }} -

Last updated

+

{{ _('Last updated') }}

@@ -432,7 +432,7 @@
- Activity Log + {{ _('Activity Log') }}
@@ -440,10 +440,10 @@ - - - - + + + + @@ -777,18 +777,18 @@ document.addEventListener('DOMContentLoaded', function() {
-
Time Entries
+
{{ _('Time Entries') }}
{{ stats.total_entries }}
@@ -94,7 +94,7 @@
-
Total Hours
+
{{ _('Total Hours') }}
{{ "%.1f"|format(stats.total_hours) }}h
@@ -109,16 +109,16 @@
- User Management + {{ _('User Management') }}
@@ -129,16 +129,16 @@
- System Settings + {{ _('System Settings') }}
@@ -152,7 +152,7 @@
- Recent Activity + {{ _('Recent Activity') }}
@@ -161,11 +161,11 @@
WhenEventUserDetails{{ _('When') }}{{ _('Event') }}{{ _('User') }}{{ _('Details') }}
- - - - - + + + + + @@ -183,9 +183,9 @@ @@ -197,8 +197,8 @@
-
No recent activity
-

No time entries have been recorded recently.

+
{{ _('No recent activity') }}
+

{{ _('No time entries have been recorded recently.') }}

{% endif %} @@ -210,22 +210,22 @@
- Quick Actions + {{ _('Quick Actions') }}
diff --git a/templates/admin/license_status.html b/templates/admin/license_status.html index d93175e..b6ddae2 100644 --- a/templates/admin/license_status.html +++ b/templates/admin/license_status.html @@ -1,190 +1,207 @@ {% extends "base.html" %} -{% block title %}License Server Status - Admin{% endblock %} +{% block title %}{{ _('Metrics Server Status') }} - {{ _('Admin') }}{% endblock %} {% block content %}
-
-
- +
+
+ - {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} - - {% endfor %} - {% endif %} - {% endwith %} + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} -
-
-
-
-
- Client Information -
-
-
-
UserProjectDateDurationStatus{{ _('User') }}{{ _('Project') }}{{ _('Date') }}{{ _('Duration') }}{{ _('Status') }}
{% if entry.end_time %} - Completed + {{ _('Completed') }} {% else %} - Running + {{ _('Running') }} {% endif %}
- - - - - - - - - - - - - - - - -
App Identifier:{{ status.app_identifier }}
App Version:{{ status.app_version }}
Instance ID:{{ status.instance_id or 'Not registered' }}
Registration Status: - {% if status.is_registered %} - Registered - {% else %} - Not Registered - {% endif %} -
-
-
-
+
+
+
+
+
+ {{ _('Client Information') }} +
+
+
+ + + + + + + + + + + + + + + + + +
{{ _('App Identifier:') }}{{ status.app_identifier }}
{{ _('App Version:') }}{{ status.app_version }}
{{ _('Instance ID:') }}{{ status.instance_id or _('Not registered') }}
{{ _('Registration Status:') }} + {% if status.is_registered %} + {{ _('Registered') }} + {% else %} + {{ _('Not Registered') }} + {% endif %} +
+
+
+
-
-
-
-
- Connection Status -
-
-
- - - - - - - - - - - - - -
Client Running: - {% if status.is_running %} - Running - {% else %} - Stopped - {% endif %} -
Server Health: - {% if status.server_healthy %} - Healthy - {% else %} - Not Responding - {% endif %} -
Offline Data: - {% if status.offline_data_count > 0 %} - {{ status.offline_data_count }} pending - {% else %} - None - {% endif %} -
-
-
-
-
+
+
+
+
+ {{ _('Connection Status') }} +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
{{ _('Client Running:') }} + {% if status.is_running %} + {{ _('Running') }} + {% else %} + {{ _('Stopped') }} + {% endif %} +
{{ _('Server Health:') }} + {% if status.server_healthy %} + {{ _('Healthy') }} + {% else %} + {{ _('Not Responding') }} + {% endif %} +
{{ _('Offline Data:') }} + {% if status.offline_data_count > 0 %} + {{ status.offline_data_count }} {{ _('pending') }} + {% else %} + {{ _('None') }} + {% endif %} +
{{ _('Server URL:') }}{{ status.server_url }}
{{ _('Heartbeat Interval:') }}{{ status.heartbeat_interval }} {{ _('seconds') }}
{{ _('Analytics Enabled:') }} + {% if status.analytics_enabled %} + {{ _('Yes') }} + {% else %} + {{ _('No') }} + {% endif %} +
+
+
+
+
-
-
-
-
-
- Configuration -
-
-
-
-
About This Implementation
-

- This TimeTracker application communicates with a license server for monitoring and analytics purposes only. - No license is required to use the application. The license server is used to: -

-
    -
  • Track application usage and features
  • -
  • Monitor system health and performance
  • -
  • Collect analytics data for improvement
  • -
  • Provide support and troubleshooting information
  • -
-
- -
-
-
Server Configuration
-
    -
  • Server URL: http://host.docker.internal:8081
  • -
  • API Key: no-license-required
  • -
  • Heartbeat Interval: 3600 seconds (1 hour)
  • -
-
-
-
Features
-
    -
  • Automatic instance registration
  • -
  • Periodic heartbeats
  • -
  • Usage data collection
  • -
  • Offline data storage
  • -
  • Graceful error handling
  • -
-
-
- -
-
-
Privacy & Analytics Settings
-
- - Analytics Setting: - {% if settings and settings.allow_analytics %} - Enabled - System information is being shared with the license server - {% else %} - Disabled - System information sharing is disabled - {% endif %} -
- - - This setting can be changed in System Settings. - License validation continues to work regardless of this setting. - -
-
-
-
-
-
-
-
-
+
+
+
+
+
+ {{ _('Configuration') }} +
+
+
+
+
{{ _('About This Implementation') }}
+

+ {{ _('This TimeTracker application communicates with a metrics server for monitoring and analytics purposes only.') }} + {{ _('No license is required') }} {{ _('to use the application. The metrics server is used to:') }} +

+
    +
  • {{ _('Track application usage and features') }}
  • +
  • {{ _('Monitor system health and performance') }}
  • +
  • {{ _('Collect analytics data for improvement') }}
  • +
  • {{ _('Provide support and troubleshooting information') }}
  • +
+
+ +
+
+
{{ _('Server Configuration') }}
+
    +
  • {{ _('Server URL:') }} {{ status.server_url }}
  • +
  • {{ _('Heartbeat Interval:') }} {{ status.heartbeat_interval }} {{ _('seconds') }}
  • +
+
+
+
{{ _('Features') }}
+
    +
  • {{ _('Automatic instance registration') }}
  • +
  • {{ _('Periodic heartbeats') }}
  • +
  • {{ _('Usage data collection') }}
  • +
  • {{ _('Offline data storage') }}
  • +
  • {{ _('Graceful error handling') }}
  • +
+
+
+ +
+
+
{{ _('Privacy & Analytics Settings') }}
+
+ + {{ _('Analytics Setting:') }} + {% if settings and settings.allow_analytics %} + {{ _('Enabled') }} + {{ _('System information is being shared with the metrics server') }} + {% else %} + {{ _('Disabled') }} + {{ _('System information sharing is disabled') }} + {% endif %} +
+ + + {{ _('This setting can be changed in') }} {{ _('System Settings') }}. + {{ _('License validation continues to work regardless of this setting.') }} + +
+
+
+
+
+
+
+
+
{% endblock %} diff --git a/templates/admin/restore.html b/templates/admin/restore.html index fb1309a..4039619 100644 --- a/templates/admin/restore.html +++ b/templates/admin/restore.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} -{% block title %}Restore Backup{% endblock %} +{% block title %}{{ _('Restore Backup') }}{% endblock %} {% block content %}
@@ -10,12 +10,12 @@

- Restore Backup + {{ _('Restore Backup') }}

- Danger Operation + {{ _('Danger Operation') }}
- Back to Dashboard + {{ _('Back to Dashboard') }}
@@ -26,18 +26,18 @@
- Upload Backup Archive (.zip) + {{ _('Upload Backup Archive (.zip)') }}
- Restoring a backup will overwrite your current database and files. Ensure you have a recent backup before proceeding. + {{ _('Restoring a backup will overwrite your current database and files. Ensure you have a recent backup before proceeding.') }}
{% if progress %}
- Status: + {{ _('Status:') }} {{ progress.status|title }} @@ -61,19 +61,19 @@ {% endif %}
- +
- Select a .zip archive previously created via the Backup action. + {{ _('Select a .zip archive previously created via the Backup action.') }}
- Cancel + {{ _('Cancel') }}
@@ -85,15 +85,15 @@
- Safety Tips + {{ _('Safety Tips') }}
    -
  • Verify the backup archive integrity before restoring.
  • -
  • Ensure no active writes are occurring during restore.
  • -
  • Keep a copy of the current data in case you need to roll back.
  • -
  • After restore, review settings and re-run migrations if required.
  • +
  • {{ _('Verify the backup archive integrity before restoring.') }}
  • +
  • {{ _('Ensure no active writes are occurring during restore.') }}
  • +
  • {{ _('Keep a copy of the current data in case you need to roll back.') }}
  • +
  • {{ _('After restore, review settings and re-run migrations if required.') }}
diff --git a/templates/admin/settings.html b/templates/admin/settings.html index 6b08008..0c92835 100644 --- a/templates/admin/settings.html +++ b/templates/admin/settings.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Settings - {{ app_name }}{% endblock %} +{% block title %}{{ _('Settings') }} - {{ app_name }}{% endblock %} {% block content %}
@@ -8,10 +8,10 @@

- System Settings + {{ _('System Settings') }}

- Back to Dashboard + {{ _('Back to Dashboard') }}
@@ -21,13 +21,13 @@
-
Configuration
+
{{ _('Configuration') }}
- + - Select your local timezone for proper time display. Times shown include DST adjustments. + {{ _('Select your local timezone for proper time display. Times shown include DST adjustments.') }}
- +
- +
- +
- +
- +
- +
- +
- +
@@ -212,53 +212,54 @@
- +
- +
- +
- +
- +
- +
- +
{% if settings and settings.has_logo() %}
- + - This will appear on invoices for payment instructions + {{ _('This will appear on invoices for payment instructions') }}
@@ -298,29 +299,29 @@
- Invoice Defaults + {{ _('Invoice Defaults') }}
- +
- +
- +
- +
@@ -329,7 +330,7 @@
- Privacy & Analytics + {{ _('Privacy & Analytics') }}
@@ -338,13 +339,13 @@
- When enabled, basic system information (OS, version, etc.) may be shared with the license server for monitoring purposes. - License validation will continue to work regardless of this setting. -
This helps improve the application and monitor system health. + {{ _('When enabled, basic system information (OS, version, etc.) may be shared with the metrics server for monitoring purposes.') }} + {{ _('Core functionality will continue to work regardless of this setting.') }} +
{{ _('This helps improve the application and monitor system health.') }}
@@ -355,12 +356,12 @@
- Current Time: - Loading... + {{ _('Current Time:') }} + {{ _('Loading...') }}
- in {{ settings.timezone if settings else 'Europe/Rome' }} timezone + {{ _('in') }} {{ settings.timezone if settings else 'Europe/Rome' }} {{ _('timezone') }}
@@ -369,13 +370,13 @@
- This time updates every second and shows the current time in your selected timezone + {{ _('This time updates every second and shows the current time in your selected timezone') }}
- Current offset: -- + {{ _('Current offset:') }} --
@@ -391,18 +392,18 @@
-
Help
+
{{ _('Help') }}
-

Configure application-wide settings such as timezone, currency, timer behavior, data export options, and company branding for invoices.

+

{{ _('Configure application-wide settings such as timezone, currency, timer behavior, data export options, and company branding for invoices.') }}

    -
  • Rounding affects how durations are rounded when displayed.
  • -
  • Single Active Timer stops any running timer when a new one is started.
  • -
  • Self Register allows new usernames to be created on login.
  • -
  • Company branding settings are used for PDF invoice generation.
  • -
  • Company logos can be uploaded directly through the interface (PNG, JPG, JPEG, GIF, SVG, WEBP formats supported).
  • -
  • Analytics setting controls whether system information is shared with the license server for monitoring purposes.
  • -
  • License validation will continue to work regardless of the analytics setting.
  • +
  • {{ _('Rounding affects how durations are rounded when displayed.') }}
  • +
  • {{ _('Single Active Timer stops any running timer when a new one is started.') }}
  • +
  • {{ _('Self Register allows new usernames to be created on login.') }}
  • +
  • {{ _('Company branding settings are used for PDF invoice generation.') }}
  • +
  • {{ _('Company logos can be uploaded directly through the interface (PNG, JPG, JPEG, GIF, SVG, WEBP formats supported).') }}
  • +
  • {{ _('Analytics setting controls whether system information is shared with the metrics server for monitoring purposes.') }}
  • +
  • {{ _('Core functionality will continue to work regardless of the analytics setting.') }}
@@ -410,7 +411,23 @@
+ + {% endblock %} diff --git a/templates/errors/400.html b/templates/errors/400.html index c26a8b0..3f6c5c3 100644 --- a/templates/errors/400.html +++ b/templates/errors/400.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}400 Bad Request - {{ app_name }}{% endblock %} +{% block title %}{{ _('400 Bad Request') }} - {{ app_name }}{% endblock %} {% block content %}
@@ -9,24 +9,24 @@

- 400 Bad Request + {{ _('400 Bad Request') }}

-
Invalid Request
+
{{ _('Invalid Request') }}

- The request you made is invalid or contains errors. This could be due to: + {{ _('The request you made is invalid or contains errors. This could be due to:') }}

    -
  • Missing or invalid form data
  • -
  • Malformed request parameters
  • +
  • {{ _('Missing or invalid form data') }}
  • +
  • {{ _('Malformed request parameters') }}
diff --git a/templates/errors/403.html b/templates/errors/403.html index e9f748d..77a3ef2 100644 --- a/templates/errors/403.html +++ b/templates/errors/403.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}403 Forbidden - {{ app_name }}{% endblock %} +{% block title %}{{ _('403 Forbidden') }} - {{ app_name }}{% endblock %} {% block content %}
@@ -9,25 +9,25 @@

- 403 Forbidden + {{ _('403 Forbidden') }}

-
Access Denied
+
{{ _('Access Denied') }}

- You don't have permission to access this resource. This could be due to: + {{ _("You don't have permission to access this resource. This could be due to:") }}

    -
  • Insufficient privileges
  • -
  • Not logged in
  • -
  • Resource access restrictions
  • +
  • {{ _('Insufficient privileges') }}
  • +
  • {{ _('Not logged in') }}
  • +
  • {{ _('Resource access restrictions') }}
diff --git a/templates/errors/404.html b/templates/errors/404.html index 8e9c7f0..f0f7f68 100644 --- a/templates/errors/404.html +++ b/templates/errors/404.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Page Not Found - {{ app_name }}{% endblock %} +{% block title %}{{ _('Page Not Found') }} - {{ app_name }}{% endblock %} {% block content %}
@@ -9,16 +9,16 @@

404

-

Page Not Found

+

{{ _('Page Not Found') }}

- The page you're looking for doesn't exist or has been moved. + {{ _("The page you're looking for doesn't exist or has been moved.") }}

diff --git a/templates/errors/500.html b/templates/errors/500.html index 82b6b34..02b1f69 100644 --- a/templates/errors/500.html +++ b/templates/errors/500.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Server Error - {{ app_name }}{% endblock %} +{% block title %}{{ _('Server Error') }} - {{ app_name }}{% endblock %} {% block content %}
@@ -9,16 +9,16 @@

500

-

Server Error

+

{{ _('Server Error') }}

- Something went wrong on our end. Please try again later. + {{ _('Something went wrong on our end. Please try again later.') }}

diff --git a/templates/invoices/create.html b/templates/invoices/create.html index e0901eb..e3d3900 100644 --- a/templates/invoices/create.html +++ b/templates/invoices/create.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Create Invoice - TimeTracker{% endblock %} +{% block title %}{{ _('Create Invoice') }} - TimeTracker{% endblock %} {% block content %}
@@ -11,14 +11,14 @@

- Create New Invoice + {{ _('Create New Invoice') }}

- Back to Invoices + {{ _('Back to Invoices') }}
@@ -30,22 +30,22 @@
1
- Basic Info + {{ _('Basic Info') }}
2
- Client Details + {{ _('Client Details') }}
3
- Settings + {{ _('Settings') }}
4
- Review + {{ _('Review') }}
@@ -60,7 +60,7 @@
- Step 1: Basic Information + {{ _('Step 1: Basic Information') }}
@@ -68,7 +68,7 @@
- +
- Select the project this invoice is for + {{ _('Select the project this invoice is for') }}
@@ -88,10 +88,10 @@
- +
- When payment is due + {{ _('When payment is due') }}
@@ -99,7 +99,7 @@
@@ -109,7 +109,7 @@ @@ -227,48 +227,48 @@
-
Select a project to see details
+
{{ _('Select a project to see details') }}
@@ -309,7 +309,7 @@
- Quick Tips + {{ _('Quick Tips') }}
@@ -318,9 +318,9 @@
- Time Integration + {{ _('Time Integration') }}

- Generate line items from tracked time entries after creation + {{ _('Generate line items from tracked time entries after creation') }}

@@ -330,9 +330,9 @@
- Auto Calculations + {{ _('Auto Calculations') }}

- Tax and totals computed automatically + {{ _('Tax and totals computed automatically') }}

@@ -342,9 +342,9 @@
- Export Options + {{ _('Export Options') }}

- CSV export and PDF generation available + {{ _('CSV export and PDF generation available') }}

@@ -503,7 +503,7 @@ document.addEventListener('DOMContentLoaded', function() { noProjectInfo.classList.add('d-none'); projectClient.textContent = clientName; projectRate.textContent = '{{ settings.currency or "EUR" }}' + hourlyRate.toFixed(2); - projectBillable.textContent = hourlyRate > 0 ? 'Yes' : 'No'; + projectBillable.textContent = hourlyRate > 0 ? '{{ _('Yes') }}' : '{{ _('No') }}'; } else { projectInfo.classList.add('d-none'); noProjectInfo.classList.remove('d-none'); @@ -520,7 +520,7 @@ document.addEventListener('DOMContentLoaded', function() { invoiceForm.addEventListener('submit', function(e) { if (!validateForm()) { e.preventDefault(); - showAlert('Please fill in all required fields and complete all steps.', 'warning'); + showAlert('{{ _('Please fill in all required fields and complete all steps.') }}', 'warning'); } }); }); @@ -601,7 +601,7 @@ function validateCurrentStep() { } if (!isValid) { - showAlert('Please fill in all required fields before proceeding.', 'warning'); + showAlert('{{ _('Please fill in all required fields before proceeding.') }}', 'warning'); } return isValid; diff --git a/templates/invoices/edit.html b/templates/invoices/edit.html index 0af5113..cfdb138 100644 --- a/templates/invoices/edit.html +++ b/templates/invoices/edit.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Edit Invoice {{ invoice.invoice_number }} - TimeTracker{% endblock %} +{% block title %}{{ _('Edit Invoice') }} {{ invoice.invoice_number }} - TimeTracker{% endblock %} {% block content %}
@@ -9,14 +9,14 @@

- Edit Invoice {{ invoice.invoice_number }} + {{ _('Edit Invoice') }} {{ invoice.invoice_number }}

@@ -27,20 +27,20 @@
-
Invoice Details
+
{{ _('Invoice Details') }}
- +
- +
@@ -48,7 +48,7 @@
- +
@@ -56,14 +56,14 @@
- +
- +
@@ -71,13 +71,13 @@
- +
- +
@@ -87,9 +87,9 @@
-
Invoice Items
+
{{ _('Invoice Items') }}
@@ -97,11 +97,11 @@ - - - - - + + + + + @@ -136,7 +136,7 @@ - + - +
Description *Quantity (Hours) *Unit Price *TotalActions{{ _('Description *') }}{{ _('Quantity (Hours) *') }}{{ _('Unit Price *') }}{{ _('Total') }}{{ _('Actions') }}
Subtotal:{{ _('Subtotal:') }} @@ -145,7 +145,7 @@
- Tax ({{ "%.2f"|format(invoice.tax_rate) }}%): + {{ _('Tax') }} ({{ "%.2f"|format(invoice.tax_rate) }}%):
Total Amount:{{ _('Total Amount:') }} @@ -172,29 +172,29 @@
-
Project Information
+
{{ _('Project Information') }}
- Project:
+ {{ _('Project:') }}
{{ invoice.project.name }}
- Client:
+ {{ _('Client:') }}
{{ invoice.project.client }}
- Hourly Rate:
+ {{ _('Hourly Rate:') }}
{% if invoice.project.hourly_rate %} {{ "%.2f"|format(invoice.project.hourly_rate) }} {{ currency }} {% else %} - Not set + {{ _('Not set') }} {% endif %}
- Billing Reference:
+ {{ _('Billing Reference:') }}
{{ invoice.project.billing_ref or 'None' }}
@@ -203,23 +203,23 @@
-
Quick Actions
+
{{ _('Quick Actions') }}
@@ -228,24 +228,24 @@
-
Totals Summary
+
{{ _('Totals Summary') }}
-
Subtotal:
+
{{ _('Subtotal:') }}
{{ "%.2f"|format(invoice.subtotal) }} {{ currency }}
-
Tax:
+
{{ _('Tax:') }}
{{ "%.2f"|format(invoice.tax_amount) }} {{ currency }}

-
Total:
+
{{ _('Total:') }}
{{ "%.2f"|format(invoice.total_amount) }} {{ currency }}
@@ -262,10 +262,10 @@
- Cancel + {{ _('Cancel') }}
@@ -316,7 +316,7 @@ function removeItemRow(button) { $(button).closest('tr').remove(); calculateTotals(); } else { - alert('At least one item is required.'); + alert('{{ _('At least one item is required.') }}'); } } @@ -362,7 +362,7 @@ function useProjectRate() { calculateRowTotal(this); }); } else { - alert('No hourly rate set for this project.'); + alert('{{ _('No hourly rate set for this project.') }}'); } } diff --git a/templates/invoices/generate_from_time.html b/templates/invoices/generate_from_time.html index fc8bee3..99b7ccf 100644 --- a/templates/invoices/generate_from_time.html +++ b/templates/invoices/generate_from_time.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Generate Invoice from Time - TimeTracker{% endblock %} +{% block title %}{{ _('Generate Invoice from Time') }} - TimeTracker{% endblock %} {% block content %}
@@ -11,12 +11,12 @@

- Generate Invoice Items from Time Entries + {{ _('Generate Invoice Items from Time Entries') }}

- Invoice #{{ invoice.invoice_number }} + {{ _('Invoice #') }}{{ invoice.invoice_number }}
- Back to Invoice + {{ _('Back to Invoice') }}
@@ -28,25 +28,25 @@
-
Client
+
{{ _('Client') }}
{{ invoice.client_name }}
-
Project
+
{{ _('Project') }}
{{ invoice.project.name }}
-
Hourly Rate
+
{{ _('Hourly Rate') }}
{{ "%.2f"|format(invoice.project.hourly_rate or 0) }} {{ currency }}
-
Available Hours
+
{{ _('Available Hours') }}
{{ "%.2f"|format(total_available_hours) }}h
@@ -62,14 +62,14 @@
- Select Time Entries + {{ _('Select Time Entries') }}
@@ -83,17 +83,17 @@
- 0 selected + 0 {{ _('selected') }} - 0.00 hours + 0.00 {{ _('hours') }}
@@ -104,12 +104,12 @@ - - - - - - + + + + + + @@ -150,11 +150,11 @@
{{ entry.notes[:50] }}{% if entry.notes|length > 50 %}...{% endif %}
{% if entry.notes|length > 50 %} - Click to view full notes + {{ _('Click to view full notes') }} {% endif %}
{% else %} - No notes + {{ _('No notes') }} {% endif %} @@ -168,12 +168,12 @@
- Summary: - 0 entries selected, - 0.00 hours total + {{ _('Summary:') }} + 0 {{ _('entries selected,') }} + 0.00 {{ _('hours total') }}
- Selected entries will be converted to invoice line items with the project's hourly rate. + {{ _("Selected entries will be converted to invoice line items with the project's hourly rate.") }}
@@ -183,10 +183,10 @@
- Cancel + {{ _('Cancel') }}
@@ -194,16 +194,16 @@
-
No time entries found
+
{{ _('No time entries found') }}

- There are no time entries available for this project to generate invoice items from. + {{ _('There are no time entries available for this project to generate invoice items from.') }}

@@ -218,22 +218,22 @@
- Quick Actions + {{ _('Quick Actions') }}
@@ -243,7 +243,7 @@
- Tips + {{ _('Tips') }}
@@ -252,9 +252,9 @@
- Smart Selection + {{ _('Smart Selection') }}

- Use quick actions to select time entries by date ranges or task types + {{ _('Use quick actions to select time entries by date ranges or task types') }}

@@ -264,9 +264,9 @@
- Automatic Calculation + {{ _('Automatic Calculation') }}

- Line items are automatically calculated using the project's hourly rate + {{ _("Line items are automatically calculated using the project's hourly rate") }}

@@ -276,9 +276,9 @@
- Review & Edit + {{ _('Review & Edit') }}

- You can edit generated items before finalizing the invoice + {{ _('You can edit generated items before finalizing the invoice') }}

@@ -460,7 +460,7 @@ document.addEventListener('DOMContentLoaded', function() { const selectedCount = document.querySelectorAll('.time-entry-checkbox:checked').length; if (selectedCount === 0) { e.preventDefault(); - showAlert('Please select at least one time entry to generate invoice items.', 'warning'); + showAlert('{{ _('Please select at least one time entry to generate invoice items.') }}', 'warning'); } }); } diff --git a/templates/invoices/list.html b/templates/invoices/list.html index e413e51..c26b28f 100644 --- a/templates/invoices/list.html +++ b/templates/invoices/list.html @@ -231,7 +231,7 @@ + data-confirm="{{ _('Are you sure you want to delete this invoice? This action cannot be undone.') }}"> @@ -406,6 +406,21 @@ {% endblock %} {% block scripts %} + + {% endblock %} diff --git a/templates/invoices/view.html b/templates/invoices/view.html index 7c391f8..4a774b1 100644 --- a/templates/invoices/view.html +++ b/templates/invoices/view.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Invoice {{ invoice.invoice_number }} - TimeTracker{% endblock %} +{% block title %}{{ _('Invoice') }} {{ invoice.invoice_number }} - TimeTracker{% endblock %} {% block content %}
@@ -11,48 +11,48 @@

- Invoice {{ invoice.invoice_number }} + {{ _('Invoice') }} {{ invoice.invoice_number }}

{% set status_config = { - 'draft': {'color': 'secondary', 'icon': 'edit', 'bg': 'bg-secondary'}, - 'sent': {'color': 'info', 'icon': 'paper-plane', 'bg': 'bg-info'}, - 'paid': {'color': 'success', 'icon': 'check-circle', 'bg': 'bg-success'}, - 'overdue': {'color': 'danger', 'icon': 'exclamation-triangle', 'bg': 'bg-danger'}, - 'cancelled': {'color': 'dark', 'icon': 'times-circle', 'bg': 'bg-dark'} + 'draft': {'color': 'secondary', 'icon': 'edit', 'bg': 'bg-secondary', 'label': _('Draft')}, + 'sent': {'color': 'info', 'icon': 'paper-plane', 'bg': 'bg-info', 'label': _('Sent')}, + 'paid': {'color': 'success', 'icon': 'check-circle', 'bg': 'bg-success', 'label': _('Paid')}, + 'overdue': {'color': 'danger', 'icon': 'exclamation-triangle', 'bg': 'bg-danger', 'label': _('Overdue')}, + 'cancelled': {'color': 'dark', 'icon': 'times-circle', 'bg': 'bg-dark', 'label': _('Cancelled')} } %} {% set config = status_config.get(invoice.status, status_config.draft) %} - {{ invoice.status|title }} + {{ config.label }}
@@ -66,7 +66,7 @@
- Bill To + {{ _('Bill To') }}
{{ invoice.client_name }}
@@ -88,29 +88,29 @@
- Invoice Details + {{ _('Invoice Details') }}
- Invoice #: + {{ _('Invoice #:') }} {{ invoice.invoice_number }}
- Project: + {{ _('Project:') }} {{ invoice.project.name }}
- Issue Date: + {{ _('Issue Date:') }} {{ invoice.issue_date.strftime('%B %d, %Y') }}
- Due Date: + {{ _('Due Date:') }} {{ invoice.due_date.strftime('%B %d, %Y') }} {% if invoice.is_overdue %} - {{ invoice.days_overdue }} days overdue + {{ invoice.days_overdue }} {{ _('days overdue') }} {% endif %} @@ -127,7 +127,7 @@
- Status & Actions + {{ _('Status & Actions') }}
@@ -136,7 +136,7 @@ {% endif %} @@ -144,21 +144,21 @@
-
Created by
+
{{ _('Created by') }}
{{ invoice.creator.display_name }}
-
Created
+
{{ _('Created') }}
{{ invoice.created_at.strftime('%B %d, %Y') }}
-
Last Modified
+
{{ _('Last Modified') }}
{{ invoice.updated_at.strftime('%B %d, %Y') }}
@@ -176,7 +176,7 @@
-
Subtotal
+
{{ _('Subtotal') }}
{{ "%.2f"|format(invoice.subtotal) }} {{ currency }}
@@ -188,7 +188,7 @@
-
Tax ({{ "%.2f"|format(invoice.tax_rate) }}%)
+
{{ _('Tax') }} ({{ "%.2f"|format(invoice.tax_rate) }}%)
{{ "%.2f"|format(invoice.tax_amount) }} {{ currency }}
@@ -200,7 +200,7 @@
-
Total Amount
+
{{ _('Total Amount') }}
{{ "%.2f"|format(invoice.total_amount) }} {{ currency }}
@@ -214,12 +214,12 @@
- Invoice Items - {{ invoice.items.count() }} items + {{ _('Invoice Items') }} + {{ invoice.items.count() }} {{ _('items') }}
{% if invoice.status == 'draft' %} - Edit Items + {{ _('Edit Items') }} {% endif %}
@@ -229,10 +229,10 @@
SelectDateUserTaskDurationNotes{{ _('Select') }}{{ _('Date') }}{{ _('User') }}{{ _('Task') }}{{ _('Duration') }}{{ _('Notes') }}
- - - - + + + + @@ -244,7 +244,7 @@ {% if item.time_entry_ids %}
- Generated from {{ item.time_entry_ids.split(',')|length }} time entries + {{ _('Generated from') }} {{ item.time_entry_ids.split(',')|length }} {{ _('time entries') }}
{% endif %} @@ -264,7 +264,7 @@
DescriptionQuantity (Hours)Unit PriceTotal Amount{{ _('Description') }}{{ _('Quantity (Hours)') }}{{ _('Unit Price') }}{{ _('Total Amount') }}
- Subtotal: + {{ _('Subtotal:') }} {{ "%.2f"|format(invoice.subtotal) }} {{ currency }} @@ -273,7 +273,7 @@ {% if invoice.tax_rate > 0 %}
- Tax ({{ "%.2f"|format(invoice.tax_rate) }}%): + {{ _('Tax') }} ({{ "%.2f"|format(invoice.tax_rate) }}%): {{ "%.2f"|format(invoice.tax_amount) }} {{ currency }} @@ -282,7 +282,7 @@ {% endif %}
- Total Amount: + {{ _('Total Amount:') }} {{ "%.2f"|format(invoice.total_amount) }} {{ currency }} @@ -295,11 +295,11 @@
-
No invoice items
-

Add items to this invoice to generate totals.

+
{{ _('No invoice items') }}
+

{{ _('Add items to this invoice to generate totals.') }}

{% if invoice.status == 'draft' %} - Add Items + {{ _('Add Items') }} {% endif %}
@@ -317,14 +317,14 @@
- Additional Information + {{ _('Additional Information') }}
{% if invoice.notes %}
-
Notes
+
{{ _('Notes') }}
{{ invoice.notes|nl2br }}
@@ -332,7 +332,7 @@ {% endif %} {% if invoice.terms %}
-
Terms & Conditions
+
{{ _('Terms & Conditions') }}
{{ invoice.terms|nl2br }}
@@ -353,28 +353,28 @@