diff --git a/apps/mobile/packages/core/lib/src/services/api_service/core_api_services/verification/verification_response.dart b/apps/mobile/packages/core/lib/src/services/api_service/core_api_services/verification/verification_response.dart index b59072c6..38f2ba25 100644 --- a/apps/mobile/packages/core/lib/src/services/api_service/core_api_services/verification/verification_response.dart +++ b/apps/mobile/packages/core/lib/src/services/api_service/core_api_services/verification/verification_response.dart @@ -1,3 +1,43 @@ +/// Represents the possible statuses of a verification job. +enum VerificationStatus { + /// Job is created and waiting to be processed. + pending('PENDING'), + + /// Job is currently being processed by machine or human. + processing('PROCESSING'), + + /// Machine verification passed automatically. + autoPass('AUTO_PASS'), + + /// Machine verification failed automatically. + autoFail('AUTO_FAIL'), + + /// Machine results are inconclusive and require human review. + needsReview('NEEDS_REVIEW'), + + /// Human reviewer approved the verification. + approved('APPROVED'), + + /// Human reviewer rejected the verification. + rejected('REJECTED'), + + /// An error occurred during processing. + error('ERROR'); + + const VerificationStatus(this.value); + + /// The string value expected by the Core API. + final String value; + + /// Creates a [VerificationStatus] from a string. + static VerificationStatus fromString(String value) { + return VerificationStatus.values.firstWhere( + (VerificationStatus e) => e.value == value, + orElse: () => VerificationStatus.error, + ); + } +} + /// Response model for verification operations. class VerificationResponse { /// Creates a [VerificationResponse]. @@ -13,7 +53,7 @@ class VerificationResponse { factory VerificationResponse.fromJson(Map json) { return VerificationResponse( verificationId: json['verificationId'] as String, - status: json['status'] as String, + status: VerificationStatus.fromString(json['status'] as String), type: json['type'] as String?, review: json['review'] != null ? json['review'] as Map @@ -25,8 +65,8 @@ class VerificationResponse { /// The unique ID of the verification job. final String verificationId; - /// Current status (e.g., PENDING, PROCESSING, SUCCESS, FAILED, NEEDS_REVIEW). - final String status; + /// Current status of the verification. + final VerificationStatus status; /// The type of verification (e.g., attire, government_id). final String? type; @@ -41,7 +81,7 @@ class VerificationResponse { Map toJson() { return { 'verificationId': verificationId, - 'status': status, + 'status': status.value, 'type': type, 'review': review, 'requestId': requestId, diff --git a/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart b/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart index edbfa78e..24f01a00 100644 --- a/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart +++ b/apps/mobile/packages/data_connect/lib/src/connectors/staff/data/repositories/staff_connector_repository_impl.dart @@ -1,8 +1,7 @@ -// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports -import 'package:firebase_data_connect/firebase_data_connect.dart'; -import 'package:krow_data_connect/krow_data_connect.dart' - hide AttireVerificationStatus; -import 'package:krow_domain/krow_domain.dart'; +import 'package:firebase_data_connect/firebase_data_connect.dart'; +import 'package:krow_data_connect/krow_data_connect.dart' as dc; +import 'package:krow_domain/krow_domain.dart' as domain; +import '../../domain/repositories/staff_connector_repository.dart'; /// Implementation of [StaffConnectorRepository]. /// @@ -12,10 +11,10 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { /// Creates a new [StaffConnectorRepositoryImpl]. /// /// Requires a [DataConnectService] instance for backend communication. - StaffConnectorRepositoryImpl({DataConnectService? service}) - : _service = service ?? DataConnectService.instance; + StaffConnectorRepositoryImpl({dc.DataConnectService? service}) + : _service = service ?? dc.DataConnectService.instance; - final DataConnectService _service; + final dc.DataConnectService _service; @override Future getProfileCompletion() async { @@ -23,17 +22,17 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { final String staffId = await _service.getStaffId(); final QueryResult< - GetStaffProfileCompletionData, - GetStaffProfileCompletionVariables + dc.GetStaffProfileCompletionData, + dc.GetStaffProfileCompletionVariables > response = await _service.connector .getStaffProfileCompletion(id: staffId) .execute(); - final GetStaffProfileCompletionStaff? staff = response.data.staff; - final List emergencyContacts = - response.data.emergencyContacts; - final List taxForms = + final dc.GetStaffProfileCompletionStaff? staff = response.data.staff; + final List + emergencyContacts = response.data.emergencyContacts; + final List taxForms = response.data.taxForms; return _isProfileComplete(staff, emergencyContacts, taxForms); @@ -46,15 +45,14 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { final String staffId = await _service.getStaffId(); final QueryResult< - GetStaffPersonalInfoCompletionData, - GetStaffPersonalInfoCompletionVariables + dc.GetStaffPersonalInfoCompletionData, + dc.GetStaffPersonalInfoCompletionVariables > response = await _service.connector .getStaffPersonalInfoCompletion(id: staffId) .execute(); - final GetStaffPersonalInfoCompletionStaff? staff = response.data.staff; - + final dc.GetStaffPersonalInfoCompletionStaff? staff = response.data.staff; return _isPersonalInfoComplete(staff); }); } @@ -65,8 +63,8 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { final String staffId = await _service.getStaffId(); final QueryResult< - GetStaffEmergencyProfileCompletionData, - GetStaffEmergencyProfileCompletionVariables + dc.GetStaffEmergencyProfileCompletionData, + dc.GetStaffEmergencyProfileCompletionVariables > response = await _service.connector .getStaffEmergencyProfileCompletion(id: staffId) @@ -82,16 +80,15 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { final String staffId = await _service.getStaffId(); final QueryResult< - GetStaffExperienceProfileCompletionData, - GetStaffExperienceProfileCompletionVariables + dc.GetStaffExperienceProfileCompletionData, + dc.GetStaffExperienceProfileCompletionVariables > response = await _service.connector .getStaffExperienceProfileCompletion(id: staffId) .execute(); - final GetStaffExperienceProfileCompletionStaff? staff = + final dc.GetStaffExperienceProfileCompletionStaff? staff = response.data.staff; - return _hasExperience(staff); }); } @@ -102,8 +99,8 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { final String staffId = await _service.getStaffId(); final QueryResult< - GetStaffTaxFormsProfileCompletionData, - GetStaffTaxFormsProfileCompletionVariables + dc.GetStaffTaxFormsProfileCompletionData, + dc.GetStaffTaxFormsProfileCompletionVariables > response = await _service.connector .getStaffTaxFormsProfileCompletion(id: staffId) @@ -114,150 +111,162 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { } /// Checks if personal info is complete. - bool _isPersonalInfoComplete(GetStaffPersonalInfoCompletionStaff? staff) { + bool _isPersonalInfoComplete(dc.GetStaffPersonalInfoCompletionStaff? staff) { if (staff == null) return false; final String fullName = staff.fullName; final String? email = staff.email; final String? phone = staff.phone; - return (fullName.trim().isNotEmpty ?? false) && + return fullName.trim().isNotEmpty && (email?.trim().isNotEmpty ?? false) && (phone?.trim().isNotEmpty ?? false); } /// Checks if staff has experience data (skills or industries). - bool _hasExperience(GetStaffExperienceProfileCompletionStaff? staff) { + bool _hasExperience(dc.GetStaffExperienceProfileCompletionStaff? staff) { if (staff == null) return false; - final dynamic skills = staff.skills; - final dynamic industries = staff.industries; - return (skills is List && skills.isNotEmpty) || - (industries is List && industries.isNotEmpty); + final List? skills = staff.skills; + final List? industries = staff.industries; + return (skills?.isNotEmpty ?? false) || (industries?.isNotEmpty ?? false); } /// Determines if the profile is complete based on all sections. bool _isProfileComplete( - GetStaffProfileCompletionStaff? staff, - List emergencyContacts, - List taxForms, + dc.GetStaffProfileCompletionStaff? staff, + List emergencyContacts, + List taxForms, ) { if (staff == null) return false; - final dynamic skills = staff.skills; - final dynamic industries = staff.industries; + + final List? skills = staff.skills; + final List? industries = staff.industries; final bool hasExperience = - (skills is List && skills.isNotEmpty) || - (industries is List && industries.isNotEmpty); - return emergencyContacts.isNotEmpty && taxForms.isNotEmpty && hasExperience; + (skills?.isNotEmpty ?? false) || (industries?.isNotEmpty ?? false); + + return (staff.fullName.trim().isNotEmpty) && + (staff.email?.trim().isNotEmpty ?? false) && + emergencyContacts.isNotEmpty && + taxForms.isNotEmpty && + hasExperience; } @override - Future getStaffProfile() async { + Future getStaffProfile() async { return _service.run(() async { final String staffId = await _service.getStaffId(); - final QueryResult response = - await _service.connector.getStaffById(id: staffId).execute(); + final QueryResult + response = await _service.connector.getStaffById(id: staffId).execute(); - if (response.data.staff == null) { - throw const ServerException(technicalMessage: 'Staff not found'); + final dc.GetStaffByIdStaff? staff = response.data.staff; + + if (staff == null) { + throw Exception('Staff not found'); } - final GetStaffByIdStaff rawStaff = response.data.staff!; - - // Map the raw data connect object to the Domain Entity - return Staff( - id: rawStaff.id, - authProviderId: rawStaff.userId, - name: rawStaff.fullName, - email: rawStaff.email ?? '', - phone: rawStaff.phone, - avatar: rawStaff.photoUrl, - status: StaffStatus.active, - address: rawStaff.addres, - totalShifts: rawStaff.totalShifts, - averageRating: rawStaff.averageRating, - onTimeRate: rawStaff.onTimeRate, - noShowCount: rawStaff.noShowCount, - cancellationCount: rawStaff.cancellationCount, - reliabilityScore: rawStaff.reliabilityScore, + return domain.Staff( + id: staff.id, + authProviderId: staff.userId, + name: staff.fullName, + email: staff.email ?? '', + phone: staff.phone, + avatar: staff.photoUrl, + status: domain.StaffStatus.active, + address: staff.addres, + totalShifts: staff.totalShifts, + averageRating: staff.averageRating, + onTimeRate: staff.onTimeRate, + noShowCount: staff.noShowCount, + cancellationCount: staff.cancellationCount, + reliabilityScore: staff.reliabilityScore, ); }); } @override - Future> getBenefits() async { + Future> getBenefits() async { return _service.run(() async { final String staffId = await _service.getStaffId(); final QueryResult< - ListBenefitsDataByStaffIdData, - ListBenefitsDataByStaffIdVariables + dc.ListBenefitsDataByStaffIdData, + dc.ListBenefitsDataByStaffIdVariables > response = await _service.connector .listBenefitsDataByStaffId(staffId: staffId) .execute(); - return response.data.benefitsDatas.map((data) { - final plan = data.vendorBenefitPlan; - return Benefit( - title: plan.title, - entitlementHours: plan.total?.toDouble() ?? 0.0, - usedHours: data.current.toDouble(), - ); - }).toList(); + return response.data.benefitsDatas + .map( + (dc.ListBenefitsDataByStaffIdBenefitsDatas e) => domain.Benefit( + title: e.vendorBenefitPlan.title, + entitlementHours: e.vendorBenefitPlan.total?.toDouble() ?? 0, + usedHours: e.current.toDouble(), + ), + ) + .toList(); }); } @override - Future> getAttireOptions() async { + Future> getAttireOptions() async { return _service.run(() async { final String staffId = await _service.getStaffId(); - // Fetch all options - final QueryResult optionsResponse = - await _service.connector.listAttireOptions().execute(); + final List> results = + await Future.wait>( + >>[ + _service.connector.listAttireOptions().execute(), + _service.connector.getStaffAttire(staffId: staffId).execute(), + ], + ); - // Fetch user's attire status - final QueryResult - attiresResponse = await _service.connector - .getStaffAttire(staffId: staffId) - .execute(); + final QueryResult optionsRes = + results[0] as QueryResult; + final QueryResult + staffAttireRes = + results[1] + as QueryResult; - final Map attireMap = { - for (final item in attiresResponse.data.staffAttires) - item.attireOptionId: item, - }; + final List staffAttire = + staffAttireRes.data.staffAttires; - return optionsResponse.data.attireOptions.map((e) { - final GetStaffAttireStaffAttires? userAttire = attireMap[e.id]; - return AttireItem( - id: e.id, - code: e.itemId, - label: e.label, - description: e.description, - imageUrl: e.imageUrl, - isMandatory: e.isMandatory ?? false, - verificationStatus: _mapAttireStatus( - userAttire?.verificationStatus?.stringValue, - ), - photoUrl: userAttire?.verificationPhotoUrl, - verificationId: userAttire?.verificationId, + return optionsRes.data.attireOptions.map(( + dc.ListAttireOptionsAttireOptions opt, + ) { + final dc.GetStaffAttireStaffAttires currentAttire = staffAttire + .firstWhere( + (dc.GetStaffAttireStaffAttires a) => a.attireOptionId == opt.id, + orElse: () => dc.GetStaffAttireStaffAttires( + attireOptionId: opt.id, + verificationPhotoUrl: null, + verificationId: null, + verificationStatus: null, + ), + ); + + return domain.AttireItem( + id: opt.id, + code: opt.itemId, + label: opt.label, + description: opt.description, + imageUrl: opt.imageUrl, + isMandatory: opt.isMandatory ?? false, + photoUrl: currentAttire.verificationPhotoUrl, + verificationId: currentAttire.verificationId, + verificationStatus: currentAttire.verificationStatus != null + ? _mapFromDCStatus(currentAttire.verificationStatus!) + : null, ); }).toList(); }); } - AttireVerificationStatus? _mapAttireStatus(String? status) { - if (status == null) return null; - return AttireVerificationStatus.values.firstWhere( - (e) => e.name.toUpperCase() == status.toUpperCase(), - orElse: () => AttireVerificationStatus.pending, - ); - } - @override Future upsertStaffAttire({ required String attireOptionId, required String photoUrl, String? verificationId, + domain.AttireVerificationStatus? verificationStatus, }) async { await _service.run(() async { final String staffId = await _service.getStaffId(); @@ -266,6 +275,67 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { .upsertStaffAttire(staffId: staffId, attireOptionId: attireOptionId) .verificationPhotoUrl(photoUrl) .verificationId(verificationId) + .verificationStatus( + verificationStatus != null + ? dc.AttireVerificationStatus.values.firstWhere( + (dc.AttireVerificationStatus e) => + e.name == verificationStatus.value.toUpperCase(), + orElse: () => dc.AttireVerificationStatus.PENDING, + ) + : null, + ) + .execute(); + }); + } + + domain.AttireVerificationStatus _mapFromDCStatus( + dc.EnumValue status, + ) { + if (status is dc.Unknown) { + return domain.AttireVerificationStatus.error; + } + final String name = + (status as dc.Known).value.name; + switch (name) { + case 'PENDING': + return domain.AttireVerificationStatus.pending; + case 'PROCESSING': + return domain.AttireVerificationStatus.processing; + case 'AUTO_PASS': + return domain.AttireVerificationStatus.autoPass; + case 'AUTO_FAIL': + return domain.AttireVerificationStatus.autoFail; + case 'NEEDS_REVIEW': + return domain.AttireVerificationStatus.needsReview; + case 'APPROVED': + return domain.AttireVerificationStatus.approved; + case 'REJECTED': + return domain.AttireVerificationStatus.rejected; + case 'ERROR': + return domain.AttireVerificationStatus.error; + default: + return domain.AttireVerificationStatus.error; + } + } + + @override + Future saveStaffProfile({ + String? firstName, + String? lastName, + String? bio, + String? profilePictureUrl, + }) async { + await _service.run(() async { + final String staffId = await _service.getStaffId(); + final String? fullName = (firstName != null || lastName != null) + ? '${firstName ?? ''} ${lastName ?? ''}'.trim() + : null; + + await _service.connector + .updateStaff(id: staffId) + .fullName(fullName) + .bio(bio) + .photoUrl(profilePictureUrl) .execute(); }); } diff --git a/apps/mobile/packages/data_connect/lib/src/connectors/staff/domain/repositories/staff_connector_repository.dart b/apps/mobile/packages/data_connect/lib/src/connectors/staff/domain/repositories/staff_connector_repository.dart index e4cc2db8..3bd3c9e7 100644 --- a/apps/mobile/packages/data_connect/lib/src/connectors/staff/domain/repositories/staff_connector_repository.dart +++ b/apps/mobile/packages/data_connect/lib/src/connectors/staff/domain/repositories/staff_connector_repository.dart @@ -55,6 +55,7 @@ abstract interface class StaffConnectorRepository { required String attireOptionId, required String photoUrl, String? verificationId, + AttireVerificationStatus? verificationStatus, }); /// Signs out the current user. @@ -63,4 +64,12 @@ abstract interface class StaffConnectorRepository { /// /// Throws an exception if the sign-out fails. Future signOut(); + + /// Saves the staff profile information. + Future saveStaffProfile({ + String? firstName, + String? lastName, + String? bio, + String? profilePictureUrl, + }); } diff --git a/apps/mobile/packages/domain/lib/src/entities/profile/attire_verification_status.dart b/apps/mobile/packages/domain/lib/src/entities/profile/attire_verification_status.dart index bc5a3430..f766e8dc 100644 --- a/apps/mobile/packages/domain/lib/src/entities/profile/attire_verification_status.dart +++ b/apps/mobile/packages/domain/lib/src/entities/profile/attire_verification_status.dart @@ -1,11 +1,39 @@ /// Represents the verification status of an attire item photo. enum AttireVerificationStatus { - /// The photo is waiting for review. - pending, + /// Job is created and waiting to be processed. + pending('PENDING'), - /// The photo was rejected. - failed, + /// Job is currently being processed by machine or human. + processing('PROCESSING'), - /// The photo was approved. - success, + /// Machine verification passed automatically. + autoPass('AUTO_PASS'), + + /// Machine verification failed automatically. + autoFail('AUTO_FAIL'), + + /// Machine results are inconclusive and require human review. + needsReview('NEEDS_REVIEW'), + + /// Human reviewer approved the verification. + approved('APPROVED'), + + /// Human reviewer rejected the verification. + rejected('REJECTED'), + + /// An error occurred during processing. + error('ERROR'); + + const AttireVerificationStatus(this.value); + + /// The string value expected by the Core API. + final String value; + + /// Creates a [AttireVerificationStatus] from a string. + static AttireVerificationStatus fromString(String value) { + return AttireVerificationStatus.values.firstWhere( + (AttireVerificationStatus e) => e.value == value, + orElse: () => AttireVerificationStatus.error, + ); + } } diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/data/repositories_impl/attire_repository_impl.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/data/repositories_impl/attire_repository_impl.dart index 9ad0acb2..65645ad8 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/data/repositories_impl/attire_repository_impl.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/data/repositories_impl/attire_repository_impl.dart @@ -1,7 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_core/core.dart'; -import 'package:krow_data_connect/krow_data_connect.dart'; +import 'package:krow_data_connect/krow_data_connect.dart' + hide AttireVerificationStatus; import 'package:krow_domain/krow_domain.dart'; import '../../domain/repositories/attire_repository.dart'; @@ -72,6 +73,7 @@ class AttireRepositoryImpl implements AttireRepository { rules: {'dressCode': dressCode}, ); final String verificationId = verifyRes.verificationId; + VerificationStatus currentStatus = verifyRes.status; // 4. Poll for status until it's finished or timeout (max 10 seconds) try { @@ -81,8 +83,9 @@ class AttireRepositoryImpl implements AttireRepository { await Future.delayed(const Duration(seconds: 2)); final VerificationResponse statusRes = await verificationService .getStatus(verificationId); - final String status = statusRes.status; - if (status != 'PENDING' && status != 'QUEUED') { + currentStatus = statusRes.status; + if (currentStatus != VerificationStatus.pending && + currentStatus != VerificationStatus.processing) { isFinished = true; } attempts++; @@ -97,10 +100,32 @@ class AttireRepositoryImpl implements AttireRepository { attireOptionId: itemId, photoUrl: photoUrl, verificationId: verificationId, + verificationStatus: _mapToAttireStatus(currentStatus), ); // 6. Return updated AttireItem by re-fetching to get the PENDING/SUCCESS status final List finalOptions = await _connector.getAttireOptions(); return finalOptions.firstWhere((AttireItem e) => e.id == itemId); } + + AttireVerificationStatus _mapToAttireStatus(VerificationStatus status) { + switch (status) { + case VerificationStatus.pending: + return AttireVerificationStatus.pending; + case VerificationStatus.processing: + return AttireVerificationStatus.processing; + case VerificationStatus.autoPass: + return AttireVerificationStatus.autoPass; + case VerificationStatus.autoFail: + return AttireVerificationStatus.autoFail; + case VerificationStatus.needsReview: + return AttireVerificationStatus.needsReview; + case VerificationStatus.approved: + return AttireVerificationStatus.approved; + case VerificationStatus.rejected: + return AttireVerificationStatus.rejected; + case VerificationStatus.error: + return AttireVerificationStatus.error; + } + } } diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart index e535b568..1c3adbd8 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart @@ -115,6 +115,22 @@ class _AttireCapturePageState extends State { } } + String _getStatusText(bool hasUploadedPhoto) { + return switch (widget.item.verificationStatus) { + AttireVerificationStatus.approved => 'Approved', + AttireVerificationStatus.rejected => 'Rejected', + _ => hasUploadedPhoto ? 'Pending Verification' : 'Not Uploaded', + }; + } + + Color _getStatusColor(bool hasUploadedPhoto) { + return switch (widget.item.verificationStatus) { + AttireVerificationStatus.approved => UiColors.textSuccess, + AttireVerificationStatus.rejected => UiColors.textError, + _ => hasUploadedPhoto ? UiColors.textWarning : UiColors.textInactive, + }; + } + @override Widget build(BuildContext context) { return BlocProvider( @@ -145,26 +161,9 @@ class _AttireCapturePageState extends State { state.photoUrl ?? widget.initialPhotoUrl; final bool hasUploadedPhoto = currentPhotoUrl != null; - final String statusText = switch (widget - .item - .verificationStatus) { - AttireVerificationStatus.success => 'Approved', - AttireVerificationStatus.failed => 'Rejected', - AttireVerificationStatus.pending => 'Pending Verification', - _ => - hasUploadedPhoto ? 'Pending Verification' : 'Not Uploaded', - }; + final String statusText = _getStatusText(hasUploadedPhoto); - final Color statusColor = - switch (widget.item.verificationStatus) { - AttireVerificationStatus.success => UiColors.textSuccess, - AttireVerificationStatus.failed => UiColors.textError, - AttireVerificationStatus.pending => UiColors.textWarning, - _ => - hasUploadedPhoto - ? UiColors.textWarning - : UiColors.textInactive, - }; + final Color statusColor = _getStatusColor(hasUploadedPhoto); return Column( children: [ @@ -196,7 +195,7 @@ class _AttireCapturePageState extends State { widget.item.imageUrl ?? '', height: 120, fit: BoxFit.cover, - errorBuilder: (_, __, ___) => + errorBuilder: (_, _, _) => const SizedBox.shrink(), ), ), @@ -223,7 +222,7 @@ class _AttireCapturePageState extends State { widget.item.imageUrl ?? '', height: 120, fit: BoxFit.cover, - errorBuilder: (_, __, ___) => + errorBuilder: (_, _, _) => const SizedBox.shrink(), ), ), diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_item_card.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_item_card.dart index abeab814..f0941d96 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_item_card.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_item_card.dart @@ -3,11 +3,6 @@ import 'package:flutter/material.dart'; import 'package:krow_domain/krow_domain.dart'; class AttireItemCard extends StatelessWidget { - final AttireItem item; - final String? uploadedPhotoUrl; - final bool isUploading; - final VoidCallback onTap; - const AttireItemCard({ super.key, required this.item, @@ -16,12 +11,17 @@ class AttireItemCard extends StatelessWidget { required this.onTap, }); + final AttireItem item; + final String? uploadedPhotoUrl; + final bool isUploading; + final VoidCallback onTap; + @override Widget build(BuildContext context) { final bool hasPhoto = item.photoUrl != null; final String statusText = switch (item.verificationStatus) { - AttireVerificationStatus.success => 'Approved', - AttireVerificationStatus.failed => 'Rejected', + AttireVerificationStatus.approved => 'Approved', + AttireVerificationStatus.rejected => 'Rejected', AttireVerificationStatus.pending => 'Pending', _ => hasPhoto ? 'Pending' : 'To Do', }; @@ -91,7 +91,7 @@ class AttireItemCard extends StatelessWidget { size: UiChipSize.xSmall, variant: item.verificationStatus == - AttireVerificationStatus.success + AttireVerificationStatus.approved ? UiChipVariant.primary : UiChipVariant.secondary, ), @@ -114,12 +114,12 @@ class AttireItemCard extends StatelessWidget { ) else if (hasPhoto && !isUploading) Icon( - item.verificationStatus == AttireVerificationStatus.success + item.verificationStatus == AttireVerificationStatus.approved ? UiIcons.check : UiIcons.clock, color: item.verificationStatus == - AttireVerificationStatus.success + AttireVerificationStatus.approved ? UiColors.textPrimary : UiColors.textWarning, size: 24, diff --git a/backend/dataconnect/connector/staffAttire/mutations.gql b/backend/dataconnect/connector/staffAttire/mutations.gql index 25184389..72fa489b 100644 --- a/backend/dataconnect/connector/staffAttire/mutations.gql +++ b/backend/dataconnect/connector/staffAttire/mutations.gql @@ -3,6 +3,7 @@ mutation upsertStaffAttire( $attireOptionId: UUID! $verificationPhotoUrl: String $verificationId: String + $verificationStatus: AttireVerificationStatus ) @auth(level: USER) { staffAttire_upsert( data: { @@ -10,7 +11,7 @@ mutation upsertStaffAttire( attireOptionId: $attireOptionId verificationPhotoUrl: $verificationPhotoUrl verificationId: $verificationId - verificationStatus: PENDING + verificationStatus: $verificationStatus } ) } diff --git a/backend/dataconnect/schema/staffAttire.gql b/backend/dataconnect/schema/staffAttire.gql index e61e8f9b..c3f0e213 100644 --- a/backend/dataconnect/schema/staffAttire.gql +++ b/backend/dataconnect/schema/staffAttire.gql @@ -1,7 +1,12 @@ enum AttireVerificationStatus { PENDING - FAILED - SUCCESS + PROCESSING + AUTO_PASS + AUTO_FAIL + NEEDS_REVIEW + APPROVED + REJECTED + ERROR } type StaffAttire @table(name: "staff_attires", key: ["staffId", "attireOptionId"]) {