Files
krow-worker-app/lib/state/compliance_controller.dart
2026-09-17 22:26:11 +05:30

179 lines
4.9 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/compliance.dart';
import 'seed.dart';
/// The four compliance sections, each a list the worker can act on.
enum ComplianceSection { documents, certificates, taxForms, wardrobe }
extension ComplianceSectionX on ComplianceSection {
String get title => switch (this) {
ComplianceSection.documents => 'Documents',
ComplianceSection.certificates => 'Certificates',
ComplianceSection.taxForms => 'Tax forms',
ComplianceSection.wardrobe => 'Attire',
};
String get subtitle => switch (this) {
ComplianceSection.documents => 'ID, SSN, work permit, address',
ComplianceSection.certificates => 'Food handler, RBS and more',
ComplianceSection.taxForms => 'Form I-9 and Form W-4',
ComplianceSection.wardrobe => "Photos of what you'll wear",
};
}
class ComplianceData {
const ComplianceData({
required this.documents,
required this.certificates,
required this.taxForms,
required this.wardrobe,
});
final List<ComplianceItem> documents;
final List<ComplianceItem> certificates;
final List<ComplianceItem> taxForms;
final List<ComplianceItem> wardrobe;
List<ComplianceItem> section(ComplianceSection s) => switch (s) {
ComplianceSection.documents => documents,
ComplianceSection.certificates => certificates,
ComplianceSection.taxForms => taxForms,
ComplianceSection.wardrobe => wardrobe,
};
List<ComplianceItem> get all => [
...documents,
...certificates,
...taxForms,
...wardrobe,
];
ComplianceData copyWith({
List<ComplianceItem>? documents,
List<ComplianceItem>? certificates,
List<ComplianceItem>? taxForms,
List<ComplianceItem>? wardrobe,
}) => ComplianceData(
documents: documents ?? this.documents,
certificates: certificates ?? this.certificates,
taxForms: taxForms ?? this.taxForms,
wardrobe: wardrobe ?? this.wardrobe,
);
}
class ComplianceController extends Notifier<ComplianceData> {
@override
ComplianceData build() => ComplianceData(
documents: Seed.documents(),
certificates: Seed.certificates(),
taxForms: Seed.taxForms(),
wardrobe: Seed.wardrobe(),
);
List<ComplianceItem> _apply(
List<ComplianceItem> list,
String id,
ComplianceItem Function(ComplianceItem) f,
) => [
for (final i in list)
if (i.id == id) f(i) else i,
];
void _update(
ComplianceSection section,
String id,
ComplianceItem Function(ComplianceItem) f,
) {
final s = state;
state = switch (section) {
ComplianceSection.documents => s.copyWith(
documents: _apply(s.documents, id, f),
),
ComplianceSection.certificates => s.copyWith(
certificates: _apply(s.certificates, id, f),
),
ComplianceSection.taxForms => s.copyWith(
taxForms: _apply(s.taxForms, id, f),
),
ComplianceSection.wardrobe => s.copyWith(
wardrobe: _apply(s.wardrobe, id, f),
),
};
}
/// Stand-in for the camera / file picker: attaching a file sends the item
/// back for review.
void uploadFile(ComplianceSection section, String id, String fileName) {
_update(
section,
id,
(i) => i.copyWith(
state: ComplianceState.pending,
statusLabel: 'In review',
fileName: fileName,
fileMeta: 'Uploaded just now',
),
);
}
void removeFile(ComplianceSection section, String id) {
_update(
section,
id,
(i) => ComplianceItem(
id: i.id,
title: i.title,
subtitle: i.subtitle,
state: ComplianceState.missing,
statusLabel: 'Not added',
required_: i.required_,
),
);
}
void setExpiry(ComplianceSection section, String id, DateTime date) {
_update(section, id, (i) => i.copyWith(expires: date));
}
ComplianceItem? find(ComplianceSection section, String id) {
for (final i in state.section(section)) {
if (i.id == id) return i;
}
return null;
}
}
final complianceProvider =
NotifierProvider<ComplianceController, ComplianceData>(
ComplianceController.new,
);
/// `(verified, total)` across every required item — what the hub reports.
final complianceProgressProvider = Provider<(int, int)>((ref) {
final items = ref.watch(complianceProvider).all.where((i) => i.required_);
final total = items.length;
final done = items.where((i) => i.state == ComplianceState.verified).length;
return (done, total);
});
/// Whether the worker is cleared to take shifts at all.
final isCompliantProvider = Provider<bool>((ref) {
final (done, total) = ref.watch(complianceProgressProvider);
return done == total;
});
final sectionProgressProvider = Provider.family<(int, int), ComplianceSection>((
ref,
section,
) {
final items = ref
.watch(complianceProvider)
.section(section)
.where((i) => i.required_);
return (
items.where((i) => i.state == ComplianceState.verified).length,
items.length,
);
});