Add PM extension (Projects, Work Logs, Comments) + Quill rich-text UI

Projects: Brick-backed model with list/detail screens and nav entry.
Work Logs: per-contributor time ledger, assignee running-clock +
helper logged minutes with Hours/Minutes input; pause-after-save and
helper-deduct prompts.
Comments: threaded comments with file attachments and Quill composer.
Detail screen: tabbed layout (Work Log / Comments / Activity),
editable title + description via edit dialog, Quill-rendered
descriptions with legacy plain-text fallback.
Create dialog: fixed-width (480px max), Quill description editor.
Shared QuillFieldEditor + QuillReadOnly widgets extracted for reuse.
RLS: hardened activity-log actor binding, immutable creator_id/
created_at triggers, SECURITY INVOKER visibility helper,
append-only work-log and scoped comment policies.
Brick migration for project_id FK on programmer_tasks.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-09-27 16:06:26 +08:00
parent 627645056b
commit b307f66f2c
33 changed files with 2960 additions and 216 deletions
+18 -1
View File
@@ -32,6 +32,8 @@ import '../screens/it_service_requests/it_service_requests_list_screen.dart';
import '../screens/it_service_requests/it_service_request_detail_screen.dart';
import '../screens/programmer_tasks/programmer_tasks_list_screen.dart';
import '../screens/programmer_tasks/programmer_task_detail_screen.dart';
import '../screens/programmer_projects/programmer_projects_list_screen.dart';
import '../screens/programmer_projects/programmer_project_detail_screen.dart';
import '../screens/network_map/network_map_overview_screen.dart';
import '../screens/network_map/network_map_site_screen.dart';
import '../screens/network_map/network_map_device_screen.dart';
@@ -96,7 +98,7 @@ final appRouterProvider = Provider<GoRouter>((ref) {
role == 'dispatcher' ||
role == 'programmer';
final isProgrammerTasksRoute =
state.matchedLocation.startsWith('/programmer-tasks');
state.matchedLocation.startsWith('/programmer-');
final hasProgrammerTasksAccess =
role == 'admin' || role == 'programmer';
@@ -231,6 +233,21 @@ final appRouterProvider = Provider<GoRouter>((ref) {
),
],
),
GoRoute(
path: '/programmer-projects',
pageBuilder: _page(const ProgrammerProjectsListScreen()),
routes: [
GoRoute(
path: ':id',
pageBuilder: (context, state) => M3ContainerTransformPage(
key: state.pageKey,
child: ProgrammerProjectDetailScreen(
projectId: state.pathParameters['id'] ?? '',
),
),
),
],
),
GoRoute(
path: '/announcements',
pageBuilder: _page(const AnnouncementsScreen()),