Files
tasq/lib/screens/programmer_tasks/programmer_task_edit.dart
redz1029 b307f66f2c 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>
2026-09-27 16:06:26 +08:00

90 lines
2.9 KiB
Dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../models/programmer_task.model.dart';
import '../../providers/programmer_tasks_provider.dart';
import '../../widgets/quill_field.dart';
/// Edit a task's Title (plain) and Description (Quill). Saves via the existing
/// [ProgrammerTasksController.updateTask].
Future<void> showEditProgrammerTaskDialog(
BuildContext context,
WidgetRef ref,
ProgrammerTask task,
) async {
final titleCtrl = TextEditingController(text: task.title);
final descQuill = quillControllerFromDelta(task.description);
final ok = await showDialog<bool>(
context: context,
builder: (context) {
final dialogWidth =
math.min(480.0, MediaQuery.of(context).size.width - 48);
return AlertDialog(
semanticLabel: 'Edit task',
title: const Text('Edit Task'),
content: SizedBox(
width: dialogWidth,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: titleCtrl,
autofocus: true,
textCapitalization: TextCapitalization.sentences,
decoration: const InputDecoration(labelText: 'Title'),
),
const SizedBox(height: 12),
Align(
alignment: Alignment.centerLeft,
child: Text('Description',
style: Theme.of(context).textTheme.bodySmall),
),
const SizedBox(height: 4),
QuillFieldEditor(
controller: descQuill,
placeholder: 'Describe the task…',
height: 180,
),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Save'),
),
],
);
},
);
final title = titleCtrl.text.trim();
titleCtrl.dispose();
final description =
quillPlainText(descQuill).isEmpty ? '' : quillToDeltaJson(descQuill);
descQuill.dispose();
if (ok != true || title.isEmpty || !context.mounted) return;
final messenger = ScaffoldMessenger.of(context);
try {
await ref.read(programmerTasksControllerProvider).updateTask(
taskId: task.id,
title: title,
description: description,
);
messenger.showSnackBar(const SnackBar(content: Text('Task updated')));
} catch (e) {
messenger.showSnackBar(SnackBar(content: Text('Could not update: $e')));
}
}