b307f66f2c
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>
90 lines
2.9 KiB
Dart
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')));
|
|
}
|
|
}
|