bottleneckcrm/mobile/lib/data/api_envelope.dart
Ashwin 4d2b67f7d8 feat: enhance solution detail screen with access control for editing and deleting
feat: add initial due date support in task form screen
fix: ensure completed tasks are displayed correctly in tasks list screen
fix: resolve layout issues in ticket detail screen buttons
fix: improve error messaging for network failures in API service
fix: handle organization data correctly in auth service
fix: update task row to prevent layout issues with dismissible actions
fix: ensure multi-select sheet buttons layout correctly
test: add tests for KPI cards in dashboard screen
test: add tests for deals filter sheet layout
test: add tests for solution detail screen access control
test: add tests for task create date functionality
test: add tests for task row dismissible behavior
test: add tests for tasks list screen behavior with completed tasks
test: add tests for API envelope handling
test: add tests for API service network failure handling
test: add tests for permissions logic
2026-08-06 23:35:54 +05:30

39 lines
1.4 KiB
Dart

/// Pulling a list out of a response body, whatever the endpoint calls it.
///
/// The API has no single list envelope. `/api/contacts/` publishes `results`,
/// `/api/users/get-teams-and-users/` publishes `profiles` and `teams`, and
/// `/api/accounts/` nests its rows two deep under
/// `active_accounts.open_accounts`. Each caller used to guess with a chain of
/// `if (data['x'] != null)`, which fails silently: a wrong guess yields an
/// empty list and a picker with nothing in it, not an error anyone sees.
///
/// Passing the paths explicitly keeps the guess in one place and makes it
/// testable against the real shape.
library;
/// The first list found at any of [paths], or `[]` if none matched.
///
/// A path is dot-separated and may descend through nested maps, so
/// `'active_accounts.open_accounts'` is valid. Order matters: the first path
/// that resolves to a list wins, so put the endpoint's real key first and any
/// tolerated alternatives after it.
List<Map<String, dynamic>> listFromEnvelope(
Map<String, dynamic> data,
List<String> paths,
) {
for (final path in paths) {
dynamic node = data;
for (final segment in path.split('.')) {
if (node is Map<String, dynamic> && node.containsKey(segment)) {
node = node[segment];
} else {
node = null;
break;
}
}
if (node is List) {
return node.whereType<Map<String, dynamic>>().toList();
}
}
return const [];
}