b
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
import 'package:flux/features/tasks/blocs/task_form_cubit.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class TaskFormScreen extends StatefulWidget {
|
||||
@@ -31,6 +33,62 @@ class _TaskFormScreenState extends State<TaskFormScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _showAddReminderDialog(BuildContext context, TaskFormCubit cubit) {
|
||||
int minutes = 15;
|
||||
String channel = 'push';
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertDialog(
|
||||
title: const Text('Aggiungi Promemoria'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DropdownButtonFormField<int>(
|
||||
initialValue: minutes,
|
||||
decoration: const InputDecoration(labelText: 'Preavviso'),
|
||||
items: const [
|
||||
DropdownMenuItem(value: 5, child: Text('5 minuti prima')),
|
||||
DropdownMenuItem(value: 15, child: Text('15 minuti prima')),
|
||||
DropdownMenuItem(value: 60, child: Text('1 ora prima')),
|
||||
DropdownMenuItem(value: 1440, child: Text('1 giorno prima')),
|
||||
],
|
||||
onChanged: (v) => {if (v != null) minutes = v},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
DropdownButtonFormField<String>(
|
||||
initialValue: channel,
|
||||
decoration: const InputDecoration(labelText: 'Canale'),
|
||||
items: const [
|
||||
DropdownMenuItem(value: 'push', child: Text('Notifica Push')),
|
||||
DropdownMenuItem(value: 'email', child: Text('Email')),
|
||||
],
|
||||
onChanged: (v) => {if (v != null) channel = v},
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
child: const Text('Annulla'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
cubit.addReminderRule(minutes, channel);
|
||||
Navigator.pop(dialogContext);
|
||||
},
|
||||
child: const Text(
|
||||
'Inserisci',
|
||||
style: TextStyle(color: Colors.orange),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<TaskFormCubit, TaskFormState>(
|
||||
@@ -79,7 +137,13 @@ class _TaskFormScreenState extends State<TaskFormScreen> {
|
||||
else
|
||||
TextButton.icon(
|
||||
onPressed: state.isFormValid
|
||||
? () => cubit.saveTask(currentUserId: 'TODO_USER_ID')
|
||||
? () => cubit.saveTask(
|
||||
currentUserId: GetIt.I
|
||||
.get<SessionCubit>()
|
||||
.state
|
||||
.currentStaffMember!
|
||||
.id!,
|
||||
)
|
||||
: null,
|
||||
icon: const Icon(Icons.save),
|
||||
label: const Text('Salva'),
|
||||
@@ -162,84 +226,181 @@ class _TaskFormScreenState extends State<TaskFormScreen> {
|
||||
TaskFormState state,
|
||||
TaskFormCubit cubit,
|
||||
) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor.withValues(alpha: 0.2),
|
||||
return FocusTraversalGroup(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Card(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor.withValues(alpha: 0.2),
|
||||
),
|
||||
),
|
||||
child: SwitchListTile(
|
||||
title: const Text(
|
||||
'Task Globale Aziendale',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: const Text(
|
||||
'Visibile a tutta l\'azienda, non legato a un negozio specifico.',
|
||||
),
|
||||
value: state.isGlobal,
|
||||
activeThumbColor: Colors.orange,
|
||||
onChanged: (val) => cubit.toggleGlobalScope(val),
|
||||
),
|
||||
),
|
||||
child: SwitchListTile(
|
||||
title: const Text(
|
||||
'Task Globale Aziendale',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
subtitle: const Text(
|
||||
'Visibile a tutta l\'azienda, non legato a un negozio specifico.',
|
||||
),
|
||||
value: state.isGlobal,
|
||||
activeThumbColor: Colors.orange,
|
||||
onChanged: (val) => cubit.toggleGlobalScope(val),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Addio initialValue, benvenuto controller!
|
||||
TextFormField(
|
||||
controller: _titleController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Titolo del Task*',
|
||||
border: OutlineInputBorder(),
|
||||
prefixIcon: Icon(Icons.title),
|
||||
// Addio initialValue, benvenuto controller!
|
||||
TextFormField(
|
||||
controller: _titleController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Titolo del Task*',
|
||||
border: OutlineInputBorder(),
|
||||
prefixIcon: Icon(Icons.title),
|
||||
),
|
||||
onChanged: cubit.updateTitle,
|
||||
),
|
||||
onChanged: cubit.updateTitle,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _descController,
|
||||
maxLines: 4,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Descrizione (opzionale)',
|
||||
border: OutlineInputBorder(),
|
||||
alignLabelWithHint: true,
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _descController,
|
||||
maxLines: 4,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Descrizione (opzionale)',
|
||||
border: OutlineInputBorder(),
|
||||
alignLabelWithHint: true,
|
||||
),
|
||||
onChanged: cubit.updateDescription,
|
||||
),
|
||||
onChanged: cubit.updateDescription,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// --- SCADENZA ---
|
||||
ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Theme.of(context).dividerColor),
|
||||
// --- SCADENZA ---
|
||||
ListTile(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Theme.of(context).dividerColor),
|
||||
),
|
||||
leading: const Icon(Icons.calendar_today, color: Colors.orange),
|
||||
title: Text(
|
||||
state.dueDate != null
|
||||
// Formattiamo aggiungendo gli zeri (es. 05/09/2026 alle 09:05)
|
||||
? 'Scadenza: ${state.dueDate!.day.toString().padLeft(2, '0')}/${state.dueDate!.month.toString().padLeft(2, '0')}/${state.dueDate!.year} alle ${state.dueDate!.hour.toString().padLeft(2, '0')}:${state.dueDate!.minute.toString().padLeft(2, '0')}'
|
||||
: 'Nessuna scadenza impostata',
|
||||
),
|
||||
trailing: state.dueDate != null
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => cubit.updateDueDate(null),
|
||||
)
|
||||
: null,
|
||||
onTap: () async {
|
||||
// 1. Chiediamo prima la Data
|
||||
final date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: state.dueDate ?? DateTime.now(),
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
// Se l'utente chiude il calendario senza scegliere, ci fermiamo
|
||||
if (date == null || !context.mounted) return;
|
||||
|
||||
// 2. Chiediamo subito dopo l'Orario
|
||||
final time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: state.dueDate != null
|
||||
? TimeOfDay.fromDateTime(state.dueDate!)
|
||||
: const TimeOfDay(hour: 9, minute: 0), // Default ore 09:00
|
||||
);
|
||||
|
||||
// Se l'utente chiude l'orologio senza scegliere, ci fermiamo
|
||||
if (time == null) return;
|
||||
|
||||
// 3. Fondiamo Data e Ora in un nuovo oggetto DateTime
|
||||
final finalDateTime = DateTime(
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
time.hour,
|
||||
time.minute,
|
||||
);
|
||||
|
||||
// Aggiorniamo lo stato tramite il Cubit
|
||||
cubit.updateDueDate(finalDateTime);
|
||||
},
|
||||
),
|
||||
leading: const Icon(Icons.calendar_today, color: Colors.orange),
|
||||
title: Text(
|
||||
state.dueDate != null
|
||||
? 'Scadenza: ${state.dueDate!.day}/${state.dueDate!.month}/${state.dueDate!.year}'
|
||||
: 'Nessuna scadenza impostata',
|
||||
),
|
||||
trailing: state.dueDate != null
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => cubit.updateDueDate(null),
|
||||
)
|
||||
: null,
|
||||
onTap: () async {
|
||||
final date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: state.dueDate ?? DateTime.now(),
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
if (date != null) cubit.updateDueDate(date);
|
||||
},
|
||||
),
|
||||
],
|
||||
if (state.dueDate != null) ...[
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'Promemoria del Task',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Card(
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor.withValues(alpha: 0.3),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
children: [
|
||||
// Elenco dei promemoria attuali del form
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: state.reminders.length,
|
||||
itemBuilder: (context, index) {
|
||||
final reminder = state.reminders[index];
|
||||
final isPush = reminder.channel == 'push';
|
||||
|
||||
return ListTile(
|
||||
dense: true,
|
||||
leading: Icon(
|
||||
isPush
|
||||
? Icons.notifications_active_outlined
|
||||
: Icons.mail_outline,
|
||||
color: isPush ? Colors.orange : Colors.blue,
|
||||
),
|
||||
title: Text(
|
||||
reminder.friendlyTime,
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
size: 18,
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
onPressed: () => cubit.removeReminderRule(index),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
// Tasto di aggiunta rapida promemoria
|
||||
TextButton.icon(
|
||||
onPressed: () => _showAddReminderDialog(context, cubit),
|
||||
icon: const Icon(Icons.add, size: 18),
|
||||
label: const Text('Aggiungi un promemoria a questo task'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.orange,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user