Added Task Number filter
This commit is contained in:
@@ -58,7 +58,7 @@ class TaskQuery {
|
||||
this.searchQuery = '',
|
||||
this.officeId,
|
||||
this.status,
|
||||
this.assigneeId,
|
||||
this.taskNumber,
|
||||
this.dateRange,
|
||||
});
|
||||
|
||||
@@ -77,10 +77,9 @@ class TaskQuery {
|
||||
/// Filter by status.
|
||||
final String? status;
|
||||
|
||||
/// Filter by assignee ID.
|
||||
final String? assigneeId;
|
||||
/// Filter by task number (partial match, case-insensitive).
|
||||
final String? taskNumber;
|
||||
|
||||
/// Filter by date range.
|
||||
/// Filter by date range.
|
||||
final DateTimeRange? dateRange;
|
||||
|
||||
@@ -90,7 +89,7 @@ class TaskQuery {
|
||||
String? searchQuery,
|
||||
String? officeId,
|
||||
String? status,
|
||||
String? assigneeId,
|
||||
String? taskNumber,
|
||||
DateTimeRange? dateRange,
|
||||
}) {
|
||||
return TaskQuery(
|
||||
@@ -99,7 +98,7 @@ class TaskQuery {
|
||||
searchQuery: searchQuery ?? this.searchQuery,
|
||||
officeId: officeId ?? this.officeId,
|
||||
status: status ?? this.status,
|
||||
assigneeId: assigneeId ?? this.assigneeId,
|
||||
taskNumber: taskNumber ?? this.taskNumber,
|
||||
dateRange: dateRange ?? this.dateRange,
|
||||
);
|
||||
}
|
||||
@@ -191,6 +190,12 @@ final tasksProvider = StreamProvider<List<Task>>((ref) {
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
if (query.taskNumber != null && query.taskNumber!.trim().isNotEmpty) {
|
||||
final tn = query.taskNumber!.toLowerCase();
|
||||
list = list
|
||||
.where((t) => (t.taskNumber ?? '').toLowerCase().contains(tn))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Sort by status groups then within-group ordering:
|
||||
// 1. queued – order by priority (desc), then queue_order (asc), then created_at
|
||||
@@ -354,16 +359,7 @@ class TasksController {
|
||||
String? assignedNumber;
|
||||
|
||||
try {
|
||||
final rpcParams = {
|
||||
'p_title': title,
|
||||
'p_description': description,
|
||||
'p_office_id': officeId,
|
||||
'p_ticket_id': ticketId,
|
||||
'p_request_type': requestType,
|
||||
'p_request_type_other': requestTypeOther,
|
||||
'p_request_category': requestCategory,
|
||||
'p_creator_id': actorId,
|
||||
};
|
||||
final rpcParams = Map<String, dynamic>.from(payload);
|
||||
// Retry RPC on duplicate-key (23505) errors which may occur
|
||||
// transiently due to concurrent inserts; prefer RPC always.
|
||||
const int rpcMaxAttempts = 3;
|
||||
@@ -598,52 +594,63 @@ class TasksController {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// fetch task_number and office_id for nicer message and deep-linking
|
||||
// fetch task_number and office (try embedding office.name); fallback to offices lookup
|
||||
String? taskNumber;
|
||||
String? officeId;
|
||||
String? officeName;
|
||||
try {
|
||||
final t = await _client
|
||||
.from('tasks')
|
||||
.select('task_number, office_id')
|
||||
.select('task_number, office_id, offices(name)')
|
||||
.eq('id', taskId)
|
||||
.maybeSingle();
|
||||
if (t != null) {
|
||||
if (t['task_number'] != null)
|
||||
if (t['task_number'] != null) {
|
||||
taskNumber = t['task_number'].toString();
|
||||
}
|
||||
if (t['office_id'] != null) officeId = t['office_id'].toString();
|
||||
final dynOffices = t['offices'];
|
||||
if (dynOffices != null) {
|
||||
if (dynOffices is List &&
|
||||
dynOffices.isNotEmpty &&
|
||||
dynOffices.first['name'] != null) {
|
||||
officeName = dynOffices.first['name'].toString();
|
||||
} else if (dynOffices is Map && dynOffices['name'] != null) {
|
||||
officeName = dynOffices['name'].toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// resolve office name when available
|
||||
String? officeName;
|
||||
if (officeId != null && officeId.isNotEmpty) {
|
||||
if ((officeName == null || officeName.isEmpty) &&
|
||||
officeId != null &&
|
||||
officeId.isNotEmpty) {
|
||||
try {
|
||||
final o = await _client
|
||||
.from('offices')
|
||||
.select('name')
|
||||
.eq('id', officeId)
|
||||
.maybeSingle();
|
||||
if (o != null && o['name'] != null)
|
||||
if (o != null && o['name'] != null) {
|
||||
officeName = o['name'].toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
final title = officeName != null
|
||||
? '$actorName created a new task in $officeName'
|
||||
: '$actorName created a new task';
|
||||
final title = 'New task';
|
||||
final body = taskNumber != null
|
||||
? (officeName != null
|
||||
? '$actorName created task #$taskNumber in $officeName'
|
||||
: '$actorName created task #$taskNumber')
|
||||
? '$actorName created task #$taskNumber in $officeName.'
|
||||
: '$actorName created task #$taskNumber.')
|
||||
: (officeName != null
|
||||
? '$actorName created a new task in $officeName'
|
||||
: '$actorName created a new task');
|
||||
? '$actorName created a new task in $officeName.'
|
||||
: '$actorName created a new task.');
|
||||
|
||||
final dataPayload = <String, dynamic>{
|
||||
'type': 'created',
|
||||
if (taskNumber != null) 'task_number': taskNumber,
|
||||
if (officeId != null) 'office_id': officeId,
|
||||
if (officeName != null) 'office_name': officeName,
|
||||
'task_number': ?taskNumber,
|
||||
'office_id': ?officeId,
|
||||
'office_name': ?officeName,
|
||||
};
|
||||
|
||||
await _client.functions.invoke(
|
||||
@@ -833,7 +840,6 @@ class TasksController {
|
||||
if (officeId != null) payload['office_id'] = officeId;
|
||||
if (ticketId != null) payload['ticket_id'] = ticketId;
|
||||
if (payload.isEmpty) return;
|
||||
|
||||
await _client.from('tasks').update(payload).eq('id', taskId);
|
||||
|
||||
// record an activity log for edit operations (best-effort)
|
||||
@@ -1000,44 +1006,65 @@ class TasksController {
|
||||
// send push for auto-assignment
|
||||
try {
|
||||
final actorName = 'Dispatcher';
|
||||
final title = '$actorName assigned you a task';
|
||||
final body = '$actorName assigned you a task';
|
||||
// fetch task_number and office for nicer deep-linking when available
|
||||
String? taskNumber;
|
||||
String? officeId;
|
||||
String? officeName;
|
||||
try {
|
||||
final t = await _client
|
||||
.from('tasks')
|
||||
.select('task_number, office_id')
|
||||
.select('task_number, office_id, offices(name)')
|
||||
.eq('id', taskId)
|
||||
.maybeSingle();
|
||||
if (t != null) {
|
||||
if (t['task_number'] != null)
|
||||
if (t['task_number'] != null) {
|
||||
taskNumber = t['task_number'].toString();
|
||||
}
|
||||
if (t['office_id'] != null) officeId = t['office_id'].toString();
|
||||
final dynOffices = t['offices'];
|
||||
if (dynOffices != null) {
|
||||
if (dynOffices is List &&
|
||||
dynOffices.isNotEmpty &&
|
||||
dynOffices.first['name'] != null) {
|
||||
officeName = dynOffices.first['name'].toString();
|
||||
} else if (dynOffices is Map && dynOffices['name'] != null) {
|
||||
officeName = dynOffices['name'].toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
String? officeName;
|
||||
if (officeId != null && officeId.isNotEmpty) {
|
||||
if ((officeName == null || officeName.isEmpty) &&
|
||||
officeId != null &&
|
||||
officeId.isNotEmpty) {
|
||||
try {
|
||||
final o = await _client
|
||||
.from('offices')
|
||||
.select('name')
|
||||
.eq('id', officeId)
|
||||
.maybeSingle();
|
||||
if (o != null && o['name'] != null)
|
||||
if (o != null && o['name'] != null) {
|
||||
officeName = o['name'].toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
final dataPayload = <String, dynamic>{
|
||||
'type': 'assignment',
|
||||
if (taskNumber != null) 'task_number': taskNumber,
|
||||
if (officeId != null) 'office_id': officeId,
|
||||
if (officeName != null) 'office_name': officeName,
|
||||
'task_number': ?taskNumber,
|
||||
'office_id': ?officeId,
|
||||
'office_name': ?officeName,
|
||||
};
|
||||
|
||||
final title = 'Task assigned';
|
||||
final body = taskNumber != null
|
||||
? (officeName != null
|
||||
? '$actorName assigned you task #$taskNumber in $officeName.'
|
||||
: '$actorName assigned you task #$taskNumber.')
|
||||
: (officeName != null
|
||||
? '$actorName assigned you a task in $officeName.'
|
||||
: '$actorName assigned you a task.');
|
||||
|
||||
await _client.functions.invoke(
|
||||
'send_fcm',
|
||||
body: {
|
||||
@@ -1201,9 +1228,9 @@ class TaskAssignmentsController {
|
||||
.eq('id', actorId)
|
||||
.maybeSingle();
|
||||
if (p != null) {
|
||||
if (p['full_name'] != null)
|
||||
if (p['full_name'] != null) {
|
||||
actorName = p['full_name'].toString();
|
||||
else if (p['display_name'] != null)
|
||||
} else if (p['display_name'] != null)
|
||||
actorName = p['display_name'].toString();
|
||||
else if (p['name'] != null)
|
||||
actorName = p['name'].toString();
|
||||
@@ -1211,58 +1238,72 @@ class TaskAssignmentsController {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
final title = '$actorName assigned you to a task';
|
||||
final body = '$actorName has assigned you to a task';
|
||||
|
||||
// fetch task_number and office for nicer deep-linking when available
|
||||
// fetch task_number and office (try embedding office.name); fallback to offices lookup
|
||||
String? taskNumber;
|
||||
String? officeId;
|
||||
String? officeName;
|
||||
try {
|
||||
final t = await _client
|
||||
.from('tasks')
|
||||
.select('task_number, office_id')
|
||||
.select('task_number, office_id, offices(name)')
|
||||
.eq('id', taskId)
|
||||
.maybeSingle();
|
||||
if (t != null) {
|
||||
if (t['task_number'] != null)
|
||||
if (t['task_number'] != null) {
|
||||
taskNumber = t['task_number'].toString();
|
||||
}
|
||||
if (t['office_id'] != null) officeId = t['office_id'].toString();
|
||||
final dynOffices = t['offices'];
|
||||
if (dynOffices != null) {
|
||||
if (dynOffices is List &&
|
||||
dynOffices.isNotEmpty &&
|
||||
dynOffices.first['name'] != null) {
|
||||
officeName = dynOffices.first['name'].toString();
|
||||
} else if (dynOffices is Map && dynOffices['name'] != null) {
|
||||
officeName = dynOffices['name'].toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
String? officeName;
|
||||
if (officeId != null && officeId.isNotEmpty) {
|
||||
if ((officeName == null || officeName.isEmpty) &&
|
||||
officeId != null &&
|
||||
officeId.isNotEmpty) {
|
||||
try {
|
||||
final o = await _client
|
||||
.from('offices')
|
||||
.select('name')
|
||||
.eq('id', officeId)
|
||||
.maybeSingle();
|
||||
if (o != null && o['name'] != null)
|
||||
if (o != null && o['name'] != null) {
|
||||
officeName = o['name'].toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
final dataPayload = <String, dynamic>{
|
||||
'type': 'assignment',
|
||||
if (taskNumber != null) 'task_number': taskNumber,
|
||||
if (ticketId != null) 'ticket_id': ticketId,
|
||||
if (officeId != null) 'office_id': officeId,
|
||||
if (officeName != null) 'office_name': officeName,
|
||||
'task_number': ?taskNumber,
|
||||
'ticket_id': ?ticketId,
|
||||
'office_id': ?officeId,
|
||||
'office_name': ?officeName,
|
||||
};
|
||||
|
||||
// include office name in title/body when possible
|
||||
final displayTitle = officeName != null
|
||||
? '$title in $officeName'
|
||||
: title;
|
||||
final displayBody = officeName != null ? '$body in $officeName' : body;
|
||||
final title = 'Task assigned';
|
||||
final body = taskNumber != null
|
||||
? (officeName != null
|
||||
? '$actorName assigned you task #$taskNumber in $officeName.'
|
||||
: '$actorName assigned you task #$taskNumber.')
|
||||
: (officeName != null
|
||||
? '$actorName assigned you a task in $officeName.'
|
||||
: '$actorName assigned you a task.');
|
||||
|
||||
await _client.functions.invoke(
|
||||
'send_fcm',
|
||||
body: {
|
||||
'user_ids': userIds,
|
||||
'title': displayTitle,
|
||||
'body': displayBody,
|
||||
'title': title,
|
||||
'body': body,
|
||||
'data': dataPayload,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -315,7 +315,7 @@ class TicketsController {
|
||||
'body': body,
|
||||
'data': {
|
||||
'ticket_id': ticketId,
|
||||
if (ticketNumber != null) 'ticket_number': ticketNumber,
|
||||
'ticket_number': ?ticketNumber,
|
||||
'type': 'created',
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user