Initial commit: Krow Worker App
This commit is contained in:
178
lib/state/compliance_controller.dart
Normal file
178
lib/state/compliance_controller.dart
Normal file
@@ -0,0 +1,178 @@
|
||||
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,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user