mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-02-19 20:18:42 -06:00
Features: Add favorite projects feature allowing users to star/bookmark frequently used projects New UserFavoriteProject association model with user-project relationships Star icons in project list for one-click favorite toggling via AJAX Filter to display only favorite projects Per-user favorites with proper isolation and cascade delete behavior Activity logging for favorite/unfavorite actions Database: Add user_favorite_projects table with migration (023_add_user_favorite_projects.py) Foreign keys to users and projects with CASCADE delete Unique constraint preventing duplicate favorites Indexes on user_id and project_id for query optimization Models: User model: Add favorite_projects relationship with helper methods add_favorite_project() - add project to favorites remove_favorite_project() - remove from favorites is_project_favorite() - check favorite status get_favorite_projects() - retrieve favorites with status filter Project model: Add is_favorited_by() method and include favorite status in to_dict() Export UserFavoriteProject model in app/models/__init__.py Routes: Add /projects/<id>/favorite POST endpoint to favorite a project Add /projects/<id>/unfavorite POST endpoint to unfavorite a project Update /projects GET route to support favorites=true query parameter Fix status filtering to work correctly with favorites JOIN query Add /reports/export/form GET endpoint for enhanced CSV export form Templates: Update projects/list.html: Add favorites filter dropdown to filter form (5-column grid) Add star icon column with Font Awesome icons (filled/unfilled) Add JavaScript toggleFavorite() function for AJAX favorite toggling Improve hover states and transitions for better UX Pass favorite_project_ids and favorites_only to template Update reports/index.html: Update CSV export link to point to new export form Add icon and improve hover styling Reports: Enhance CSV export functionality with dedicated form page Add filter options for users, projects, clients, and date ranges Set default date range to last 30 days Import Client model and or_ operator for advanced filtering Testing: Comprehensive test suite in tests/test_favorite_projects.py (550+ lines) Model tests for UserFavoriteProject creation and validation User/Project method tests for favorite operations Route tests for favorite/unfavorite endpoints Filtering tests for favorites-only view Relationship tests for cascade delete behavior Smoke tests for complete workflows Coverage for edge cases and error handling Documentation: Add comprehensive feature documentation in docs/FAVORITE_PROJECTS_FEATURE.md User guide with step-by-step instructions Technical implementation details API documentation for new endpoints Migration guide and troubleshooting Performance and security considerations Template Cleanup: Remove duplicate templates from root templates/ directory Admin templates (dashboard, users, settings, OIDC debug, etc.) Client CRUD templates Error page templates Invoice templates Project templates Report templates Timer templates All templates now properly located in app/templates/ Breaking Changes: None - fully backward compatible Migration Required: Run alembic upgrade head to create user_favorite_projects table
622 lines
48 KiB
HTML
622 lines
48 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ _('Help') }} - {{ app_name }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold">{{ _('Help') }}</h1>
|
|
<p class="text-text-muted-light dark:text-text-muted-dark">{{ _('Complete documentation and user guide') }}</p>
|
|
</div>
|
|
<div class="text-sm text-text-muted-light dark:text-text-muted-dark mt-2 md:mt-0">
|
|
<i class="fas fa-question-circle"></i> / {{ _('Help') }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-5 gap-6">
|
|
<!-- Left sticky navigation -->
|
|
<aside class="lg:col-span-1">
|
|
<div class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark rounded-lg shadow p-4 lg:sticky lg:top-4">
|
|
<h3 class="font-semibold mb-3"><i class="fas fa-list mr-2"></i>{{ _('Quick Navigation') }}</h3>
|
|
<div class="relative mb-3">
|
|
<i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
|
|
<input id="help-nav-filter" type="text" placeholder="{{ _('Filter sections...') }}" class="w-full pl-9 pr-3 py-2 text-sm rounded border border-border-light dark:border-border-dark bg-background-light dark:bg-background-dark" />
|
|
</div>
|
|
<nav id="help-nav" class="flex flex-col text-sm">
|
|
<a class="help-link py-1 hover:text-primary" href="#quick-start"><i class="fas fa-rocket mr-2"></i>{{ _('Quick Start') }}</a>
|
|
<a class="help-link py-1 hover:text-primary" href="#time-tracking"><i class="fas fa-stopwatch mr-2"></i>{{ _('Time Tracking') }}</a>
|
|
<a class="help-link py-1 hover:text-primary" href="#project-management"><i class="fas fa-project-diagram mr-2"></i>{{ _('Project Management') }}</a>
|
|
<a class="help-link py-1 hover:text-primary" href="#client-management"><i class="fas fa-building mr-2"></i>{{ _('Client Management') }}</a>
|
|
<a class="help-link py-1 hover:text-primary" href="#task-management"><i class="fas fa-tasks mr-2"></i>{{ _('Task Management') }}</a>
|
|
<a class="help-link py-1 hover:text-primary" href="#invoicing"><i class="fas fa-file-invoice-dollar mr-2"></i>{{ _('Invoicing') }}</a>
|
|
<a class="help-link py-1 hover:text-primary" href="#reports-analytics"><i class="fas fa-chart-line mr-2"></i>{{ _('Reports & Analytics') }}</a>
|
|
{% if current_user.is_admin %}
|
|
<a class="help-link py-1 hover:text-primary" href="#admin-features"><i class="fas fa-cog mr-2"></i>{{ _('Admin Features') }}</a>
|
|
{% endif %}
|
|
<a class="help-link py-1 hover:text-primary" href="#mobile-usage"><i class="fas fa-mobile-alt mr-2"></i>{{ _('Mobile Usage') }}</a>
|
|
<a class="help-link py-1 hover:text-primary" href="#troubleshooting"><i class="fas fa-tools mr-2"></i>{{ _('Troubleshooting') }}</a>
|
|
</nav>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Main content -->
|
|
<section class="lg:col-span-4 space-y-8">
|
|
<!-- Hero -->
|
|
<div class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow text-center">
|
|
<h2 class="text-xl font-semibold mb-1">{{ _('TimeTracker Help Center') }}</h2>
|
|
<p class="text-text-muted-light dark:text-text-muted-dark">{{ _('Everything you need to know to get the most out of TimeTracker') }}</p>
|
|
|
|
<div class="flex flex-wrap gap-2 justify-center mt-4">
|
|
<a href="{{ url_for('timer.manual_entry') }}" class="px-3 py-2 rounded-lg bg-primary text-white text-sm hover:opacity-90">
|
|
<i class="fas fa-play mr-1"></i>{{ _('Start Tracking Time') }}
|
|
</a>
|
|
<a href="{{ url_for('projects.list_projects') }}" class="px-3 py-2 rounded-lg border border-border-light dark:border-border-dark text-sm hover:bg-background-light dark:hover:bg-background-dark">
|
|
<i class="fas fa-project-diagram mr-1"></i>{{ _('View Projects') }}
|
|
</a>
|
|
<a href="{{ url_for('reports.reports') }}" class="px-3 py-2 rounded-lg border border-sky-600 text-sky-600 text-sm hover:bg-sky-50 dark:hover:bg-sky-900/20">
|
|
<i class="fas fa-chart-bar mr-1"></i>{{ _('Generate Reports') }}
|
|
</a>
|
|
{% if current_user.is_admin %}
|
|
<a href="{{ url_for('admin.settings') }}" class="px-3 py-2 rounded-lg border border-amber-600 text-amber-600 text-sm hover:bg-amber-50 dark:hover:bg-amber-900/20">
|
|
<i class="fas fa-cog mr-1"></i>{{ _('System Settings') }}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Start -->
|
|
<section id="quick-start" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-rocket text-primary mr-2"></i>{{ _('Quick Start Guide') }}</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('For New Users') }}</h4>
|
|
<ol class="list-decimal ml-5 space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Log in with your username (no password required)') }}</li>
|
|
<li>{{ _('Explore the dashboard to see your overview') }}</li>
|
|
<li>{{ _('Start your first timer on an existing project') }}</li>
|
|
<li>{{ _('View your time entries in reports') }}</li>
|
|
</ol>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('For Administrators') }}</h4>
|
|
<ol class="list-decimal ml-5 space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Set up clients in the Client Management section') }}</li>
|
|
<li>{{ _('Create projects and assign them to clients') }}</li>
|
|
<li>{{ _('Configure system settings (timezone, currency, etc.)') }}</li>
|
|
<li>{{ _('Manage users and permissions') }}</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 flex items-start gap-3 p-3 rounded-lg border border-sky-600/30 bg-sky-50 dark:bg-sky-900/20 text-sm">
|
|
<i class="fas fa-lightbulb text-sky-600 mt-0.5"></i>
|
|
<p class="text-text-muted-light dark:text-text-muted-dark"><strong>{{ _('Pro Tip:') }}</strong> {{ _('Use the mobile-friendly interface to track time on the go. The timer continues running even if you close your browser!') }}</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Time Tracking -->
|
|
<section id="time-tracking" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-stopwatch text-green-600 mr-2"></i>{{ _('Time Tracking') }}</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mb-4">
|
|
<div class="bg-background-light dark:bg-background-dark/40 p-4 rounded border border-border-light dark:border-border-dark">
|
|
<h4 class="font-semibold mb-1">{{ _('Smart Timers') }}</h4>
|
|
<p class="text-text-muted-light dark:text-text-muted-dark">{{ _('Real-time tracking with automatic idle detection and WebSocket updates') }}</p>
|
|
</div>
|
|
<div class="bg-background-light dark:bg-background-dark/40 p-4 rounded border border-border-light dark:border-border-dark">
|
|
<h4 class="font-semibold mb-1">{{ _('Manual Entry') }}</h4>
|
|
<p class="text-text-muted-light dark:text-text-muted-dark">{{ _('Log time manually with custom start and end times') }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Starting a Timer') }}</h4>
|
|
<ol class="list-decimal ml-5 space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Navigate to the Timer page or dashboard') }}</li>
|
|
<li>{{ _('Select a project from the dropdown') }}</li>
|
|
<li>{{ _('Optionally select a task for more detailed tracking') }}</li>
|
|
<li>{{ _('Add notes about what you\'re working on (optional)') }}</li>
|
|
<li>{{ _('Add tags separated by commas (optional)') }}</li>
|
|
<li>{{ _('Click "Start Timer"') }}</li>
|
|
</ol>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Timer Features') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('Real-time duration display') }}</li>
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('Continues running if browser closes') }}</li>
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('Automatic idle detection (configurable)') }}</li>
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('One active timer per user (configurable)') }}</li>
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('WebSocket live updates') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="mt-6">
|
|
<h4 class="font-semibold mb-2">{{ _('Manual Time Entry') }}</h4>
|
|
<p class="text-sm text-text-muted-light dark:text-text-muted-dark mb-3">{{ _('Create time entries manually when you need to record time spent away from the computer or adjust existing entries.') }}</p>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h5 class="font-semibold">{{ _('Required Information') }}</h5>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Project selection') }}</li>
|
|
<li>{{ _('Start date and time') }}</li>
|
|
<li>{{ _('End date and time') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h5 class="font-semibold">{{ _('Optional Information') }}</h5>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Task assignment') }}</li>
|
|
<li>{{ _('Description/notes') }}</li>
|
|
<li>{{ _('Tags for categorization') }}</li>
|
|
<li>{{ _('Billable status override') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Project Management -->
|
|
<section id="project-management" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-project-diagram text-sky-600 mr-2"></i>{{ _('Project Management') }}</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Project Information') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-tag text-primary mr-2"></i><strong>{{ _('Name') }}:</strong> {{ _('Descriptive project name') }}</li>
|
|
<li><i class="fas fa-building text-green-600 mr-2"></i><strong>{{ _('Client') }}:</strong> {{ _('Associated client organization') }}</li>
|
|
<li><i class="fas fa-align-left text-sky-600 mr-2"></i><strong>{{ _('Description') }}:</strong> {{ _('Project details and scope') }}</li>
|
|
<li><i class="fas fa-calendar text-amber-600 mr-2"></i><strong>{{ _('Status') }}:</strong> {{ _('Active, completed, or archived') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Billing Information') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-dollar-sign text-green-600 mr-2"></i><strong>{{ _('Billable') }}:</strong> {{ _('Whether time should be tracked for billing') }}</li>
|
|
<li><i class="fas fa-money-bill text-sky-600 mr-2"></i><strong>{{ _('Hourly Rate') }}:</strong> {{ _('Rate for billable time calculations') }}</li>
|
|
<li><i class="fas fa-hashtag text-amber-600 mr-2"></i><strong>{{ _('Billing Reference') }}:</strong> {{ _('PO number or billing code') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
{% if current_user.is_admin %}
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mt-6">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Project Operations') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Create new projects with client relationships') }}</li>
|
|
<li>{{ _('Edit existing projects to update details') }}</li>
|
|
<li>{{ _('Archive projects to hide from timers (preserves data)') }}</li>
|
|
<li>{{ _('Delete projects (removes all associated time entries)') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Best Practices') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Use descriptive project names') }}</li>
|
|
<li>{{ _('Set accurate hourly rates for billing') }}</li>
|
|
<li>{{ _('Archive instead of delete when possible') }}</li>
|
|
<li>{{ _('Use billing references for external tracking') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<!-- Client Management -->
|
|
<section id="client-management" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-building text-amber-600 mr-2"></i>{{ _('Client Management') }}</h3>
|
|
<p class="text-sm text-text-muted-light dark:text-text-muted-dark mb-4">{{ _('The client management system helps organize your work by client organizations, preventing errors and streamlining project creation.') }}</p>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Client Information') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-building text-primary mr-2"></i><strong>{{ _('Organization Name') }}:</strong> {{ _('Company or client name') }}</li>
|
|
<li><i class="fas fa-user text-green-600 mr-2"></i><strong>{{ _('Contact Person') }}:</strong> {{ _('Primary contact details') }}</li>
|
|
<li><i class="fas fa-envelope text-sky-600 mr-2"></i><strong>{{ _('Email & Phone') }}:</strong> {{ _('Contact information') }}</li>
|
|
<li><i class="fas fa-map-marker-alt text-amber-600 mr-2"></i><strong>{{ _('Address') }}:</strong> {{ _('Business address') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Benefits') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('Dropdown selection prevents typos') }}</li>
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('Default rates auto-populate projects') }}</li>
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('Consistent client naming') }}</li>
|
|
<li><i class="fas fa-check text-green-600 mr-2"></i>{{ _('Easier project organization') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 text-sm mt-6">
|
|
<div class="text-center p-4 rounded border border-border-light dark:border-border-dark">
|
|
<i class="fas fa-project-diagram text-primary mb-2"></i>
|
|
<h5 class="font-semibold">{{ _('Project Count') }}</h5>
|
|
<p class="text-xs text-text-muted-light dark:text-text-muted-dark">{{ _('Total and active projects') }}</p>
|
|
</div>
|
|
<div class="text-center p-4 rounded border border-border-light dark:border-border-dark">
|
|
<i class="fas fa-clock text-green-600 mb-2"></i>
|
|
<h5 class="font-semibold">{{ _('Time Tracking') }}</h5>
|
|
<p class="text-xs text-text-muted-light dark:text-text-muted-dark">{{ _('Total hours worked') }}</p>
|
|
</div>
|
|
<div class="text-center p-4 rounded border border-border-light dark:border-border-dark">
|
|
<i class="fas fa-dollar-sign text-sky-600 mb-2"></i>
|
|
<h5 class="font-semibold">{{ _('Cost Estimation') }}</h5>
|
|
<p class="text-xs text-text-muted-light dark:text-text-muted-dark">{{ _('Estimated billing amounts') }}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Task Management -->
|
|
<section id="task-management" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-tasks text-red-500 mr-2"></i>{{ _('Task Management') }}</h3>
|
|
<p class="text-sm text-text-muted-light dark:text-text-muted-dark mb-4">{{ _('Break down projects into manageable tasks with detailed tracking and progress monitoring.') }}</p>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Task Properties') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-tag text-primary mr-2"></i><strong>{{ _('Name & Description') }}:</strong> {{ _('Clear task identification') }}</li>
|
|
<li><i class="fas fa-flag text-amber-600 mr-2"></i><strong>{{ _('Priority Levels') }}:</strong> {{ _('Low, Medium, High, Urgent') }}</li>
|
|
<li><i class="fas fa-calendar text-sky-600 mr-2"></i><strong>{{ _('Due Dates') }}:</strong> {{ _('Deadline tracking') }}</li>
|
|
<li><i class="fas fa-user text-green-600 mr-2"></i><strong>{{ _('Assignment') }}:</strong> {{ _('Task ownership') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Status Tracking') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-circle text-gray-400 mr-2"></i>{{ _('To Do - Not started') }}</li>
|
|
<li><i class="fas fa-circle text-primary mr-2"></i>{{ _('In Progress - Currently working') }}</li>
|
|
<li><i class="fas fa-circle text-amber-500 mr-2"></i>{{ _('Review - Ready for review') }}</li>
|
|
<li><i class="fas fa-circle text-green-600 mr-2"></i>{{ _('Done - Completed') }}</li>
|
|
<li><i class="fas fa-circle text-red-500 mr-2"></i>{{ _('Cancelled - Not needed') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mt-6">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Time Tracking Features') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Start timers directly from tasks') }}</li>
|
|
<li>{{ _('Link time entries to specific tasks') }}</li>
|
|
<li>{{ _('Track estimated vs actual hours') }}</li>
|
|
<li>{{ _('Monitor task progress automatically') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Task Views') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('My Tasks - Your assigned tasks') }}</li>
|
|
<li>{{ _('All Tasks - Complete task list') }}</li>
|
|
<li>{{ _('Overdue Tasks - Past due items') }}</li>
|
|
<li>{{ _('Project Tasks - Tasks within projects') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Invoicing -->
|
|
<section id="invoicing" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-file-invoice-dollar text-green-600 mr-2"></i>{{ _('Professional Invoicing') }}</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mb-4">
|
|
<div class="bg-background-light dark:bg-background-dark/40 p-4 rounded border border-border-light dark:border-border-dark">
|
|
<h4 class="font-semibold mb-1">{{ _('Core Features') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-file-pdf text-red-500 mr-2"></i>{{ _('Professional PDF generation') }}</li>
|
|
<li><i class="fas fa-palette text-primary mr-2"></i>{{ _('Company branding and logos') }}</li>
|
|
<li><i class="fas fa-hashtag text-sky-600 mr-2"></i>{{ _('Automatic invoice numbering') }}</li>
|
|
<li><i class="fas fa-calculator text-green-600 mr-2"></i>{{ _('Tax calculations') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-background-light dark:bg-background-dark/40 p-4 rounded border border-border-light dark:border-border-dark">
|
|
<h4 class="font-semibold mb-1">{{ _('Time Integration') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-clock text-amber-600 mr-2"></i>{{ _('Generate from time entries') }}</li>
|
|
<li><i class="fas fa-layer-group text-sky-600 mr-2"></i>{{ _('Smart grouping by task/project') }}</li>
|
|
<li><i class="fas fa-shield-alt text-green-600 mr-2"></i>{{ _('Prevent double-billing') }}</li>
|
|
<li><i class="fas fa-money-bill text-primary mr-2"></i>{{ _('Use project hourly rates') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
|
<div class="text-center p-4 rounded border border-border-light dark:border-border-dark">
|
|
<div class="mx-auto mb-2 w-10 h-10 rounded-full flex items-center justify-center bg-primary/10"><span class="font-bold text-primary">1</span></div>
|
|
<h5 class="font-semibold">{{ _('Create Invoice') }}</h5>
|
|
<p class="text-xs text-text-muted-light dark:text-text-muted-dark">{{ _('Set up client and project details') }}</p>
|
|
</div>
|
|
<div class="text-center p-4 rounded border border-border-light dark:border-border-dark">
|
|
<div class="mx-auto mb-2 w-10 h-10 rounded-full flex items-center justify-center bg-green-600/10"><span class="font-bold text-green-600">2</span></div>
|
|
<h5 class="font-semibold">{{ _('Add Items') }}</h5>
|
|
<p class="text-xs text-text-muted-light dark:text-text-muted-dark">{{ _('Generate from time or add manually') }}</p>
|
|
</div>
|
|
<div class="text-center p-4 rounded border border-border-light dark:border-border-dark">
|
|
<div class="mx-auto mb-2 w-10 h-10 rounded-full flex items-center justify-center bg-sky-600/10"><span class="font-bold text-sky-600">3</span></div>
|
|
<h5 class="font-semibold">{{ _('Review & Send') }}</h5>
|
|
<p class="text-xs text-text-muted-light dark:text-text-muted-dark">{{ _('Export PDF and update status') }}</p>
|
|
</div>
|
|
<div class="text-center p-4 rounded border border-border-light dark:border-border-dark">
|
|
<div class="mx-auto mb-2 w-10 h-10 rounded-full flex items-center justify-center bg-amber-600/10"><span class="font-bold text-amber-600">4</span></div>
|
|
<h5 class="font-semibold">{{ _('Track Payment') }}</h5>
|
|
<p class="text-xs text-text-muted-light dark:text-text-muted-dark">{{ _('Monitor status and payments') }}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Reports & Analytics -->
|
|
<section id="reports-analytics" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-chart-line text-sky-600 mr-2"></i>{{ _('Reports & Analytics') }}</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mb-4">
|
|
<div class="bg-background-light dark:bg-background-dark/40 p-4 rounded border border-border-light dark:border-border-dark">
|
|
<h4 class="font-semibold mb-1">{{ _('Standard Reports') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Project Report - Time breakdown by project') }}</li>
|
|
<li>{{ _('User Report - Individual performance metrics') }}</li>
|
|
<li>{{ _('Summary Report - Key metrics and trends') }}</li>
|
|
<li>{{ _('Time Entry Report - Detailed time logs') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-background-light dark:bg-background-dark/40 p-4 rounded border border-border-light dark:border-border-dark">
|
|
<h4 class="font-semibold mb-1">{{ _('Export Options') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('CSV Export - For external analysis') }}</li>
|
|
<li>{{ _('Configurable delimiters') }}</li>
|
|
<li>{{ _('Custom date ranges') }}</li>
|
|
<li>{{ _('Filtered data export') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Filter Options') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-calendar text-primary mr-2"></i>{{ _('Date Range - Custom start and end dates') }}</li>
|
|
<li><i class="fas fa-project-diagram text-green-600 mr-2"></i>{{ _('Project - Filter by specific projects') }}</li>
|
|
<li><i class="fas fa-user text-sky-600 mr-2"></i>{{ _('User - Filter by team members') }}</li>
|
|
<li><i class="fas fa-building text-amber-600 mr-2"></i>{{ _('Client - Filter by client organization') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Visual Analytics') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-chart-bar text-primary mr-2"></i>{{ _('Interactive bar charts') }}</li>
|
|
<li><i class="fas fa-chart-pie text-green-600 mr-2"></i>{{ _('Time distribution pie charts') }}</li>
|
|
<li><i class="fas fa-chart-line text-sky-600 mr-2"></i>{{ _('Trend analysis over time') }}</li>
|
|
<li><i class="fas fa-mobile-alt text-amber-600 mr-2"></i>{{ _('Mobile-optimized charts') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{% if current_user.is_admin %}
|
|
<!-- Admin Features -->
|
|
<section id="admin-features" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-cog text-amber-600 mr-2"></i>{{ _('Administrator Features') }}</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('User Management') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Create new users with username-only authentication') }}</li>
|
|
<li>{{ _('Assign user roles (user or admin)') }}</li>
|
|
<li>{{ _('Activate/deactivate user accounts') }}</li>
|
|
<li>{{ _('View user statistics and activity') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Access Control') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Role-based permissions') }}</li>
|
|
<li>{{ _('Admin-only system settings') }}</li>
|
|
<li>{{ _('User data access controls') }}</li>
|
|
<li>{{ _('Audit trail for admin actions') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mt-6">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('General Settings') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-globe text-primary mr-2"></i>{{ _('Timezone and locale settings') }}</li>
|
|
<li><i class="fas fa-dollar-sign text-green-600 mr-2"></i>{{ _('Currency configuration') }}</li>
|
|
<li><i class="fas fa-clock text-sky-600 mr-2"></i>{{ _('Time rounding rules') }}</li>
|
|
<li><i class="fas fa-user-plus text-amber-600 mr-2"></i>{{ _('Self-registration settings') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Timer Settings') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-pause text-primary mr-2"></i>{{ _('Idle timeout configuration') }}</li>
|
|
<li><i class="fas fa-play text-green-600 mr-2"></i>{{ _('Single active timer mode') }}</li>
|
|
<li><i class="fas fa-stopwatch text-sky-600 mr-2"></i>{{ _('Timer display preferences') }}</li>
|
|
<li><i class="fas fa-bell text-amber-600 mr-2"></i>{{ _('Notification settings') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mt-6">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Database Management') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Create manual database backups') }}</li>
|
|
<li>{{ _('View database migration status') }}</li>
|
|
<li>{{ _('Monitor database performance') }}</li>
|
|
<li>{{ _('Clean up old data and logs') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('System Monitoring') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('View system information and health') }}</li>
|
|
<li>{{ _('Monitor application performance') }}</li>
|
|
<li>{{ _('Review system logs and errors') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Mobile Usage -->
|
|
<section id="mobile-usage" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-mobile-alt text-primary mr-2"></i>{{ _('Mobile Usage') }}</h3>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Mobile-Friendly Features') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-mobile-alt text-primary mr-2"></i>{{ _('Optimized for phones and tablets') }}</li>
|
|
<li><i class="fas fa-hand-pointer text-green-600 mr-2"></i>{{ _('Touch-friendly interface') }}</li>
|
|
<li><i class="fas fa-expand-arrows-alt text-sky-600 mr-2"></i>{{ _('Adaptive layouts for all screen sizes') }}</li>
|
|
<li><i class="fas fa-eye text-amber-600 mr-2"></i>{{ _('High contrast for outdoor visibility') }}</li>
|
|
</ul>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Mobile Navigation') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li><i class="fas fa-bars text-primary mr-2"></i>{{ _('Bottom tab bar for easy access') }}</li>
|
|
<li><i class="fas fa-home text-green-600 mr-2"></i>{{ _('Quick access to dashboard') }}</li>
|
|
<li><i class="fas fa-plus text-sky-600 mr-2"></i>{{ _('One-tap time logging') }}</li>
|
|
<li><i class="fas fa-chart-bar text-amber-600 mr-2"></i>{{ _('Mobile-optimized reports') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm mt-6">
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('Installation') }}</h4>
|
|
<ol class="list-decimal ml-5 space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Open TimeTracker in your mobile browser') }}</li>
|
|
<li>{{ _('Look for "Add to Home Screen" option') }}</li>
|
|
<li>{{ _('Follow browser-specific installation prompts') }}</li>
|
|
<li>{{ _('Launch from your home screen like a native app') }}</li>
|
|
</ol>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold mb-2">{{ _('PWA Benefits') }}</h4>
|
|
<ul class="space-y-1 text-text-muted-light dark:text-text-muted-dark">
|
|
<li>{{ _('Offline capability for basic functions') }}</li>
|
|
<li>{{ _('Faster loading times') }}</li>
|
|
<li>{{ _('Native app-like experience') }}</li>
|
|
<li>{{ _('Push notifications (where supported)') }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Troubleshooting -->
|
|
<section id="troubleshooting" class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow">
|
|
<h3 class="text-lg font-semibold mb-4"><i class="fas fa-tools text-red-500 mr-2"></i>{{ _('Troubleshooting & FAQ') }}</h3>
|
|
<div class="space-y-3 text-sm">
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-stopwatch mr-2"></i>{{ _('What happens if I forget to stop my timer?') }}</summary>
|
|
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('The timer will continue running until you stop it manually. You can see your active timer on the dashboard and timer page. The timer persists even if you close your browser or restart your device. If idle detection is enabled, the timer may pause automatically after the configured timeout period.') }}</p>
|
|
</details>
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-edit mr-2"></i>{{ _("Can I edit time entries after they're created?") }}</summary>
|
|
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('Yes, you can edit any time entry by clicking the edit button in the time entry list. You can modify the project, start/end times, notes, tags, billable status, and task assignment. Admins can edit all time entries, while regular users can only edit their own entries.') }}</p>
|
|
</details>
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-project-diagram mr-2"></i>{{ _('How do I track time for multiple projects?') }}</summary>
|
|
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('By default, you can only have one active timer at a time. To switch projects, stop your current timer and start a new one for the different project. Alternatively, you can create manual time entries for past work or configure the system to allow multiple active timers (admin setting).') }}</p>
|
|
</details>
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-download mr-2"></i>{{ _('How do I export my time data?') }}</summary>
|
|
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('Go to the Reports page and use the "Export CSV" feature. You can apply filters to export specific data, or export all time entries. The CSV file includes all time entry details and can be opened in Excel or other spreadsheet applications. You can also configure the delimiter for different regional standards.') }}</p>
|
|
</details>
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-dollar-sign mr-2"></i>{{ _("What's the difference between billable and non-billable time?") }}</summary>
|
|
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('Billable time is tracked for client billing purposes and can have an hourly rate associated with it. Non-billable time is for internal work that doesn\'t get charged to clients. You can mark individual time entries as billable or non-billable, and projects can have default billable settings. This distinction is crucial for accurate invoicing and profitability analysis.') }}</p>
|
|
</details>
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-file-invoice-dollar mr-2"></i>{{ _('How do I create an invoice from my time entries?') }}</summary>
|
|
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('Navigate to Invoices → Create Invoice, set up the client and project details, then use "Generate from Time Entries" to automatically create invoice items from your tracked time. You can filter by date range and project, and the system will group time entries intelligently. Review the generated items and export as a professional PDF.') }}</p>
|
|
</details>
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-mobile-alt mr-2"></i>{{ _('Can I use TimeTracker on my mobile device?') }}</summary>
|
|
<p class="mt-2 text-text-muted-light dark:text-text-muted-dark">{{ _('Yes! TimeTracker is fully responsive and works great on mobile devices. You can install it as a Progressive Web App (PWA) for a native-like experience. The mobile interface includes a bottom tab bar for easy navigation and touch-optimized controls for time tracking on the go.') }}</p>
|
|
</details>
|
|
<details class="group border border-border-light dark:border-border-dark rounded p-4">
|
|
<summary class="font-semibold cursor-pointer flex items-center"><i class="fas fa-question-circle mr-2"></i>{{ _('Where can I get additional help?') }}</summary>
|
|
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-4 text-text-muted-light dark:text-text-muted-dark">
|
|
<div>
|
|
<h4 class="font-semibold">{{ _('Documentation') }}</h4>
|
|
<p>{{ _('This help page covers most common questions and features.') }}</p>
|
|
</div>
|
|
<div>
|
|
<h4 class="font-semibold">{{ _('Community Support') }}</h4>
|
|
<p>{{ _('Report issues and request features on') }} <a class="text-primary" href="https://github.com/drytrix/TimeTracker/issues" target="_blank">GitHub Issues</a>.</p>
|
|
</div>
|
|
</div>
|
|
{% if current_user.is_admin %}
|
|
<div class="mt-3 p-3 rounded border border-sky-600/30 bg-sky-50 dark:bg-sky-900/20">
|
|
<i class="fas fa-info-circle mr-2"></i>{{ _('As an admin, you can access additional system information and diagnostics in the') }} <a class="text-primary" href="{{ url_for('admin.system_info') }}">{{ _('System Info') }}</a> {{ _('section.') }}
|
|
</div>
|
|
{% endif %}
|
|
</details>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Footer Help -->
|
|
<div class="bg-card-light dark:bg-card-dark border border-border-light dark:border-border-dark p-6 rounded-lg shadow text-center">
|
|
<h3 class="text-lg font-semibold">{{ _('Still Need Help?') }}</h3>
|
|
<p class="text-sm text-text-muted-light dark:text-text-muted-dark mb-4">{{ _('Can\'t find what you\'re looking for? Here are additional resources:') }}</p>
|
|
<div class="flex flex-wrap gap-3 justify-center">
|
|
{% if current_user.is_admin %}
|
|
<a href="{{ url_for('admin.system_info') }}" class="px-4 py-2 rounded-lg border border-primary text-primary hover:bg-primary/10">
|
|
<i class="fas fa-info-circle mr-1"></i>{{ _('System Info') }}
|
|
</a>
|
|
{% endif %}
|
|
<a href="https://github.com/drytrix/TimeTracker" target="_blank" rel="noopener" class="px-4 py-2 rounded-lg border border-border-light dark:border-border-dark hover:bg-background-light dark:hover:bg-background-dark">
|
|
<i class="fab fa-github mr-1"></i>{{ _('GitHub Repository') }}
|
|
</a>
|
|
<a href="https://github.com/drytrix/TimeTracker/issues" target="_blank" rel="noopener" class="px-4 py-2 rounded-lg border border-amber-600 text-amber-600 hover:bg-amber-50 dark:hover:bg-amber-900/20">
|
|
<i class="fas fa-bug mr-1"></i>{{ _('Report Issue') }}
|
|
</a>
|
|
<a href="https://buymeacoffee.com/DryTrix" target="_blank" rel="noopener" class="px-4 py-2 rounded-lg border border-green-600 text-green-600 hover:bg-green-50 dark:hover:bg-green-900/20">
|
|
<i class="fas fa-mug-saucer mr-1"></i>{{ _('Support Development') }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
{% endblock %}
|
|
{% block scripts_extra %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function(){
|
|
const links = Array.from(document.querySelectorAll('#help-nav .help-link'));
|
|
const filter = document.getElementById('help-nav-filter');
|
|
// Smooth scroll
|
|
links.forEach(a => {
|
|
a.addEventListener('click', (e) => {
|
|
const targetId = a.getAttribute('href');
|
|
if (targetId && targetId.startsWith('#')) {
|
|
const el = document.querySelector(targetId);
|
|
if (el) {
|
|
e.preventDefault();
|
|
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
history.replaceState(null, '', targetId);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// Active highlighting via IntersectionObserver
|
|
const sectionIds = links.map(a => a.getAttribute('href')).filter(Boolean);
|
|
const sections = sectionIds.map(id => document.querySelector(id)).filter(Boolean);
|
|
const byId = new Map(links.map(a => [a.getAttribute('href'), a]));
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
const id = '#' + entry.target.id;
|
|
const link = byId.get(id);
|
|
if (!link) return;
|
|
if (entry.isIntersecting) {
|
|
links.forEach(l => l.classList.remove('text-primary','font-semibold'));
|
|
link.classList.add('text-primary','font-semibold');
|
|
}
|
|
});
|
|
}, { rootMargin: '-40% 0px -50% 0px', threshold: [0, 1] });
|
|
sections.forEach(s => observer.observe(s));
|
|
|
|
// Filter
|
|
if (filter) {
|
|
filter.addEventListener('input', () => {
|
|
const q = filter.value.toLowerCase().trim();
|
|
links.forEach(a => {
|
|
const txt = a.textContent.toLowerCase();
|
|
a.style.display = txt.includes(q) ? '' : 'none';
|
|
});
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |