Initial Commit: Duty Schedule and Attendance Logbook
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
import '../models/attendance_log.dart';
|
||||
import '../utils/app_time.dart';
|
||||
import 'profile_provider.dart';
|
||||
import 'reports_provider.dart';
|
||||
import 'supabase_provider.dart';
|
||||
import 'stream_recovery.dart';
|
||||
import 'realtime_controller.dart';
|
||||
|
||||
/// Date range for attendance logbook, defaults to "Last 7 Days".
|
||||
final attendanceDateRangeProvider = StateProvider<ReportDateRange>((ref) {
|
||||
final now = AppTime.now();
|
||||
final today = DateTime(now.year, now.month, now.day);
|
||||
return ReportDateRange(
|
||||
start: today.subtract(const Duration(days: 7)),
|
||||
end: today.add(const Duration(days: 1)),
|
||||
label: 'Last 7 Days',
|
||||
);
|
||||
});
|
||||
|
||||
/// All visible attendance logs (own for standard, all for admin/dispatcher/it_staff).
|
||||
final attendanceLogsProvider = StreamProvider<List<AttendanceLog>>((ref) {
|
||||
final client = ref.watch(supabaseClientProvider);
|
||||
final profileAsync = ref.watch(currentProfileProvider);
|
||||
final profile = profileAsync.valueOrNull;
|
||||
if (profile == null) return Stream.value(const <AttendanceLog>[]);
|
||||
|
||||
final hasFullAccess =
|
||||
profile.role == 'admin' ||
|
||||
profile.role == 'dispatcher' ||
|
||||
profile.role == 'it_staff';
|
||||
|
||||
final wrapper = StreamRecoveryWrapper<AttendanceLog>(
|
||||
stream: hasFullAccess
|
||||
? client
|
||||
.from('attendance_logs')
|
||||
.stream(primaryKey: ['id'])
|
||||
.order('check_in_at', ascending: false)
|
||||
: client
|
||||
.from('attendance_logs')
|
||||
.stream(primaryKey: ['id'])
|
||||
.eq('user_id', profile.id)
|
||||
.order('check_in_at', ascending: false),
|
||||
onPollData: () async {
|
||||
final query = client.from('attendance_logs').select();
|
||||
final data = hasFullAccess
|
||||
? await query.order('check_in_at', ascending: false)
|
||||
: await query
|
||||
.eq('user_id', profile.id)
|
||||
.order('check_in_at', ascending: false);
|
||||
return data.map(AttendanceLog.fromMap).toList();
|
||||
},
|
||||
fromMap: AttendanceLog.fromMap,
|
||||
channelName: 'attendance_logs',
|
||||
onStatusChanged: ref.read(realtimeControllerProvider).handleChannelStatus,
|
||||
);
|
||||
|
||||
ref.onDispose(wrapper.dispose);
|
||||
return wrapper.stream.map((result) => result.data);
|
||||
});
|
||||
|
||||
final attendanceControllerProvider = Provider<AttendanceController>((ref) {
|
||||
final client = ref.watch(supabaseClientProvider);
|
||||
return AttendanceController(client);
|
||||
});
|
||||
|
||||
class AttendanceController {
|
||||
AttendanceController(this._client);
|
||||
|
||||
final SupabaseClient _client;
|
||||
|
||||
/// Check in to a duty schedule. Returns the attendance log ID.
|
||||
Future<String?> checkIn({
|
||||
required String dutyScheduleId,
|
||||
required double lat,
|
||||
required double lng,
|
||||
}) async {
|
||||
final data = await _client.rpc(
|
||||
'attendance_check_in',
|
||||
params: {'p_duty_id': dutyScheduleId, 'p_lat': lat, 'p_lng': lng},
|
||||
);
|
||||
return data as String?;
|
||||
}
|
||||
|
||||
/// Check out from an attendance log.
|
||||
Future<void> checkOut({
|
||||
required String attendanceId,
|
||||
required double lat,
|
||||
required double lng,
|
||||
}) async {
|
||||
await _client.rpc(
|
||||
'attendance_check_out',
|
||||
params: {'p_attendance_id': attendanceId, 'p_lat': lat, 'p_lng': lng},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user