mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-07 13:00:22 -05:00
f5c3c3f59f
Fixed multiple issues with keyboard shortcuts and browser notifications: Keyboard Shortcuts: - Fixed Ctrl+/ not working to focus search input - Resolved conflict between three event handlers (base.html, commands.js, keyboard-shortcuts-advanced.js) - Changed inline handler from Ctrl+K to Ctrl+/ to avoid command palette conflict - Updated search bar UI badge to display Ctrl+/ instead of Ctrl+K - Removed conflicting ? key handler from commands.js (now uses Shift+? for shortcuts panel) - Improved key detection to properly handle special characters like / and ? - Added debug logging for troubleshooting keyboard events Final keyboard mapping: - Ctrl+K: Open Command Palette - Ctrl+/: Focus Search Input - Shift+?: Show All Keyboard Shortcuts - Esc: Close Modals/Panels Notification System: - Fixed "right-hand side of 'in' should be an object" error in smart-notifications.js - Changed notification permission request to follow browser security policies - Permission now checked silently on load, only requested on user interaction - Added "Enable Notifications" banner in notification center panel - Fixed service worker sync check to properly verify registration object Browser Compatibility: - All fixes respect browser security policies for notification permissions - Graceful degradation when service worker features unavailable - Works correctly on Chrome, Firefox, Safari, and Edge Files modified: - app/static/enhanced-search.js - app/static/keyboard-shortcuts-advanced.js - app/static/smart-notifications.js - app/templates/base.html - app/static/commands.js Closes issues with keyboard shortcuts not responding and browser console errors.
173 lines
10 KiB
HTML
173 lines
10 KiB
HTML
{% extends "base.html" %}
|
|
{% from "components/ui.html" import page_header, stat_card, badge %}
|
|
|
|
{% block content %}
|
|
{% set breadcrumbs = [
|
|
{'text': 'Tasks'}
|
|
] %}
|
|
|
|
{{ page_header(
|
|
icon_class='fas fa-tasks',
|
|
title_text='Tasks',
|
|
subtitle_text='Manage your tasks here',
|
|
breadcrumbs=breadcrumbs,
|
|
actions_html='<a href="' + url_for("tasks.create_task") + '" class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary/90 transition-colors"><i class="fas fa-plus mr-2"></i>Create Task</a>'
|
|
) }}
|
|
|
|
<!-- Task Summary Cards -->
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6 stagger-animation">
|
|
{% set todo_count = tasks|selectattr('status', 'equalto', 'todo')|list|length %}
|
|
{% set in_progress_count = tasks|selectattr('status', 'equalto', 'in_progress')|list|length %}
|
|
{% set review_count = tasks|selectattr('status', 'equalto', 'review')|list|length %}
|
|
{% set done_count = tasks|selectattr('status', 'equalto', 'done')|list|length %}
|
|
|
|
{{ stat_card('To Do', todo_count, 'fas fa-list', 'slate-500') }}
|
|
{{ stat_card('In Progress', in_progress_count, 'fas fa-spinner', 'blue-500') }}
|
|
{{ stat_card('In Review', review_count, 'fas fa-eye', 'amber-500') }}
|
|
{{ stat_card('Completed', done_count, 'fas fa-check-circle', 'green-500') }}
|
|
</div>
|
|
|
|
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow mb-6">
|
|
<h2 class="text-lg font-semibold mb-4">Filter Tasks</h2>
|
|
<form method="GET" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" data-filter-form>
|
|
<div class="lg:col-span-1">
|
|
<label for="search" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Search</label>
|
|
<input type="text" name="search" id="search" value="{{ search or '' }}" class="form-input">
|
|
</div>
|
|
<div>
|
|
<label for="status" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Status</label>
|
|
<select name="status" id="status" class="form-input">
|
|
<option value="">All</option>
|
|
<option value="todo" {% if status == 'todo' %}selected{% endif %}>To Do</option>
|
|
<option value="in_progress" {% if status == 'in_progress' %}selected{% endif %}>In Progress</option>
|
|
<option value="review" {% if status == 'review' %}selected{% endif %}>Review</option>
|
|
<option value="done" {% if status == 'done' %}selected{% endif %}>Done</option>
|
|
<option value="cancelled" {% if status == 'cancelled' %}selected{% endif %}>Cancelled</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="priority" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Priority</label>
|
|
<select name="priority" id="priority" class="form-input">
|
|
<option value="">All</option>
|
|
<option value="low" {% if priority == 'low' %}selected{% endif %}>Low</option>
|
|
<option value="medium" {% if priority == 'medium' %}selected{% endif %}>Medium</option>
|
|
<option value="high" {% if priority == 'high' %}selected{% endif %}>High</option>
|
|
<option value="urgent" {% if priority == 'urgent' %}selected{% endif %}>Urgent</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="project_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Project</label>
|
|
<select name="project_id" id="project_id" class="form-input">
|
|
<option value="">All</option>
|
|
{% for project in projects %}
|
|
<option value="{{ project.id }}" {% if project_id == project.id %}selected{% endif %}>{{ project.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="assigned_to" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Assigned To</label>
|
|
<select name="assigned_to" id="assigned_to" class="form-input">
|
|
<option value="">All</option>
|
|
{% for user in users %}
|
|
<option value="{{ user.id }}" {% if assigned_to == user.id %}selected{% endif %}>{{ user.display_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="flex items-center pt-5">
|
|
<input type="checkbox" name="overdue" id="overdue" value="1" {% if overdue %}checked{% endif %} class="h-4 w-4 rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
|
<label for="overdue" class="ml-2 block text-sm text-gray-900 dark:text-gray-300">Overdue only</label>
|
|
</div>
|
|
<div class="col-span-full flex justify-end">
|
|
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg">Filter</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow overflow-x-auto">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="text-sm font-medium text-text-muted-light dark:text-text-muted-dark">
|
|
{{ tasks|length }} task{{ 's' if tasks|length != 1 else '' }} found
|
|
</h3>
|
|
<div class="flex gap-2">
|
|
<button type="button" class="px-3 py-1.5 text-sm bg-background-light dark:bg-background-dark rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" title="Export to CSV">
|
|
<i class="fas fa-download mr-1"></i> Export
|
|
</button>
|
|
<button type="button" class="px-3 py-1.5 text-sm bg-background-light dark:bg-background-dark rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" title="Bulk actions">
|
|
<i class="fas fa-check-square mr-1"></i> Bulk
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<table class="table table-zebra w-full text-left" data-enhanced>
|
|
<thead class="border-b border-border-light dark:border-border-dark">
|
|
<tr>
|
|
<th class="p-4" data-sortable>Name</th>
|
|
<th class="p-4" data-sortable>Project</th>
|
|
<th class="p-4" data-sortable>Priority</th>
|
|
<th class="p-4" data-sortable>Status</th>
|
|
<th class="p-4 table-number" data-sortable>Due</th>
|
|
<th class="p-4 table-number" data-sortable>Progress</th>
|
|
<th class="p-4">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for task in tasks %}
|
|
<tr class="border-b border-border-light dark:border-border-dark">
|
|
<td class="p-4">{{ task.name }}</td>
|
|
<td class="p-4">{{ task.project.name }}</td>
|
|
<td class="p-4">
|
|
{% set p = task.priority %}
|
|
{% set pcls = {'low':'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300',
|
|
'medium':'bg-sky-100 text-sky-700 dark:bg-sky-900/30 dark:text-sky-300',
|
|
'high':'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',
|
|
'urgent':'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'}[p] if p in ['low','medium','high','urgent'] else 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300' %}
|
|
<span class="px-2 py-1 rounded-full text-xs font-medium {{ pcls }}">{{ task.priority_display }}</span>
|
|
</td>
|
|
<td class="p-4">
|
|
{% set s = task.status %}
|
|
{% set scls = {'todo':'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300',
|
|
'in_progress':'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300',
|
|
'review':'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',
|
|
'done':'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300',
|
|
'cancelled':'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200'}[s] if s in ['todo','in_progress','review','done','cancelled'] else 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300' %}
|
|
<span class="px-2 py-1 rounded-full text-xs font-medium {{ scls }}">{{ task.status_display }}</span>
|
|
</td>
|
|
<td class="p-4 table-number">
|
|
{% if task.due_date %}
|
|
{% set overdue = task.is_overdue %}
|
|
<span class="chip {{ 'chip-danger' if overdue else 'chip-neutral' }}">{{ task.due_date.strftime('%Y-%m-%d') }}</span>
|
|
{% else %}
|
|
<span class="text-text-muted-light dark:text-text-muted-dark">—</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="p-4 table-number">
|
|
{% set pct = task.progress_percentage or 0 %}
|
|
<div class="w-28 h-2 bg-gray-200 dark:bg-gray-700 rounded">
|
|
<div class="h-2 rounded {{ 'bg-emerald-500' if pct>=100 else 'bg-primary' }}" style="width: {{ [pct,100]|min }}%"></div>
|
|
</div>
|
|
</td>
|
|
<td class="p-4">
|
|
<a href="{{ url_for('tasks.view_task', task_id=task.id) }}" class="text-primary hover:underline">View</a>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
{% if not tasks %}
|
|
{% from "components/ui.html" import empty_state %}
|
|
{% set actions %}
|
|
<a href="{{ url_for('tasks.create_task') }}" class="bg-primary text-white px-4 py-2 rounded-lg hover:bg-primary/90 transition-colors">
|
|
<i class="fas fa-plus mr-2"></i>Create Your First Task
|
|
</a>
|
|
<a href="{{ url_for('main.help') }}" class="bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-4 py-2 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors">
|
|
<i class="fas fa-question-circle mr-2"></i>Learn More
|
|
</a>
|
|
{% endset %}
|
|
{{ empty_state('fas fa-tasks', 'No Tasks Found', 'Get started by creating your first task to organize your work and track progress.', actions) }}
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block content_after %}{% endblock %} |