mobile_team/lib/screens/log_form_screen.dart
2026-04-16 15:06:41 +05:30

263 lines
8.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../models/task_log_model.dart';
import '../models/project_model.dart';
import '../models/branch_model.dart';
import '../services/project_service.dart';
import '../services/task_log_service.dart';
import '../providers/auth_provider.dart';
class LogFormScreen extends StatefulWidget {
final TaskLogModel? log;
const LogFormScreen({super.key, this.log});
@override
State<LogFormScreen> createState() => _LogFormScreenState();
}
class _LogFormScreenState extends State<LogFormScreen> {
final _formKey = GlobalKey<FormState>();
late DateTime _selectedDate;
ProjectModel? _selectedProject;
BranchModel? _selectedBranch;
String? _selectedFlavor;
String _status = 'in_progress';
final _moduleController = TextEditingController();
final _remarksController = TextEditingController();
List<ProjectModel> _projects = [];
List<BranchModel> _branches = [];
bool _isLoading = false;
bool _isInitializing = true;
@override
void initState() {
super.initState();
_selectedDate = widget.log?.date ?? DateTime.now();
_status = widget.log?.status ?? 'in_progress';
_moduleController.text = widget.log?.moduleName ?? '';
_remarksController.text = widget.log?.remarks ?? '';
_selectedFlavor = widget.log?.flavor;
_loadProjects();
}
Future<void> _loadProjects() async {
final stream = context.read<ProjectService>().getProjects();
final list = await stream.first;
if (mounted) {
setState(() {
_projects = list;
if (widget.log != null) {
_selectedProject = _projects.any((p) => p.id == widget.log!.projectId)
? _projects.firstWhere((p) => p.id == widget.log!.projectId)
: null;
if (_selectedProject != null) {
_loadBranches(_selectedProject!.id);
}
}
_isInitializing = false;
});
}
}
Future<void> _loadBranches(String projectId) async {
final list = await context.read<ProjectService>().getBranchesOnce(projectId);
if (mounted) {
setState(() {
_branches = list;
if (widget.log != null && _selectedBranch == null) {
_selectedBranch = _branches.any((b) => b.id == widget.log!.branchId)
? _branches.firstWhere((b) => b.id == widget.log!.branchId)
: null;
} else {
_selectedBranch = null;
}
});
}
}
void _submit() async {
if (!_formKey.currentState!.validate() || _selectedProject == null || _selectedBranch == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Please select Project and Branch"), backgroundColor: Colors.orange)
);
return;
}
setState(() => _isLoading = true);
final user = context.read<AuthProvider>().user!;
final newLog = TaskLogModel(
id: widget.log?.id ?? '',
userId: user.uid,
userName: user.name,
projectId: _selectedProject!.id,
projectName: _selectedProject!.name,
branchId: _selectedBranch!.id,
branchName: _selectedBranch!.name,
flavor: _selectedFlavor,
moduleName: _moduleController.text.trim(),
status: _status,
remarks: _remarksController.text.trim(),
date: _selectedDate,
createdAt: widget.log?.createdAt ?? DateTime.now(),
);
try {
if (widget.log == null) {
await context.read<TaskLogService>().addTaskLog(newLog);
} else {
await context.read<TaskLogService>().updateTaskLog(newLog);
}
if (mounted) Navigator.pop(context);
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Failed: $e")));
}
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.log == null ? "Add Work Log" : "Edit Work Log"),
),
body: _isInitializing
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildDateField(),
const SizedBox(height: 16),
_buildProjectDropdown(),
if (_selectedProject != null && _selectedProject!.flavors.isNotEmpty) ...[
const SizedBox(height: 16),
_buildFlavorDropdown(),
],
const SizedBox(height: 16),
_buildBranchDropdown(),
const SizedBox(height: 16),
TextFormField(
controller: _moduleController,
decoration: const InputDecoration(
labelText: "Module/Task Name",
hintText: "e.g., Login UI, Bug fixing",
border: OutlineInputBorder(),
),
validator: (v) => v!.isEmpty ? "Required" : null,
),
const SizedBox(height: 24),
const Text("Status", style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
_buildStatusChips(),
const SizedBox(height: 24),
TextFormField(
controller: _remarksController,
maxLines: 4,
decoration: const InputDecoration(
labelText: "Remarks",
border: OutlineInputBorder(),
),
validator: (v) => v!.isEmpty ? "Required" : null,
),
const SizedBox(height: 32),
_isLoading
? const Center(child: CircularProgressIndicator())
: ElevatedButton(
onPressed: _submit,
child: Text(widget.log == null ? "SAVE LOG" : "UPDATE LOG"),
),
],
),
),
),
);
}
Widget _buildDateField() {
return ListTile(
contentPadding: EdgeInsets.zero,
title: const Text("Date of Work", style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text(DateFormat('EEEE, dd MMM yyyy').format(_selectedDate)),
trailing: const Icon(Icons.calendar_month, color: Colors.indigo),
onTap: () async {
final date = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime.now().subtract(const Duration(days: 30)),
lastDate: DateTime.now(),
);
if (date != null) setState(() => _selectedDate = date);
},
);
}
Widget _buildProjectDropdown() {
return DropdownButtonFormField<ProjectModel>(
value: _selectedProject,
decoration: const InputDecoration(labelText: "Project", border: OutlineInputBorder()),
items: _projects.map((p) => DropdownMenuItem(value: p, child: Text(p.name))).toList(),
onChanged: (v) {
setState(() {
_selectedProject = v;
_selectedBranch = null;
_selectedFlavor = null;
_branches = [];
});
if (v != null) _loadBranches(v.id);
},
);
}
Widget _buildFlavorDropdown() {
return DropdownButtonFormField<String>(
value: _selectedFlavor,
decoration: const InputDecoration(labelText: "Flavor", border: OutlineInputBorder()),
items: _selectedProject!.flavors.map((f) => DropdownMenuItem(value: f, child: Text(f))).toList(),
onChanged: (v) => setState(() => _selectedFlavor = v),
validator: (v) => v == null ? "Required" : null,
);
}
Widget _buildBranchDropdown() {
return DropdownButtonFormField<BranchModel>(
value: _selectedBranch,
decoration: const InputDecoration(labelText: "Branch", border: OutlineInputBorder()),
items: _branches.map((b) => DropdownMenuItem(value: b, child: Text(b.name))).toList(),
onChanged: (v) => setState(() => _selectedBranch = v),
hint: const Text("Select Branch"),
);
}
Widget _buildStatusChips() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_statusChip('in_progress', 'IN PROGRESS', Colors.orange),
_statusChip('done', 'DONE', Colors.green),
_statusChip('blocked', 'BLOCKED', Colors.red),
],
);
}
Widget _statusChip(String value, String label, Color color) {
bool selected = _status == value;
return ChoiceChip(
label: Text(label, style: TextStyle(color: selected ? Colors.white : color, fontSize: 11)),
selected: selected,
selectedColor: color,
onSelected: (s) {
if (s) setState(() => _status = value);
},
);
}
}