376 lines
12 KiB
Dart
376 lines
12 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flux/core/enums_and_consts/consts.dart';
|
|
import 'package:flux/features/tasks/models/task_reminder_config.dart';
|
|
import 'package:flux/features/tasks/models/task_status.dart';
|
|
import 'package:get_it/get_it.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
// Sostituisci con i percorsi corretti di FLUX
|
|
import 'package:flux/features/tasks/models/task_model.dart';
|
|
|
|
class TasksRepository {
|
|
final _supabase = GetIt.I.get<SupabaseClient>();
|
|
|
|
// =========================================================================
|
|
// 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) {
|
|
debugPrint('Errore fetch personal reminders: $e');
|
|
throw Exception('Errore fetch personal reminders: $e');
|
|
}
|
|
}
|
|
|
|
// --- RECUPERO DEI TASK FILTRATI ---
|
|
Future<List<TaskModel>> getTasks({
|
|
required String companyId,
|
|
String? storeId,
|
|
String? staffId,
|
|
List<TaskStatus>? statuses,
|
|
int? limit,
|
|
}) async {
|
|
try {
|
|
// 1. FASE FILTRI: Usa il join esplicito stile "Notes"
|
|
var filterBuilder = _supabase
|
|
.from(Tables.tasks)
|
|
.select('''
|
|
*,
|
|
task_assignments:${Tables.taskAssignments} (
|
|
${Tables.staffMembers} (*)
|
|
)
|
|
''')
|
|
.eq('company_id', companyId);
|
|
|
|
if (storeId != null) {
|
|
filterBuilder = filterBuilder.or(
|
|
'store_id.eq.$storeId,store_id.is.null',
|
|
);
|
|
}
|
|
|
|
if (staffId != null) {
|
|
// Grazie al trigger, hai l'array pronto per il filtro senza impazzire!
|
|
filterBuilder = filterBuilder.contains('assigned_to_ids', [staffId]);
|
|
}
|
|
|
|
if (statuses != null && statuses.isNotEmpty) {
|
|
final statusValues = statuses.map((s) => s.toValue).toList();
|
|
filterBuilder = filterBuilder.inFilter('status', statusValues);
|
|
}
|
|
|
|
// 2. FASE TRASFORMAZIONI
|
|
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;
|
|
|
|
// 4. PARSING DEI DATI
|
|
return (response as List).map((json) => TaskModel.fromMap(json)).toList();
|
|
} catch (e) {
|
|
throw Exception('Errore nel recupero dei task: $e');
|
|
}
|
|
}
|
|
|
|
Future<TaskModel?> fetchTaskById(String taskId) async {
|
|
try {
|
|
final response = await _supabase
|
|
.from(Tables.tasks)
|
|
.select('''
|
|
*,
|
|
task_assignments:${Tables.taskAssignments} (
|
|
${Tables.staffMembers} (*)
|
|
)
|
|
''')
|
|
.eq('id', taskId)
|
|
.single();
|
|
|
|
return TaskModel.fromMap(response);
|
|
} catch (e) {
|
|
debugPrint('Errore fetch task by id: $e');
|
|
throw Exception('Errore fetch task by id: $e');
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// 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)
|
|
// =========================================================================
|
|
Future<void> createTask({
|
|
required TaskModel task,
|
|
required List<String> assignedStaffIds,
|
|
required String currentUserId,
|
|
required List<TaskReminderConfig> currentUserCustomReminders,
|
|
TaskReminderConfig? managerForcedOverride,
|
|
}) async {
|
|
try {
|
|
// 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();
|
|
|
|
final String taskId = taskResponse['id'];
|
|
|
|
// 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
|
|
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);
|
|
}
|
|
|
|
// 4. Creiamo la lista Bulk Insert per la tabella task_reminders
|
|
List<Map<String, dynamic>> remindersToInsert = [];
|
|
|
|
for (var staffId in assignedStaffIds) {
|
|
// A) Se è l'utente loggato -> usa i reminder configurati nel form
|
|
if (staffId == currentUserId) {
|
|
for (var config in currentUserCustomReminders) {
|
|
final triggerAt = task.dueDate!.subtract(
|
|
Duration(minutes: config.minutesBefore),
|
|
);
|
|
if (triggerAt.isAfter(DateTime.now())) {
|
|
remindersToInsert.add(
|
|
_buildReminderRow(
|
|
task,
|
|
taskId,
|
|
staffId,
|
|
config,
|
|
triggerAt,
|
|
false,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
// B) Se è un collega -> eredita i suoi default preimpostati
|
|
else {
|
|
final staffRules = otherDefaults.where(
|
|
(row) => row['staff_id'] == staffId,
|
|
);
|
|
for (var rule in staffRules) {
|
|
final config = TaskReminderConfig(
|
|
minutesBefore: rule['minutes_before'],
|
|
channel: rule['channel'],
|
|
);
|
|
final triggerAt = task.dueDate!.subtract(
|
|
Duration(minutes: config.minutesBefore),
|
|
);
|
|
if (triggerAt.isAfter(DateTime.now())) {
|
|
remindersToInsert.add(
|
|
_buildReminderRow(
|
|
task,
|
|
taskId,
|
|
staffId,
|
|
config,
|
|
triggerAt,
|
|
false,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// C) Override forzato del manager (per tutti)
|
|
if (managerForcedOverride != null) {
|
|
final triggerAt = task.dueDate!.subtract(
|
|
Duration(minutes: managerForcedOverride.minutesBefore),
|
|
);
|
|
if (triggerAt.isAfter(DateTime.now())) {
|
|
remindersToInsert.add(
|
|
_buildReminderRow(
|
|
task,
|
|
taskId,
|
|
staffId,
|
|
managerForcedOverride,
|
|
triggerAt,
|
|
true,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Inserimento massivo finale
|
|
if (remindersToInsert.isNotEmpty) {
|
|
await _supabase.from('task_reminders').insert(remindersToInsert);
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Errore durante la creazione del task: $e');
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// AGGIORNAMENTO (Update)
|
|
// =========================================================================
|
|
Future<void> updateTask({
|
|
required TaskModel task,
|
|
required List<String> assignedStaffIds,
|
|
required String currentUserId,
|
|
required List<TaskReminderConfig> currentUserCustomReminders,
|
|
}) async {
|
|
try {
|
|
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) => {
|
|
'task_id': taskId,
|
|
'staff_id': staffId,
|
|
'company_id': task.companyId,
|
|
},
|
|
)
|
|
.toList();
|
|
await _supabase.from('task_assignments').insert(assignmentsToInsert);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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!
|
|
|
|
// 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 = [];
|
|
|
|
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,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
if (toInsert.isNotEmpty) {
|
|
await _supabase.from('task_reminders').insert(toInsert);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
throw Exception('Errore durante l\'aggiornamento del task: $e');
|
|
}
|
|
}
|
|
|
|
// --- HELPER PRIVATO PER LA MAPPA DEL REMINDER ---
|
|
Map<String, dynamic> _buildReminderRow(
|
|
TaskModel task,
|
|
String taskId,
|
|
String staffId,
|
|
TaskReminderConfig config,
|
|
DateTime triggerAt,
|
|
bool isForced,
|
|
) {
|
|
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,
|
|
};
|
|
}
|
|
}
|