Added Task Number filter
This commit is contained in:
parent
0a8e388757
commit
5a74299a1c
|
|
@ -140,9 +140,9 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||||
message.data['actorId'] ??
|
message.data['actorId'] ??
|
||||||
message.data['actor'])
|
message.data['actor'])
|
||||||
?.toString();
|
?.toString();
|
||||||
if (taskNumber != null && taskNumber.isNotEmpty)
|
if (taskNumber != null && taskNumber.isNotEmpty) {
|
||||||
sb.write('tasknum:$taskNumber');
|
sb.write('tasknum:$taskNumber');
|
||||||
else if (taskId != null && taskId.isNotEmpty)
|
} else if (taskId != null && taskId.isNotEmpty)
|
||||||
sb.write('task:$taskId');
|
sb.write('task:$taskId');
|
||||||
if (ticketId != null && ticketId.isNotEmpty) {
|
if (ticketId != null && ticketId.isNotEmpty) {
|
||||||
if (sb.isNotEmpty) sb.write('|');
|
if (sb.isNotEmpty) sb.write('|');
|
||||||
|
|
@ -318,15 +318,16 @@ Future<void> main() async {
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
String? name;
|
String? name;
|
||||||
if (res['full_name'] != null)
|
if (res['full_name'] != null) {
|
||||||
name = res['full_name'].toString();
|
name = res['full_name'].toString();
|
||||||
else if (res['display_name'] != null)
|
} else if (res['display_name'] != null)
|
||||||
name = res['display_name'].toString();
|
name = res['display_name'].toString();
|
||||||
else if (res['name'] != null)
|
else if (res['name'] != null)
|
||||||
name = res['name'].toString();
|
name = res['name'].toString();
|
||||||
if (name != null && name.isNotEmpty)
|
if (name != null && name.isNotEmpty) {
|
||||||
dataForFormatting['actor_name'] = name;
|
dataForFormatting['actor_name'] = name;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// ignore lookup failures and fall back to data payload
|
// ignore lookup failures and fall back to data payload
|
||||||
}
|
}
|
||||||
|
|
@ -359,9 +360,9 @@ Future<void> main() async {
|
||||||
message.data['actorId'] ??
|
message.data['actorId'] ??
|
||||||
message.data['actor'])
|
message.data['actor'])
|
||||||
?.toString();
|
?.toString();
|
||||||
if (taskNumber != null && taskNumber.isNotEmpty)
|
if (taskNumber != null && taskNumber.isNotEmpty) {
|
||||||
sb.write('tasknum:$taskNumber');
|
sb.write('tasknum:$taskNumber');
|
||||||
else if (taskId != null && taskId.isNotEmpty)
|
} else if (taskId != null && taskId.isNotEmpty)
|
||||||
sb.write('task:$taskId');
|
sb.write('task:$taskId');
|
||||||
if (ticketId != null && ticketId.isNotEmpty) {
|
if (ticketId != null && ticketId.isNotEmpty) {
|
||||||
if (sb.isNotEmpty) sb.write('|');
|
if (sb.isNotEmpty) sb.write('|');
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class TaskQuery {
|
||||||
this.searchQuery = '',
|
this.searchQuery = '',
|
||||||
this.officeId,
|
this.officeId,
|
||||||
this.status,
|
this.status,
|
||||||
this.assigneeId,
|
this.taskNumber,
|
||||||
this.dateRange,
|
this.dateRange,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -77,10 +77,9 @@ class TaskQuery {
|
||||||
/// Filter by status.
|
/// Filter by status.
|
||||||
final String? status;
|
final String? status;
|
||||||
|
|
||||||
/// Filter by assignee ID.
|
/// Filter by task number (partial match, case-insensitive).
|
||||||
final String? assigneeId;
|
final String? taskNumber;
|
||||||
|
|
||||||
/// Filter by date range.
|
|
||||||
/// Filter by date range.
|
/// Filter by date range.
|
||||||
final DateTimeRange? dateRange;
|
final DateTimeRange? dateRange;
|
||||||
|
|
||||||
|
|
@ -90,7 +89,7 @@ class TaskQuery {
|
||||||
String? searchQuery,
|
String? searchQuery,
|
||||||
String? officeId,
|
String? officeId,
|
||||||
String? status,
|
String? status,
|
||||||
String? assigneeId,
|
String? taskNumber,
|
||||||
DateTimeRange? dateRange,
|
DateTimeRange? dateRange,
|
||||||
}) {
|
}) {
|
||||||
return TaskQuery(
|
return TaskQuery(
|
||||||
|
|
@ -99,7 +98,7 @@ class TaskQuery {
|
||||||
searchQuery: searchQuery ?? this.searchQuery,
|
searchQuery: searchQuery ?? this.searchQuery,
|
||||||
officeId: officeId ?? this.officeId,
|
officeId: officeId ?? this.officeId,
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
assigneeId: assigneeId ?? this.assigneeId,
|
taskNumber: taskNumber ?? this.taskNumber,
|
||||||
dateRange: dateRange ?? this.dateRange,
|
dateRange: dateRange ?? this.dateRange,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +190,12 @@ final tasksProvider = StreamProvider<List<Task>>((ref) {
|
||||||
)
|
)
|
||||||
.toList();
|
.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:
|
// Sort by status groups then within-group ordering:
|
||||||
// 1. queued – order by priority (desc), then queue_order (asc), then created_at
|
// 1. queued – order by priority (desc), then queue_order (asc), then created_at
|
||||||
|
|
@ -354,16 +359,7 @@ class TasksController {
|
||||||
String? assignedNumber;
|
String? assignedNumber;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final rpcParams = {
|
final rpcParams = Map<String, dynamic>.from(payload);
|
||||||
'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,
|
|
||||||
};
|
|
||||||
// Retry RPC on duplicate-key (23505) errors which may occur
|
// Retry RPC on duplicate-key (23505) errors which may occur
|
||||||
// transiently due to concurrent inserts; prefer RPC always.
|
// transiently due to concurrent inserts; prefer RPC always.
|
||||||
const int rpcMaxAttempts = 3;
|
const int rpcMaxAttempts = 3;
|
||||||
|
|
@ -598,52 +594,63 @@ class TasksController {
|
||||||
} catch (_) {}
|
} 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? taskNumber;
|
||||||
String? officeId;
|
String? officeId;
|
||||||
|
String? officeName;
|
||||||
try {
|
try {
|
||||||
final t = await _client
|
final t = await _client
|
||||||
.from('tasks')
|
.from('tasks')
|
||||||
.select('task_number, office_id')
|
.select('task_number, office_id, offices(name)')
|
||||||
.eq('id', taskId)
|
.eq('id', taskId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
if (t['task_number'] != null)
|
if (t['task_number'] != null) {
|
||||||
taskNumber = t['task_number'].toString();
|
taskNumber = t['task_number'].toString();
|
||||||
|
}
|
||||||
if (t['office_id'] != null) officeId = t['office_id'].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 (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
// resolve office name when available
|
if ((officeName == null || officeName.isEmpty) &&
|
||||||
String? officeName;
|
officeId != null &&
|
||||||
if (officeId != null && officeId.isNotEmpty) {
|
officeId.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
final o = await _client
|
final o = await _client
|
||||||
.from('offices')
|
.from('offices')
|
||||||
.select('name')
|
.select('name')
|
||||||
.eq('id', officeId)
|
.eq('id', officeId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (o != null && o['name'] != null)
|
if (o != null && o['name'] != null) {
|
||||||
officeName = o['name'].toString();
|
officeName = o['name'].toString();
|
||||||
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
final title = officeName != null
|
final title = 'New task';
|
||||||
? '$actorName created a new task in $officeName'
|
|
||||||
: '$actorName created a new task';
|
|
||||||
final body = taskNumber != null
|
final body = taskNumber != null
|
||||||
? (officeName != null
|
? (officeName != null
|
||||||
? '$actorName created task #$taskNumber in $officeName'
|
? '$actorName created task #$taskNumber in $officeName.'
|
||||||
: '$actorName created task #$taskNumber')
|
: '$actorName created task #$taskNumber.')
|
||||||
: (officeName != null
|
: (officeName != null
|
||||||
? '$actorName created a new task in $officeName'
|
? '$actorName created a new task in $officeName.'
|
||||||
: '$actorName created a new task');
|
: '$actorName created a new task.');
|
||||||
|
|
||||||
final dataPayload = <String, dynamic>{
|
final dataPayload = <String, dynamic>{
|
||||||
'type': 'created',
|
'type': 'created',
|
||||||
if (taskNumber != null) 'task_number': taskNumber,
|
'task_number': ?taskNumber,
|
||||||
if (officeId != null) 'office_id': officeId,
|
'office_id': ?officeId,
|
||||||
if (officeName != null) 'office_name': officeName,
|
'office_name': ?officeName,
|
||||||
};
|
};
|
||||||
|
|
||||||
await _client.functions.invoke(
|
await _client.functions.invoke(
|
||||||
|
|
@ -833,7 +840,6 @@ class TasksController {
|
||||||
if (officeId != null) payload['office_id'] = officeId;
|
if (officeId != null) payload['office_id'] = officeId;
|
||||||
if (ticketId != null) payload['ticket_id'] = ticketId;
|
if (ticketId != null) payload['ticket_id'] = ticketId;
|
||||||
if (payload.isEmpty) return;
|
if (payload.isEmpty) return;
|
||||||
|
|
||||||
await _client.from('tasks').update(payload).eq('id', taskId);
|
await _client.from('tasks').update(payload).eq('id', taskId);
|
||||||
|
|
||||||
// record an activity log for edit operations (best-effort)
|
// record an activity log for edit operations (best-effort)
|
||||||
|
|
@ -1000,44 +1006,65 @@ class TasksController {
|
||||||
// send push for auto-assignment
|
// send push for auto-assignment
|
||||||
try {
|
try {
|
||||||
final actorName = 'Dispatcher';
|
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
|
// fetch task_number and office for nicer deep-linking when available
|
||||||
String? taskNumber;
|
String? taskNumber;
|
||||||
String? officeId;
|
String? officeId;
|
||||||
|
String? officeName;
|
||||||
try {
|
try {
|
||||||
final t = await _client
|
final t = await _client
|
||||||
.from('tasks')
|
.from('tasks')
|
||||||
.select('task_number, office_id')
|
.select('task_number, office_id, offices(name)')
|
||||||
.eq('id', taskId)
|
.eq('id', taskId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
if (t['task_number'] != null)
|
if (t['task_number'] != null) {
|
||||||
taskNumber = t['task_number'].toString();
|
taskNumber = t['task_number'].toString();
|
||||||
|
}
|
||||||
if (t['office_id'] != null) officeId = t['office_id'].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 (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
String? officeName;
|
if ((officeName == null || officeName.isEmpty) &&
|
||||||
if (officeId != null && officeId.isNotEmpty) {
|
officeId != null &&
|
||||||
|
officeId.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
final o = await _client
|
final o = await _client
|
||||||
.from('offices')
|
.from('offices')
|
||||||
.select('name')
|
.select('name')
|
||||||
.eq('id', officeId)
|
.eq('id', officeId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (o != null && o['name'] != null)
|
if (o != null && o['name'] != null) {
|
||||||
officeName = o['name'].toString();
|
officeName = o['name'].toString();
|
||||||
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
final dataPayload = <String, dynamic>{
|
final dataPayload = <String, dynamic>{
|
||||||
'type': 'assignment',
|
'type': 'assignment',
|
||||||
if (taskNumber != null) 'task_number': taskNumber,
|
'task_number': ?taskNumber,
|
||||||
if (officeId != null) 'office_id': officeId,
|
'office_id': ?officeId,
|
||||||
if (officeName != null) 'office_name': officeName,
|
'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(
|
await _client.functions.invoke(
|
||||||
'send_fcm',
|
'send_fcm',
|
||||||
body: {
|
body: {
|
||||||
|
|
@ -1201,9 +1228,9 @@ class TaskAssignmentsController {
|
||||||
.eq('id', actorId)
|
.eq('id', actorId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
if (p['full_name'] != null)
|
if (p['full_name'] != null) {
|
||||||
actorName = p['full_name'].toString();
|
actorName = p['full_name'].toString();
|
||||||
else if (p['display_name'] != null)
|
} else if (p['display_name'] != null)
|
||||||
actorName = p['display_name'].toString();
|
actorName = p['display_name'].toString();
|
||||||
else if (p['name'] != null)
|
else if (p['name'] != null)
|
||||||
actorName = p['name'].toString();
|
actorName = p['name'].toString();
|
||||||
|
|
@ -1211,58 +1238,72 @@ class TaskAssignmentsController {
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
final title = '$actorName assigned you to a task';
|
// fetch task_number and office (try embedding office.name); fallback to offices lookup
|
||||||
final body = '$actorName has assigned you to a task';
|
|
||||||
|
|
||||||
// fetch task_number and office for nicer deep-linking when available
|
|
||||||
String? taskNumber;
|
String? taskNumber;
|
||||||
String? officeId;
|
String? officeId;
|
||||||
|
String? officeName;
|
||||||
try {
|
try {
|
||||||
final t = await _client
|
final t = await _client
|
||||||
.from('tasks')
|
.from('tasks')
|
||||||
.select('task_number, office_id')
|
.select('task_number, office_id, offices(name)')
|
||||||
.eq('id', taskId)
|
.eq('id', taskId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
if (t['task_number'] != null)
|
if (t['task_number'] != null) {
|
||||||
taskNumber = t['task_number'].toString();
|
taskNumber = t['task_number'].toString();
|
||||||
|
}
|
||||||
if (t['office_id'] != null) officeId = t['office_id'].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 (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
String? officeName;
|
if ((officeName == null || officeName.isEmpty) &&
|
||||||
if (officeId != null && officeId.isNotEmpty) {
|
officeId != null &&
|
||||||
|
officeId.isNotEmpty) {
|
||||||
try {
|
try {
|
||||||
final o = await _client
|
final o = await _client
|
||||||
.from('offices')
|
.from('offices')
|
||||||
.select('name')
|
.select('name')
|
||||||
.eq('id', officeId)
|
.eq('id', officeId)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
if (o != null && o['name'] != null)
|
if (o != null && o['name'] != null) {
|
||||||
officeName = o['name'].toString();
|
officeName = o['name'].toString();
|
||||||
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
final dataPayload = <String, dynamic>{
|
final dataPayload = <String, dynamic>{
|
||||||
'type': 'assignment',
|
'type': 'assignment',
|
||||||
if (taskNumber != null) 'task_number': taskNumber,
|
'task_number': ?taskNumber,
|
||||||
if (ticketId != null) 'ticket_id': ticketId,
|
'ticket_id': ?ticketId,
|
||||||
if (officeId != null) 'office_id': officeId,
|
'office_id': ?officeId,
|
||||||
if (officeName != null) 'office_name': officeName,
|
'office_name': ?officeName,
|
||||||
};
|
};
|
||||||
|
|
||||||
// include office name in title/body when possible
|
final title = 'Task assigned';
|
||||||
final displayTitle = officeName != null
|
final body = taskNumber != null
|
||||||
? '$title in $officeName'
|
? (officeName != null
|
||||||
: title;
|
? '$actorName assigned you task #$taskNumber in $officeName.'
|
||||||
final displayBody = officeName != null ? '$body in $officeName' : body;
|
: '$actorName assigned you task #$taskNumber.')
|
||||||
|
: (officeName != null
|
||||||
|
? '$actorName assigned you a task in $officeName.'
|
||||||
|
: '$actorName assigned you a task.');
|
||||||
|
|
||||||
await _client.functions.invoke(
|
await _client.functions.invoke(
|
||||||
'send_fcm',
|
'send_fcm',
|
||||||
body: {
|
body: {
|
||||||
'user_ids': userIds,
|
'user_ids': userIds,
|
||||||
'title': displayTitle,
|
'title': title,
|
||||||
'body': displayBody,
|
'body': body,
|
||||||
'data': dataPayload,
|
'data': dataPayload,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,7 @@ class TicketsController {
|
||||||
'body': body,
|
'body': body,
|
||||||
'data': {
|
'data': {
|
||||||
'ticket_id': ticketId,
|
'ticket_id': ticketId,
|
||||||
if (ticketNumber != null) 'ticket_number': ticketNumber,
|
'ticket_number': ?ticketNumber,
|
||||||
'type': 'created',
|
'type': 'created',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class TasksListScreen extends ConsumerStatefulWidget {
|
||||||
class _TasksListScreenState extends ConsumerState<TasksListScreen>
|
class _TasksListScreenState extends ConsumerState<TasksListScreen>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
final TextEditingController _subjectController = TextEditingController();
|
final TextEditingController _subjectController = TextEditingController();
|
||||||
|
final TextEditingController _taskNumberController = TextEditingController();
|
||||||
String? _selectedOfficeId;
|
String? _selectedOfficeId;
|
||||||
String? _selectedStatus;
|
String? _selectedStatus;
|
||||||
String? _selectedAssigneeId;
|
String? _selectedAssigneeId;
|
||||||
|
|
@ -55,6 +56,7 @@ class _TasksListScreenState extends ConsumerState<TasksListScreen>
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_subjectController.dispose();
|
_subjectController.dispose();
|
||||||
|
_taskNumberController.dispose();
|
||||||
_tabController.dispose();
|
_tabController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -63,13 +65,18 @@ class _TasksListScreenState extends ConsumerState<TasksListScreen>
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabController = TabController(length: 2, vsync: this);
|
_tabController = TabController(length: 2, vsync: this);
|
||||||
|
_tabController.addListener(() {
|
||||||
|
// rebuild when tab changes so filters shown/hidden update
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _hasTaskFilters {
|
bool get _hasTaskFilters {
|
||||||
return _subjectController.text.trim().isNotEmpty ||
|
return _subjectController.text.trim().isNotEmpty ||
|
||||||
|
_taskNumberController.text.trim().isNotEmpty ||
|
||||||
_selectedOfficeId != null ||
|
_selectedOfficeId != null ||
|
||||||
_selectedStatus != null ||
|
_selectedStatus != null ||
|
||||||
_selectedAssigneeId != null ||
|
(_tabController.index == 1 && _selectedAssigneeId != null) ||
|
||||||
_selectedDateRange != null;
|
_selectedDateRange != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +162,7 @@ class _TasksListScreenState extends ConsumerState<TasksListScreen>
|
||||||
tasks,
|
tasks,
|
||||||
ticketById: ticketById,
|
ticketById: ticketById,
|
||||||
subjectQuery: _subjectController.text,
|
subjectQuery: _subjectController.text,
|
||||||
|
taskNumber: _taskNumberController.text,
|
||||||
officeId: _selectedOfficeId,
|
officeId: _selectedOfficeId,
|
||||||
status: _selectedStatus,
|
status: _selectedStatus,
|
||||||
assigneeId: _selectedAssigneeId,
|
assigneeId: _selectedAssigneeId,
|
||||||
|
|
@ -190,6 +198,18 @@ class _TasksListScreenState extends ConsumerState<TasksListScreen>
|
||||||
decoration: const InputDecoration(labelText: 'Office'),
|
decoration: const InputDecoration(labelText: 'Office'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 160,
|
||||||
|
child: TextField(
|
||||||
|
controller: _taskNumberController,
|
||||||
|
onChanged: (_) => setState(() {}),
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Task #',
|
||||||
|
prefixIcon: Icon(Icons.filter_alt),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_tabController.index == 1)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 220,
|
width: 220,
|
||||||
child: DropdownButtonFormField<String?>(
|
child: DropdownButtonFormField<String?>(
|
||||||
|
|
@ -760,6 +780,7 @@ List<Task> _applyTaskFilters(
|
||||||
List<Task> tasks, {
|
List<Task> tasks, {
|
||||||
required Map<String, Ticket> ticketById,
|
required Map<String, Ticket> ticketById,
|
||||||
required String subjectQuery,
|
required String subjectQuery,
|
||||||
|
required String taskNumber,
|
||||||
required String? officeId,
|
required String? officeId,
|
||||||
required String? status,
|
required String? status,
|
||||||
required String? assigneeId,
|
required String? assigneeId,
|
||||||
|
|
@ -767,7 +788,12 @@ List<Task> _applyTaskFilters(
|
||||||
required Map<String, String?> latestAssigneeByTaskId,
|
required Map<String, String?> latestAssigneeByTaskId,
|
||||||
}) {
|
}) {
|
||||||
final query = subjectQuery.trim().toLowerCase();
|
final query = subjectQuery.trim().toLowerCase();
|
||||||
|
final tnQuery = taskNumber.trim().toLowerCase();
|
||||||
return tasks.where((task) {
|
return tasks.where((task) {
|
||||||
|
if (tnQuery.isNotEmpty &&
|
||||||
|
!(task.taskNumber?.toLowerCase().contains(tnQuery) ?? false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final ticket = task.ticketId == null ? null : ticketById[task.ticketId];
|
final ticket = task.ticketId == null ? null : ticketById[task.ticketId];
|
||||||
final subject = task.title.isNotEmpty
|
final subject = task.title.isNotEmpty
|
||||||
? task.title
|
? task.title
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user