- Add Control Room page with stacks sidebar and containers list - Implement container actions (start/stop/restart) with snackbars - Add container logs viewer dialog - Add search/filter for both stacks and containers lists - Add external links for Portainer and Netdata (url_launcher) - Add VS Code launch configuration for Flutter web debugging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
109 lines
3.0 KiB
Dart
109 lines
3.0 KiB
Dart
/// Result from a data source fetch operation.
|
|
class DataGridResult<T> {
|
|
const DataGridResult({
|
|
required this.items,
|
|
required this.totalCount,
|
|
this.hasMore = false,
|
|
});
|
|
|
|
/// The fetched items.
|
|
final List<T> items;
|
|
|
|
/// Total count of items (for pagination display).
|
|
final int totalCount;
|
|
|
|
/// Whether there are more items to load (for infinite scroll).
|
|
final bool hasMore;
|
|
|
|
/// Creates an empty result.
|
|
const DataGridResult.empty()
|
|
: items = const [],
|
|
totalCount = 0,
|
|
hasMore = false;
|
|
}
|
|
|
|
/// Abstract data source for DataGrid.
|
|
///
|
|
/// Implement this to provide data to the grid. Can be backed by
|
|
/// API calls, local database, or in-memory lists.
|
|
abstract class DataGridSource<T> {
|
|
/// Fetches items from the data source.
|
|
///
|
|
/// - [searchQuery]: Optional search text to filter results.
|
|
/// - [sortField]: Field name to sort by.
|
|
/// - [sortDescending]: Whether to sort in descending order.
|
|
/// - [offset]: Number of items to skip (for pagination).
|
|
/// - [limit]: Maximum number of items to return.
|
|
Future<DataGridResult<T>> fetch({
|
|
String? searchQuery,
|
|
String? sortField,
|
|
bool sortDescending = false,
|
|
int? offset,
|
|
int? limit,
|
|
});
|
|
|
|
/// Gets the total count of items matching the query.
|
|
///
|
|
/// Override this if you need a separate count query.
|
|
/// By default, returns the totalCount from the last fetch.
|
|
Future<int> count({String? searchQuery}) async {
|
|
final result = await fetch(searchQuery: searchQuery, limit: 0);
|
|
return result.totalCount;
|
|
}
|
|
}
|
|
|
|
/// In-memory data source for local data.
|
|
class InMemoryDataSource<T> extends DataGridSource<T> {
|
|
InMemoryDataSource({
|
|
required this.items,
|
|
this.searchMatcher,
|
|
this.sortComparator,
|
|
});
|
|
|
|
/// All items in the data source.
|
|
final List<T> items;
|
|
|
|
/// Function to check if an item matches the search query.
|
|
final bool Function(T item, String query)? searchMatcher;
|
|
|
|
/// Function to compare two items for sorting.
|
|
final int Function(T a, T b, String field, bool descending)? sortComparator;
|
|
|
|
@override
|
|
Future<DataGridResult<T>> fetch({
|
|
String? searchQuery,
|
|
String? sortField,
|
|
bool sortDescending = false,
|
|
int? offset,
|
|
int? limit,
|
|
}) async {
|
|
var result = List<T>.from(items);
|
|
|
|
// Apply search filter
|
|
if (searchQuery != null && searchQuery.isNotEmpty && searchMatcher != null) {
|
|
result = result.where((item) => searchMatcher!(item, searchQuery)).toList();
|
|
}
|
|
|
|
// Apply sorting
|
|
if (sortField != null && sortComparator != null) {
|
|
result.sort((a, b) => sortComparator!(a, b, sortField, sortDescending));
|
|
}
|
|
|
|
final totalCount = result.length;
|
|
|
|
// Apply pagination
|
|
if (offset != null && offset > 0) {
|
|
result = result.skip(offset).toList();
|
|
}
|
|
if (limit != null && limit > 0) {
|
|
result = result.take(limit).toList();
|
|
}
|
|
|
|
return DataGridResult(
|
|
items: result,
|
|
totalCount: totalCount,
|
|
hasMore: offset != null && limit != null && (offset + limit) < totalCount,
|
|
);
|
|
}
|
|
}
|