mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Add "go to last page" and page count to notifications and devices lists
The list endpoints now return a total count alongside the page so the
front-end can show how many pages exist and offer a last-page jump.
Backend: add count(is_new) and count_devices(...) (sharing a WHERE-builder
with list_devices so page and count can't drift), wrap both list responses
in {items, total_count} structs, and register them with utoipa.
Front-end: parse the wrapper shape (dropping the fetch-one-extra trick),
add a Last-page button and a responsive "Page X of Y" / "X / Y" label to
the shared PaginationBar, and track the total in both lists. Notifications
re-sync the count on every fetch and decrement it locally on mark-read/
unread removals so the count stays accurate without a re-fetch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3cb104e364
commit
844d7d189c
@@ -35,60 +35,62 @@ void main() {
|
||||
expect(summary.totalRegistered, 7);
|
||||
});
|
||||
|
||||
test('listDevices sends filter + pagination params and trims extra item',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/devices',
|
||||
(server) => server.reply(200, [
|
||||
deviceJson(macAddress: '00:00:00:00:00:01'),
|
||||
deviceJson(macAddress: '00:00:00:00:00:02'),
|
||||
deviceJson(macAddress: '00:00:00:00:00:03'),
|
||||
]),
|
||||
queryParameters: {
|
||||
'is_registered': false,
|
||||
'device_type': 'laptop',
|
||||
'sort_by': 'last_seen',
|
||||
'sort_order': 'asc',
|
||||
'page_offset': 0,
|
||||
'page_limit': 3,
|
||||
},
|
||||
);
|
||||
test(
|
||||
'listDevices sends filter + pagination params and reports the total',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/devices',
|
||||
(server) => server.reply(
|
||||
200,
|
||||
pagedListJson([
|
||||
deviceJson(macAddress: '00:00:00:00:00:01'),
|
||||
deviceJson(macAddress: '00:00:00:00:00:02'),
|
||||
], totalCount: 12),
|
||||
),
|
||||
queryParameters: {
|
||||
'is_registered': false,
|
||||
'device_type': 'laptop',
|
||||
'sort_by': 'last_seen',
|
||||
'sort_order': 'asc',
|
||||
'page_offset': 0,
|
||||
'page_limit': 2,
|
||||
},
|
||||
);
|
||||
|
||||
final result = await BackendAPI.instance.listDevices(
|
||||
isRegistered: false,
|
||||
deviceType: DeviceType.laptop,
|
||||
sortBy: 'last_seen',
|
||||
sortAscending: true,
|
||||
page: 0,
|
||||
perPage: 2,
|
||||
);
|
||||
final result = await BackendAPI.instance.listDevices(
|
||||
isRegistered: false,
|
||||
deviceType: DeviceType.laptop,
|
||||
sortBy: 'last_seen',
|
||||
sortAscending: true,
|
||||
page: 0,
|
||||
perPage: 2,
|
||||
);
|
||||
|
||||
expect(result.items, hasLength(2));
|
||||
expect(result.hasNextPage, isTrue);
|
||||
});
|
||||
expect(result.items, hasLength(2));
|
||||
expect(result.totalCount, 12);
|
||||
},
|
||||
);
|
||||
|
||||
test('listDevices reports no next page when fewer than perPage+1 returned',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/devices',
|
||||
(server) => server.reply(200, [deviceJson()]),
|
||||
);
|
||||
test(
|
||||
'listDevices reports the total when a single page is returned',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/devices',
|
||||
(server) => server.reply(200, pagedListJson([deviceJson()])),
|
||||
);
|
||||
|
||||
final result = await BackendAPI.instance.listDevices(perPage: 2);
|
||||
final result = await BackendAPI.instance.listDevices(perPage: 2);
|
||||
|
||||
expect(result.items, hasLength(1));
|
||||
expect(result.hasNextPage, isFalse);
|
||||
});
|
||||
expect(result.items, hasLength(1));
|
||||
expect(result.totalCount, 1);
|
||||
},
|
||||
);
|
||||
|
||||
test('listDevices maps the unknown device type to an empty filter', () async {
|
||||
adapter.onGet(
|
||||
'/devices',
|
||||
(server) => server.reply(200, <dynamic>[]),
|
||||
queryParameters: {
|
||||
'device_type': '',
|
||||
'page_offset': 0,
|
||||
'page_limit': 11,
|
||||
},
|
||||
(server) => server.reply(200, pagedListJson([])),
|
||||
queryParameters: {'device_type': '', 'page_offset': 0, 'page_limit': 10},
|
||||
);
|
||||
|
||||
final result = await BackendAPI.instance.listDevices(
|
||||
|
||||
@@ -13,10 +13,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('markNotificationAsRead GETs the notification path', () async {
|
||||
adapter.onGet(
|
||||
'/notifications/7',
|
||||
(server) => server.reply(200, null),
|
||||
);
|
||||
adapter.onGet('/notifications/7', (server) => server.reply(200, null));
|
||||
|
||||
await BackendAPI.instance.markNotificationAsRead(7);
|
||||
});
|
||||
@@ -39,51 +36,49 @@ void main() {
|
||||
await BackendAPI.instance.markAllNotificationsAsRead();
|
||||
});
|
||||
|
||||
test('listNotifications sends is_new + pagination and trims extra item',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/notifications',
|
||||
(server) => server.reply(200, [
|
||||
notificationJson(id: 1),
|
||||
notificationJson(id: 2),
|
||||
notificationJson(id: 3),
|
||||
]),
|
||||
queryParameters: {
|
||||
'is_new': true,
|
||||
'page_offset': 0,
|
||||
'page_limit': 3,
|
||||
},
|
||||
);
|
||||
test(
|
||||
'listNotifications sends is_new + pagination and reports the total',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/notifications',
|
||||
(server) => server.reply(
|
||||
200,
|
||||
pagedListJson([
|
||||
notificationJson(id: 1),
|
||||
notificationJson(id: 2),
|
||||
], totalCount: 9),
|
||||
),
|
||||
queryParameters: {'is_new': true, 'page_offset': 0, 'page_limit': 2},
|
||||
);
|
||||
|
||||
final result = await BackendAPI.instance.listNotifications(
|
||||
true,
|
||||
page: 0,
|
||||
perPage: 2,
|
||||
);
|
||||
final result = await BackendAPI.instance.listNotifications(
|
||||
true,
|
||||
page: 0,
|
||||
perPage: 2,
|
||||
);
|
||||
|
||||
expect(result.items, hasLength(2));
|
||||
expect(result.hasNextPage, isTrue);
|
||||
});
|
||||
expect(result.items, hasLength(2));
|
||||
expect(result.totalCount, 9);
|
||||
},
|
||||
);
|
||||
|
||||
test('listNotifications maps a null filter to an empty is_new param',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/notifications',
|
||||
(server) => server.reply(200, <dynamic>[]),
|
||||
queryParameters: {
|
||||
'is_new': '',
|
||||
'page_offset': 10,
|
||||
'page_limit': 6,
|
||||
},
|
||||
);
|
||||
test(
|
||||
'listNotifications maps a null filter to an empty is_new param',
|
||||
() async {
|
||||
adapter.onGet(
|
||||
'/notifications',
|
||||
(server) => server.reply(200, pagedListJson([])),
|
||||
queryParameters: {'is_new': '', 'page_offset': 10, 'page_limit': 5},
|
||||
);
|
||||
|
||||
final result = await BackendAPI.instance.listNotifications(
|
||||
null,
|
||||
page: 2,
|
||||
perPage: 5,
|
||||
);
|
||||
final result = await BackendAPI.instance.listNotifications(
|
||||
null,
|
||||
page: 2,
|
||||
perPage: 5,
|
||||
);
|
||||
|
||||
expect(result.items, isEmpty);
|
||||
expect(result.hasNextPage, isFalse);
|
||||
});
|
||||
expect(result.items, isEmpty);
|
||||
expect(result.totalCount, 0);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user