Files
flux/lib/features/tasks/data/task_repository.dart

355 lines
11 KiB
Dart
Raw Normal View History

2026-05-28 13:55:28 +02:00
import 'dart:async';
2026-05-29 12:26:41 +02:00
import 'package:flutter/foundation.dart';
2026-05-26 19:31:25 +02:00
import 'package:flux/core/enums_and_consts/consts.dart';
2026-05-29 12:26:41 +02:00
import 'package:flux/features/tasks/models/task_reminder_config.dart';
2026-05-26 12:28:12 +02:00
import 'package:flux/features/tasks/models/task_status.dart';
2026-05-29 12:26:41 +02:00
import 'package:get_it/get_it.dart';
2026-05-26 12:28:12 +02:00
import 'package:supabase_flutter/supabase_flutter.dart';
// Sostituisci con i percorsi corretti di FLUX
import 'package:flux/features/tasks/models/task_model.dart';
2026-05-29 19:24:40 +02:00
class TasksRepository {
final _supabase = GetIt.I.get<SupabaseClient>();
2026-05-26 12:28:12 +02:00
2026-05-29 19:24:40 +02:00
// =========================================================================
// LETTURA REMINDER (Per il form in edit)
// =========================================================================
Future<List<TaskReminderConfig>> fetchPersonalReminders({
required String taskId,
required String staffId,
}) async {
try {
final response = await _supabase
.from('task_reminders')
.select()
.eq('task_id', taskId)
.eq('staff_id', staffId)
.eq(
'is_forced',
false,
); // Peschiamo SOLO quelli modificabili dall'utente
return (response as List)
.map(
(r) => TaskReminderConfig(
minutesBefore: r['minutes_before'],
channel: r['channel'],
),
)
.toList();
} catch (e) {
throw Exception('Errore fetch personal reminders: $e');
}
2026-05-28 13:55:28 +02:00
}
2026-05-26 12:28:12 +02:00
// --- RECUPERO DEI TASK FILTRATI ---
Future<List<TaskModel>> getTasks({
required String companyId,
String? storeId,
String? staffId,
List<TaskStatus>? statuses,
int? limit,
}) async {
try {
2026-05-26 19:31:25 +02:00
// 1. FASE FILTRI: Usa il join esplicito stile "Notes"
2026-05-26 12:28:12 +02:00
var filterBuilder = _supabase
.from(Tables.tasks)
2026-05-26 19:31:25 +02:00
.select('''
*,
2026-05-27 16:00:50 +02:00
task_assignments:${Tables.taskAssignments} (
${Tables.staffMembers} (*)
2026-05-26 19:31:25 +02:00
)
''')
2026-05-26 12:28:12 +02:00
.eq('company_id', companyId);
if (storeId != null) {
filterBuilder = filterBuilder.or(
'store_id.eq.$storeId,store_id.is.null',
);
}
if (staffId != null) {
2026-05-26 19:31:25 +02:00
// Grazie al trigger, hai l'array pronto per il filtro senza impazzire!
filterBuilder = filterBuilder.contains('assigned_to_ids', [staffId]);
2026-05-26 12:28:12 +02:00
}
if (statuses != null && statuses.isNotEmpty) {
final statusValues = statuses.map((s) => s.toValue).toList();
filterBuilder = filterBuilder.inFilter('status', statusValues);
}
2026-05-26 19:31:25 +02:00
// 2. FASE TRASFORMAZIONI
2026-05-26 12:28:12 +02:00
var transformBuilder = filterBuilder
.order('due_date', ascending: true, nullsFirst: false)
.order('created_at', ascending: false, nullsFirst: false);
if (limit != null) {
transformBuilder = transformBuilder.limit(limit);
}
// 3. ESECUZIONE DELLA QUERY
final response = await transformBuilder;
2026-05-26 19:31:25 +02:00
// 4. PARSING DEI DATI
2026-05-26 12:28:12 +02:00
return (response as List).map((json) => TaskModel.fromMap(json)).toList();
} catch (e) {
throw Exception('Errore nel recupero dei task: $e');
}
}
2026-05-29 19:24:40 +02:00
// =========================================================================
// REALTIME STREAM (La sentinella per la bacheca)
// =========================================================================
Stream<List<TaskModel>> watchCompanyTasks(String companyId) {
return _supabase
.from('tasks')
.stream(primaryKey: ['id'])
.eq('company_id', companyId)
.map((listOfMaps) {
return listOfMaps.map((map) => TaskModel.fromMap(map)).toList();
});
}
// =========================================================================
// CREAZIONE (Insert)
// =========================================================================
2026-05-29 12:26:41 +02:00
Future<void> createTask({
required TaskModel task,
required List<String> assignedStaffIds,
required String currentUserId,
required List<TaskReminderConfig> currentUserCustomReminders,
2026-05-29 19:24:40 +02:00
TaskReminderConfig? managerForcedOverride,
2026-05-29 12:26:41 +02:00
}) async {
2026-05-26 12:28:12 +02:00
try {
2026-05-29 19:24:40 +02:00
// 1. Inseriamo il Task principale per farci generare l'ID dal DB
final taskResponse = await _supabase
.from('tasks')
.insert(task.toMap()) // Assicurati che toMap() escluda l'id se è null
.select('id')
.single();
2026-05-26 12:28:12 +02:00
2026-05-29 19:24:40 +02:00
final String taskId = taskResponse['id'];
2026-05-26 12:28:12 +02:00
2026-05-29 19:24:40 +02:00
// 2. Inseriamo le Assegnazioni (tabella task_assignments)
if (assignedStaffIds.isNotEmpty) {
final assignmentsToInsert = assignedStaffIds
.map(
(staffId) => {
'task_id': taskId,
'staff_id': staffId,
'company_id': task.companyId,
},
)
.toList();
await _supabase.from('task_assignments').insert(assignmentsToInsert);
}
// Se non c'è data di scadenza, niente promemoria a tempo
if (task.dueDate == null || assignedStaffIds.isEmpty) return;
// 3. Setup Reminder: Peschiamo i default degli ALTRI dipendenti coinvolti
2026-05-29 12:26:41 +02:00
final otherStaffIds = assignedStaffIds
.where((id) => id != currentUserId)
.toList();
List<dynamic> otherDefaults = [];
if (otherStaffIds.isNotEmpty) {
otherDefaults = await _supabase
.from('staff_task_reminder_defaults')
.select()
.inFilter('staff_id', otherStaffIds);
}
2026-05-26 12:28:12 +02:00
2026-05-29 19:24:40 +02:00
// 4. Creiamo la lista Bulk Insert per la tabella task_reminders
List<Map<String, dynamic>> remindersToInsert = [];
2026-05-29 12:26:41 +02:00
for (var staffId in assignedStaffIds) {
2026-05-29 19:24:40 +02:00
// A) Se è l'utente loggato -> usa i reminder configurati nel form
2026-05-29 12:26:41 +02:00
if (staffId == currentUserId) {
for (var config in currentUserCustomReminders) {
2026-05-29 19:24:40 +02:00
final triggerAt = task.dueDate!.subtract(
2026-05-29 12:26:41 +02:00
Duration(minutes: config.minutesBefore),
);
2026-05-29 19:24:40 +02:00
if (triggerAt.isAfter(DateTime.now())) {
remindersToInsert.add(
_buildReminderRow(
task,
taskId,
staffId,
config,
triggerAt,
false,
),
);
2026-05-29 12:26:41 +02:00
}
}
}
2026-05-29 19:24:40 +02:00
// B) Se è un collega -> eredita i suoi default preimpostati
2026-05-29 12:26:41 +02:00
else {
final staffRules = otherDefaults.where(
(row) => row['staff_id'] == staffId,
);
for (var rule in staffRules) {
2026-05-29 19:24:40 +02:00
final config = TaskReminderConfig(
minutesBefore: rule['minutes_before'],
channel: rule['channel'],
2026-05-29 12:26:41 +02:00
);
2026-05-29 19:24:40 +02:00
final triggerAt = task.dueDate!.subtract(
Duration(minutes: config.minutesBefore),
);
if (triggerAt.isAfter(DateTime.now())) {
remindersToInsert.add(
_buildReminderRow(
task,
taskId,
staffId,
config,
triggerAt,
false,
),
);
2026-05-29 12:26:41 +02:00
}
}
}
2026-05-29 19:24:40 +02:00
// C) Override forzato del manager (per tutti)
if (managerForcedOverride != null) {
2026-05-29 12:26:41 +02:00
final triggerAt = task.dueDate!.subtract(
Duration(minutes: managerForcedOverride.minutesBefore),
);
if (triggerAt.isAfter(DateTime.now())) {
2026-05-29 19:24:40 +02:00
remindersToInsert.add(
_buildReminderRow(
task,
taskId,
staffId,
managerForcedOverride,
triggerAt,
true,
),
);
2026-05-29 12:26:41 +02:00
}
}
2026-05-26 12:28:12 +02:00
}
2026-05-29 19:24:40 +02:00
// 5. Inserimento massivo finale
2026-05-29 12:26:41 +02:00
if (remindersToInsert.isNotEmpty) {
await _supabase.from('task_reminders').insert(remindersToInsert);
}
2026-05-26 12:28:12 +02:00
} catch (e) {
2026-05-29 19:24:40 +02:00
throw Exception('Errore durante la creazione del task: $e');
2026-05-26 12:28:12 +02:00
}
}
2026-05-29 19:24:40 +02:00
// =========================================================================
// AGGIORNAMENTO (Update)
// =========================================================================
Future<void> updateTask({
2026-05-29 12:26:41 +02:00
required TaskModel task,
required List<String> assignedStaffIds,
required String currentUserId,
required List<TaskReminderConfig> currentUserCustomReminders,
}) async {
try {
2026-05-29 19:24:40 +02:00
final taskId = task.id!;
// 1. Aggiornamento dati Task Base
await _supabase
.from('tasks')
.update({
'title': task.title,
'description': task.description,
'due_date': task.dueDate?.toIso8601String(),
'store_id': task.storeId,
'updated_at': DateTime.now().toIso8601String(),
})
.eq('id', taskId);
// 2. Aggiornamento Assegnazioni: eliminiamo le vecchie, inseriamo le nuove
await _supabase.from('task_assignments').delete().eq('task_id', taskId);
if (assignedStaffIds.isNotEmpty) {
final assignmentsToInsert = assignedStaffIds
.map(
(staffId) => {
2026-05-29 12:26:41 +02:00
'task_id': taskId,
'staff_id': staffId,
2026-05-29 19:24:40 +02:00
'company_id': task.companyId,
},
)
.toList();
await _supabase.from('task_assignments').insert(assignmentsToInsert);
2026-05-29 12:26:41 +02:00
}
2026-05-29 19:24:40 +02:00
// Se non c'è una data, eliminiamo tutti i vecchi promemoria dell'utente loggato per pulizia
if (task.dueDate == null) {
await _supabase
.from('task_reminders')
.delete()
.eq('task_id', taskId)
.eq('staff_id', currentUserId)
.eq('is_forced', false);
return;
2026-05-29 12:26:41 +02:00
}
2026-05-29 19:24:40 +02:00
// 3. GESTIONE REMINDER: Puliamo SOLO quelli modificabili dall'utente loggato
await _supabase
.from('task_reminders')
.delete()
.eq('task_id', taskId)
.eq('staff_id', currentUserId)
.eq('is_forced', false); // NON tocchiamo quelli forzati dal manager!
2026-05-29 12:26:41 +02:00
2026-05-29 19:24:40 +02:00
// 4. Inseriamo le nuove configurazioni salvate dal Cubit (solo se è ancora tra gli assegnatari)
if (assignedStaffIds.contains(currentUserId) &&
currentUserCustomReminders.isNotEmpty) {
final List<Map<String, dynamic>> toInsert = [];
2026-05-29 12:26:41 +02:00
2026-05-29 19:24:40 +02:00
for (var config in currentUserCustomReminders) {
final triggerAt = task.dueDate!.subtract(
Duration(minutes: config.minutesBefore),
);
if (triggerAt.isAfter(DateTime.now())) {
toInsert.add(
_buildReminderRow(
task,
taskId,
currentUserId,
config,
triggerAt,
false,
),
2026-05-29 12:26:41 +02:00
);
}
}
2026-05-29 19:24:40 +02:00
if (toInsert.isNotEmpty) {
await _supabase.from('task_reminders').insert(toInsert);
}
2026-05-29 12:26:41 +02:00
}
} catch (e) {
2026-05-29 19:24:40 +02:00
throw Exception('Errore durante l\'aggiornamento del task: $e');
2026-05-29 12:26:41 +02:00
}
}
2026-05-29 19:24:40 +02:00
// --- HELPER PRIVATO PER LA MAPPA DEL REMINDER ---
Map<String, dynamic> _buildReminderRow(
TaskModel task,
2026-05-29 12:26:41 +02:00
String taskId,
2026-05-29 19:24:40 +02:00
String staffId,
TaskReminderConfig config,
DateTime triggerAt,
bool isForced,
2026-05-29 12:26:41 +02:00
) {
2026-05-29 19:24:40 +02:00
return {
'company_id': task.companyId,
'task_id': taskId,
'staff_id': staffId,
'minutes_before': config.minutesBefore,
'channel': config.channel,
'trigger_at': triggerAt.toIso8601String(),
'is_forced': isForced,
'is_sent': false,
};
2026-05-29 12:26:41 +02:00
}
2026-05-26 12:28:12 +02:00
}