184 lines
5.0 KiB
Dart
184 lines
5.0 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, {
|
|
String? meta,
|
|
}) {
|
|
_update(
|
|
section,
|
|
id,
|
|
(i) => i.copyWith(
|
|
state: ComplianceState.pending,
|
|
statusLabel: 'In review',
|
|
fileName: fileName,
|
|
fileMeta: meta ?? '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,
|
|
);
|
|
});
|