Attendance validation involving Location Detection + Facial Recoginition with Liveness Detection
This commit is contained in:
@@ -11,6 +11,9 @@ class AttendanceLog {
|
||||
this.checkOutAt,
|
||||
this.checkOutLat,
|
||||
this.checkOutLng,
|
||||
this.justification,
|
||||
this.verificationStatus = 'pending',
|
||||
this.verificationPhotoUrl,
|
||||
});
|
||||
|
||||
final String id;
|
||||
@@ -22,8 +25,14 @@ class AttendanceLog {
|
||||
final DateTime? checkOutAt;
|
||||
final double? checkOutLat;
|
||||
final double? checkOutLng;
|
||||
final String? justification;
|
||||
final String verificationStatus; // pending, verified, unverified, skipped
|
||||
final String? verificationPhotoUrl;
|
||||
|
||||
bool get isCheckedOut => checkOutAt != null;
|
||||
bool get isVerified => verificationStatus == 'verified';
|
||||
bool get isUnverified =>
|
||||
verificationStatus == 'unverified' || verificationStatus == 'skipped';
|
||||
|
||||
factory AttendanceLog.fromMap(Map<String, dynamic> map) {
|
||||
return AttendanceLog(
|
||||
@@ -38,6 +47,9 @@ class AttendanceLog {
|
||||
: AppTime.parse(map['check_out_at'] as String),
|
||||
checkOutLat: (map['check_out_lat'] as num?)?.toDouble(),
|
||||
checkOutLng: (map['check_out_lng'] as num?)?.toDouble(),
|
||||
justification: map['justification'] as String?,
|
||||
verificationStatus: map['verification_status'] as String? ?? 'pending',
|
||||
verificationPhotoUrl: map['verification_photo_url'] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import '../utils/app_time.dart';
|
||||
|
||||
class LeaveOfAbsence {
|
||||
LeaveOfAbsence({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
required this.leaveType,
|
||||
required this.justification,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.status,
|
||||
required this.filedBy,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String userId;
|
||||
final String leaveType;
|
||||
final String justification;
|
||||
final DateTime startTime;
|
||||
final DateTime endTime;
|
||||
final String status;
|
||||
final String filedBy;
|
||||
final DateTime createdAt;
|
||||
|
||||
factory LeaveOfAbsence.fromMap(Map<String, dynamic> map) {
|
||||
return LeaveOfAbsence(
|
||||
id: map['id'] as String,
|
||||
userId: map['user_id'] as String,
|
||||
leaveType: map['leave_type'] as String,
|
||||
justification: map['justification'] as String,
|
||||
startTime: AppTime.parse(map['start_time'] as String),
|
||||
endTime: AppTime.parse(map['end_time'] as String),
|
||||
status: map['status'] as String? ?? 'pending',
|
||||
filedBy: map['filed_by'] as String,
|
||||
createdAt: AppTime.parse(map['created_at'] as String),
|
||||
);
|
||||
}
|
||||
|
||||
String get leaveTypeLabel {
|
||||
switch (leaveType) {
|
||||
case 'emergency_leave':
|
||||
return 'Emergency Leave';
|
||||
case 'parental_leave':
|
||||
return 'Parental Leave';
|
||||
case 'sick_leave':
|
||||
return 'Sick Leave';
|
||||
case 'vacation_leave':
|
||||
return 'Vacation Leave';
|
||||
default:
|
||||
return leaveType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@ class Profile {
|
||||
required this.fullName,
|
||||
this.religion = 'catholic',
|
||||
this.allowTracking = false,
|
||||
this.avatarUrl,
|
||||
this.facePhotoUrl,
|
||||
this.faceEnrolledAt,
|
||||
});
|
||||
|
||||
final String id;
|
||||
@@ -12,6 +15,11 @@ class Profile {
|
||||
final String fullName;
|
||||
final String religion;
|
||||
final bool allowTracking;
|
||||
final String? avatarUrl;
|
||||
final String? facePhotoUrl;
|
||||
final DateTime? faceEnrolledAt;
|
||||
|
||||
bool get hasFaceEnrolled => facePhotoUrl != null && faceEnrolledAt != null;
|
||||
|
||||
factory Profile.fromMap(Map<String, dynamic> map) {
|
||||
return Profile(
|
||||
@@ -20,6 +28,11 @@ class Profile {
|
||||
fullName: map['full_name'] as String? ?? '',
|
||||
religion: map['religion'] as String? ?? 'catholic',
|
||||
allowTracking: map['allow_tracking'] as bool? ?? false,
|
||||
avatarUrl: map['avatar_url'] as String?,
|
||||
facePhotoUrl: map['face_photo_url'] as String?,
|
||||
faceEnrolledAt: map['face_enrolled_at'] == null
|
||||
? null
|
||||
: DateTime.tryParse(map['face_enrolled_at'] as String),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import '../utils/app_time.dart';
|
||||
|
||||
/// A cross-device face verification session.
|
||||
///
|
||||
/// Created on web when no camera is detected. The web client generates a QR
|
||||
/// code containing the session ID. The mobile client scans the QR, performs
|
||||
/// liveness detection, uploads the photo, and marks the session completed.
|
||||
class VerificationSession {
|
||||
VerificationSession({
|
||||
required this.id,
|
||||
required this.userId,
|
||||
required this.type,
|
||||
this.contextId,
|
||||
this.status = 'pending',
|
||||
this.imageUrl,
|
||||
required this.createdAt,
|
||||
required this.expiresAt,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String userId;
|
||||
final String type; // 'enrollment' or 'verification'
|
||||
final String? contextId; // e.g. attendance_log_id
|
||||
final String status; // 'pending', 'completed', 'expired'
|
||||
final String? imageUrl;
|
||||
final DateTime createdAt;
|
||||
final DateTime expiresAt;
|
||||
|
||||
bool get isPending => status == 'pending';
|
||||
bool get isCompleted => status == 'completed';
|
||||
bool get isExpired =>
|
||||
status == 'expired' || DateTime.now().toUtc().isAfter(expiresAt);
|
||||
|
||||
factory VerificationSession.fromMap(Map<String, dynamic> map) {
|
||||
return VerificationSession(
|
||||
id: map['id'] as String,
|
||||
userId: map['user_id'] as String,
|
||||
type: map['type'] as String,
|
||||
contextId: map['context_id'] as String?,
|
||||
status: (map['status'] as String?) ?? 'pending',
|
||||
imageUrl: map['image_url'] as String?,
|
||||
createdAt: AppTime.parse(map['created_at'] as String),
|
||||
expiresAt: AppTime.parse(map['expires_at'] as String),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'user_id': userId,
|
||||
'type': type,
|
||||
if (contextId != null) 'context_id': contextId,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user