feat(tasks): add activity log, Markdown editor, and dark-mode polish

Backend
- Add TaskActivity model to record start/pause/review/complete/cancel/reopen
- Preserve started_at when reopening tasks; keep timestamps in app-local time
- Expose recent activities on task detail view

Migrations
- Add 004_add_task_activities_table (FKs + indexes)

Task detail (UI)
- Move Description into its own card and render as Markdown (markdown + bleach)
- Restyle Quick Actions to use dashboard btn-action styles
- Add custom confirmation modal + tooltips
- Recent Time Entries: use dashboard action buttons, add progressive “Show more/less” (10 per click)

Tasks list
- Fix dark mode for filter UI (inputs, selects, input-group-text, checkboxes)

Create/Edit task
- Integrate EasyMDE Markdown editor with toolbar
- Strong dark-theme overrides (toolbar, editor, preview, status bar, tokens)
- Prevent unintended side-by-side persistence
- Align “Current Task Info” dark-mode styles with task detail

CSS
- Add dark-mode tints for action buttons, tooltip light theme in dark mode
- Editor layout polish (padding, focus ring, gutters, selection)
- Quick actions layout: compact horizontal group

Deps
- Add: markdown, bleach

Run
- flask db upgrade  # applies 004_add_task_activities_table
This commit is contained in:
Dries Peeters
2025-09-08 08:06:48 +02:00
parent 6a0f3efe77
commit c297f1503b
11 changed files with 594 additions and 71 deletions

View File

@@ -1,5 +1,11 @@
from flask import Blueprint
from app.utils.timezone import utc_to_local, format_local_datetime
try:
import markdown as _md
import bleach
except Exception:
_md = None
bleach = None
def register_template_filters(app):
"""Register custom template filters for the application"""
@@ -38,3 +44,27 @@ def register_template_filters(app):
if text is None:
return ""
return text.replace('\n', '<br>')
@app.template_filter('markdown')
def markdown_filter(text):
"""Render markdown to safe HTML using bleach sanitation."""
if not text:
return ""
if _md is None:
# Fallback: escape and basic nl2br
try:
from markupsafe import escape
except Exception:
return text
return escape(text).replace('\n', '<br>')
html = _md.markdown(text, extensions=['extra', 'sane_lists', 'smarty'])
if bleach is None:
return html
allowed_tags = bleach.sanitizer.ALLOWED_TAGS.union({'p','pre','code','img','h1','h2','h3','h4','h5','h6','table','thead','tbody','tr','th','td','hr','br','ul','ol','li','strong','em','blockquote','a'})
allowed_attrs = {
**bleach.sanitizer.ALLOWED_ATTRIBUTES,
'a': ['href', 'title', 'rel', 'target'],
'img': ['src', 'alt', 'title'],
}
return bleach.clean(html, tags=allowed_tags, attributes=allowed_attrs, strip=True)