171 lines
5.6 KiB
Dart
171 lines
5.6 KiB
Dart
import 'package:flux/core/enums_and_consts/consts.dart';
|
|
import 'package:flux/features/tasks/models/task_status.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 TaskRepository {
|
|
final SupabaseClient _supabase;
|
|
|
|
TaskRepository({SupabaseClient? supabase})
|
|
: _supabase = supabase ?? Supabase.instance.client;
|
|
|
|
// --- 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('''
|
|
*,
|
|
${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');
|
|
}
|
|
}
|
|
|
|
// --- 2. CREAZIONE DEL TASK ---
|
|
Future<TaskModel> createTask(TaskModel task) async {
|
|
try {
|
|
final taskData = task.toMap();
|
|
|
|
// Rimuoviamo l'array prima di inviare i dati alla tabella principale,
|
|
// la "Strada C" impone che la verità assoluta derivi dalla tabella di giunzione!
|
|
taskData.remove('assigned_to_ids');
|
|
|
|
// 1. Inseriamo il record base nella tabella tasks
|
|
final response = await _supabase
|
|
.from(Tables.tasks)
|
|
.insert(taskData)
|
|
.select()
|
|
.single();
|
|
|
|
final newTask = TaskModel.fromMap(response);
|
|
|
|
// 2. Se l'utente ha assegnato il task a qualcuno, popoliamo la giunzione
|
|
if (task.assignedToIds.isNotEmpty && newTask.id != null) {
|
|
await _syncAssignments(newTask.id!, task.assignedToIds);
|
|
|
|
// 3. Ricarichiamo il task appena creato per ottenere l'array e il JOIN aggiornati dal trigger!
|
|
return await getTaskById(newTask.id!);
|
|
}
|
|
|
|
return newTask;
|
|
} catch (e) {
|
|
throw Exception('Errore nella creazione del task: $e');
|
|
}
|
|
}
|
|
|
|
// --- 3. AGGIORNAMENTO DEL TASK ---
|
|
Future<TaskModel> updateTask(TaskModel task) async {
|
|
if (task.id == null) {
|
|
throw Exception('ID Task mancante. Impossibile aggiornare.');
|
|
}
|
|
|
|
try {
|
|
final taskData = task.toMap();
|
|
taskData.remove(
|
|
'assigned_to_ids',
|
|
); // Sempre via l'array dal body principale
|
|
|
|
// 1. Aggiorniamo i dati base del task (titolo, stato, scadenza, ecc.)
|
|
await _supabase.from(Tables.tasks).update(taskData).eq('id', task.id!);
|
|
|
|
// 2. Sincronizziamo in modo distruttivo gli assegnatari nella tabella di giunzione
|
|
await _syncAssignments(task.id!, task.assignedToIds);
|
|
|
|
// 3. Ritorniamo il modello fresco di DB
|
|
return await getTaskById(task.id!);
|
|
} catch (e) {
|
|
throw Exception('Errore nell\'aggiornamento del task: $e');
|
|
}
|
|
}
|
|
|
|
// --- 4. ELIMINAZIONE DEL TASK ---
|
|
Future<void> deleteTask(String taskId) async {
|
|
try {
|
|
// Eliminando il task, se la Foreign Key su Supabase ha "ON DELETE CASCADE",
|
|
// le righe nella tabella di giunzione si distruggeranno da sole in automatico!
|
|
await _supabase.from(Tables.tasks).delete().eq('id', taskId);
|
|
} catch (e) {
|
|
throw Exception('Errore nell\'eliminazione del task: $e');
|
|
}
|
|
}
|
|
|
|
// --- HELPER: RECUPERO SINGOLO TASK ---
|
|
Future<TaskModel> getTaskById(String taskId) async {
|
|
try {
|
|
final response = await _supabase
|
|
.from(Tables.tasks)
|
|
.select(
|
|
'*, assigned_to_staff:${Tables.staffMembers}!${Tables.taskAssignments}(*)',
|
|
)
|
|
.eq('id', taskId)
|
|
.single();
|
|
return TaskModel.fromMap(response);
|
|
} catch (e) {
|
|
throw Exception('Errore nel recupero del singolo task: $e');
|
|
}
|
|
}
|
|
|
|
// --- HELPER: SINCRONIZZAZIONE DELLA TABELLA DI GIUNZIONE ---
|
|
Future<void> _syncAssignments(String taskId, List<String> staffIds) async {
|
|
// TECNICA "WIPE & REPLACE":
|
|
// Invece di capire chi è stato aggiunto e chi tolto, eliminiamo tutti i record
|
|
// di questo task e li reinseriamo. È fulmineo ed esclude a priori bug di disallineamento.
|
|
|
|
// 1. Facciamo piazza pulita
|
|
await _supabase.from(Tables.taskAssignments).delete().eq('task_id', taskId);
|
|
|
|
// 2. Inseriamo le nuove assegnazioni (se ce ne sono)
|
|
if (staffIds.isNotEmpty) {
|
|
final List<Map<String, dynamic>> assignments = staffIds
|
|
.map((staffId) => {'task_id': taskId, 'staff_id': staffId})
|
|
.toList();
|
|
|
|
await _supabase.from(Tables.taskAssignments).insert(assignments);
|
|
}
|
|
}
|
|
}
|