UI Phase 2 (L0): shared layout primitives + chrome standardization
Extract four shared layout widgets from the best existing implementations and adopt them across list/overview screens, deleting duplicated inline code. Layout/consistency only — no behavior change. New primitives (lib/widgets/): - app_section_header.dart — AppSectionHeader (azure accent bar + uppercase telemetry label; promoted from the dashboard's _sectionTitle). - app_metric_card.dart — AppMetricCard (uppercase label + mono value on a flat hairline surface; unifies KPI/stat tiles). - app_status_summary.dart — AppStatusSummaryRow/Item/Chip (dot + count + label, selectable; semantic AppStatusColors). - app_filter_bar.dart — AppFilterBar (responsive filter-row chrome). Adopted / de-duplicated: - Dashboard: _MetricCard now wraps AppMetricCard; section titles use AppSectionHeader (removed local _sectionTitle). - Tickets / Tasks / ISR: three separate summary-chip implementations (_StatusSummaryCard x2, _SummaryChip) collapsed into AppStatusSummaryRow. - Network Map overview: Sites/Devices tiles -> AppMetricCard (removed local _SummaryCard). - Workforce: "Duty Schedules" header -> AppSectionHeader with trailing chip. Verified: flutter analyze lib clean; flutter build web green; visual QA (tickets + ISR summary chips) in dark. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -7,6 +7,7 @@ import '../../providers/network_map/network_devices_provider.dart';
|
||||
import '../../providers/network_map/network_sites_provider.dart';
|
||||
import '../../providers/profile_provider.dart';
|
||||
import '../../utils/snackbar.dart';
|
||||
import '../../widgets/app_metric_card.dart';
|
||||
import '../../widgets/app_page_header.dart';
|
||||
import '../../widgets/app_state_view.dart';
|
||||
import '../../widgets/responsive_body.dart';
|
||||
@@ -167,41 +168,24 @@ class _SummaryRow extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(child: _SummaryCard(label: 'Sites', value: siteCount.toString(), icon: Icons.location_city_outlined)),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: _SummaryCard(label: 'Devices', value: deviceCount.toString(), icon: Icons.devices_outlined)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SummaryCard extends StatelessWidget {
|
||||
const _SummaryCard({required this.label, required this.value, required this.icon});
|
||||
final String label;
|
||||
final String value;
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tt = Theme.of(context).textTheme;
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: cs.primary),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: tt.labelSmall?.copyWith(color: cs.onSurfaceVariant)),
|
||||
Text(value, style: tt.headlineSmall?.copyWith(fontWeight: FontWeight.w700)),
|
||||
],
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: AppMetricCard(
|
||||
title: 'Sites',
|
||||
value: siteCount.toString(),
|
||||
icon: Icons.location_city_outlined,
|
||||
animateValue: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: AppMetricCard(
|
||||
title: 'Devices',
|
||||
value: deviceCount.toString(),
|
||||
icon: Icons.devices_outlined,
|
||||
animateValue: false,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user