Files
krow-worker-app/lib/screens/requirements_screen.dart

174 lines
5.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../core/theme.dart';
import '../models/compliance.dart';
import '../state/compliance_controller.dart';
import '../state/shifts_controller.dart';
import '../widgets/common.dart';
import 'shift_detail_screen.dart';
/// Everything one business asks for, on its own screen.
///
/// The shift page shows the same check inline; this is the full read, grouped
/// the way the compliance hub groups things, so it is obvious *which* kind of
/// item is missing and where to go and fix it.
class RequirementsScreen extends ConsumerWidget {
const RequirementsScreen({super.key, required this.shiftId});
final String shiftId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final shift = ref.watch(shiftsProvider.notifier).byId(shiftId);
if (shift == null) {
return Scaffold(
appBar: AppBar(),
body: const Center(child: Text('That shift is no longer listed.')),
);
}
final compliance = ref.watch(complianceProvider);
/// The items this shift needs, split by which compliance section they
/// belong to, so each group can be labelled and linked separately.
List<ComplianceItem> group(ComplianceSection section) => compliance
.section(section)
.where((i) => shift.requirements.contains(i.title))
.toList();
final groups = <(ComplianceSection, String, List<ComplianceItem>)>[
(
ComplianceSection.documents,
'Documents',
group(ComplianceSection.documents),
),
(ComplianceSection.wardrobe, 'Attire', group(ComplianceSection.wardrobe)),
(
ComplianceSection.certificates,
'Certificates',
group(ComplianceSection.certificates),
),
];
return Scaffold(
appBar: AppBar(
title: const Text('Requirements'),
actions: [
Padding(
padding: const EdgeInsets.only(right: 16),
child: Center(
child: IconButton(
onPressed: () => context.push('/support'),
icon: const Icon(LucideIcons.messageCircle, size: 20),
tooltip: 'Message the manager',
),
),
),
],
),
body: ListView(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 28),
children: [
Text(
'${shift.role} at ${shift.business}',
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w700,
letterSpacing: -0.7,
),
),
const SizedBox(height: 16),
// The same summary the shift page shows, so the two never disagree.
ShiftRequirements(shift: shift),
for (final (section, label, items) in groups)
if (items.isNotEmpty) ...[
SectionLabel(
label,
trailing: TextButton(
onPressed: () => context.push('/compliance/${section.name}'),
child: const Text('Manage'),
),
),
KrowCard(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
for (var i = 0; i < items.length; i++) ...[
if (i > 0) const Divider(height: 1),
_RequirementRow(item: items[i]),
],
],
),
),
],
const SizedBox(height: 16),
const InfoBanner(
'These are shared with the business only while you are booked on '
'one of their shifts.',
),
],
),
);
}
}
class _RequirementRow extends StatelessWidget {
const _RequirementRow({required this.item});
final ComplianceItem item;
@override
Widget build(BuildContext context) {
final ok = item.state == ComplianceState.verified;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 11),
child: Row(
children: [
Container(
width: 22,
height: 22,
decoration: BoxDecoration(
color: ok ? KrowPalette.blueTint : const Color(0xFFF7E4AE),
borderRadius: BorderRadius.circular(7),
),
child: Icon(
ok ? LucideIcons.check : LucideIcons.clock,
size: 12,
color: ok ? KrowPalette.blue : KrowPalette.ink,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 1),
Text(
item.subtitle,
style: const TextStyle(
fontSize: 12,
height: 1.4,
color: KrowPalette.mute,
),
),
],
),
),
const SizedBox(width: 8),
StatusChip(item.badge, tone: ok ? ChipTone.neutral : ChipTone.yellow),
],
),
);
}
}