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:
@@ -347,6 +347,7 @@ class ProgrammerTasksController {
|
||||
required String category,
|
||||
String? description,
|
||||
String? assigneeId,
|
||||
String? projectId,
|
||||
int priority = 1,
|
||||
}) async {
|
||||
final userId = _client.auth.currentUser?.id;
|
||||
@@ -363,6 +364,7 @@ class ProgrammerTasksController {
|
||||
'priority': priority,
|
||||
'assignee_id': assignee,
|
||||
'creator_id': userId,
|
||||
'project_id': ?projectId,
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -384,6 +386,7 @@ class ProgrammerTasksController {
|
||||
priority: priority,
|
||||
assigneeId: assignee,
|
||||
creatorId: userId,
|
||||
projectId: projectId,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
);
|
||||
@@ -481,6 +484,33 @@ class ProgrammerTasksController {
|
||||
await _insertLog(taskId, 'updated', meta: {'fields': updates.keys.toList()});
|
||||
}
|
||||
|
||||
/// Assigns the task to a project (pass null to clear it).
|
||||
Future<void> setProject({
|
||||
required String taskId,
|
||||
required String? projectId,
|
||||
}) async {
|
||||
await _updateTaskRow(taskId, {'project_id': projectId});
|
||||
await _insertLog(taskId, 'updated', meta: {
|
||||
'fields': ['project_id'],
|
||||
});
|
||||
}
|
||||
|
||||
/// Records a time adjustment (negative [seconds]) against a task's worked
|
||||
/// duration — e.g. when the current user helped on another task and chose to
|
||||
/// deduct that time from their own running task.
|
||||
Future<void> addAdjustment({
|
||||
required String taskId,
|
||||
required int seconds,
|
||||
String? reason,
|
||||
String? sourceTaskId,
|
||||
}) async {
|
||||
await _insertLog(taskId, 'adjustment', meta: {
|
||||
'seconds': seconds,
|
||||
'reason': ?reason,
|
||||
'source_task_id': ?sourceTaskId,
|
||||
});
|
||||
}
|
||||
|
||||
/// Applies a field patch to a task row. When [onlyIfStartNull] is set,
|
||||
/// `started_at` is dropped if the task already has one (preserves the first
|
||||
/// execution start across pause/resume cycles).
|
||||
@@ -578,6 +608,8 @@ ProgrammerTask _applyUpdateFields(ProgrammerTask t, Map<String, dynamic> f) {
|
||||
assigneeId:
|
||||
f.containsKey('assignee_id') ? f['assignee_id'] as String? : t.assigneeId,
|
||||
creatorId: t.creatorId,
|
||||
projectId:
|
||||
f.containsKey('project_id') ? f['project_id'] as String? : t.projectId,
|
||||
createdAt: t.createdAt,
|
||||
startedAt: f.containsKey('started_at') ? parseOpt('started_at') : t.startedAt,
|
||||
completedAt:
|
||||
|
||||
Reference in New Issue
Block a user