feat: Introduce AttireVerificationStatus enum and add verificationId to staff attire items.

This commit is contained in:
Achintha Isuru
2026-02-24 17:31:41 -05:00
parent 616f23fec9
commit fd0208efa0
10 changed files with 64 additions and 14 deletions

View File

@@ -1,6 +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 // 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:firebase_data_connect/firebase_data_connect.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 'package:krow_domain/krow_domain.dart';
/// Implementation of [StaffConnectorRepository]. /// Implementation of [StaffConnectorRepository].
@@ -233,17 +234,28 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
description: e.description, description: e.description,
imageUrl: e.imageUrl, imageUrl: e.imageUrl,
isMandatory: e.isMandatory ?? false, isMandatory: e.isMandatory ?? false,
verificationStatus: userAttire?.verificationStatus?.stringValue, verificationStatus: _mapAttireStatus(
userAttire?.verificationStatus?.stringValue,
),
photoUrl: userAttire?.verificationPhotoUrl, photoUrl: userAttire?.verificationPhotoUrl,
); );
}).toList(); }).toList();
}); });
} }
AttireVerificationStatus? _mapAttireStatus(String? status) {
if (status == null) return null;
return AttireVerificationStatus.values.firstWhere(
(e) => e.name.toUpperCase() == status.toUpperCase(),
orElse: () => AttireVerificationStatus.pending,
);
}
@override @override
Future<void> upsertStaffAttire({ Future<void> upsertStaffAttire({
required String attireOptionId, required String attireOptionId,
required String photoUrl, required String photoUrl,
String? verificationId,
}) async { }) async {
await _service.run(() async { await _service.run(() async {
final String staffId = await _service.getStaffId(); final String staffId = await _service.getStaffId();
@@ -251,6 +263,7 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
await _service.connector await _service.connector
.upsertStaffAttire(staffId: staffId, attireOptionId: attireOptionId) .upsertStaffAttire(staffId: staffId, attireOptionId: attireOptionId)
.verificationPhotoUrl(photoUrl) .verificationPhotoUrl(photoUrl)
// .verificationId(verificationId) // Uncomment after SDK regeneration
.execute(); .execute();
}); });
} }

View File

@@ -54,6 +54,7 @@ abstract interface class StaffConnectorRepository {
Future<void> upsertStaffAttire({ Future<void> upsertStaffAttire({
required String attireOptionId, required String attireOptionId,
required String photoUrl, required String photoUrl,
String? verificationId,
}); });
/// Signs out the current user. /// Signs out the current user.

View File

@@ -68,6 +68,7 @@ export 'src/adapters/financial/bank_account/bank_account_adapter.dart';
// Profile // Profile
export 'src/entities/profile/staff_document.dart'; export 'src/entities/profile/staff_document.dart';
export 'src/entities/profile/attire_item.dart'; export 'src/entities/profile/attire_item.dart';
export 'src/entities/profile/attire_verification_status.dart';
export 'src/entities/profile/relationship_type.dart'; export 'src/entities/profile/relationship_type.dart';
export 'src/entities/profile/industry.dart'; export 'src/entities/profile/industry.dart';
export 'src/entities/profile/tax_form.dart'; export 'src/entities/profile/tax_form.dart';

View File

@@ -1,5 +1,7 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'attire_verification_status.dart';
/// Represents an attire item that a staff member might need or possess. /// Represents an attire item that a staff member might need or possess.
/// ///
/// Attire items are specific clothing or equipment required for jobs. /// Attire items are specific clothing or equipment required for jobs.
@@ -13,6 +15,7 @@ class AttireItem extends Equatable {
this.isMandatory = false, this.isMandatory = false,
this.verificationStatus, this.verificationStatus,
this.photoUrl, this.photoUrl,
this.verificationId,
}); });
/// Unique identifier of the attire item. /// Unique identifier of the attire item.
@@ -31,11 +34,14 @@ class AttireItem extends Equatable {
final bool isMandatory; final bool isMandatory;
/// The current verification status of the uploaded photo. /// The current verification status of the uploaded photo.
final String? verificationStatus; final AttireVerificationStatus? verificationStatus;
/// The URL of the photo uploaded by the staff member. /// The URL of the photo uploaded by the staff member.
final String? photoUrl; final String? photoUrl;
/// The ID of the verification record.
final String? verificationId;
@override @override
List<Object?> get props => <Object?>[ List<Object?> get props => <Object?>[
id, id,
@@ -45,5 +51,6 @@ class AttireItem extends Equatable {
isMandatory, isMandatory,
verificationStatus, verificationStatus,
photoUrl, photoUrl,
verificationId,
]; ];
} }

View File

@@ -0,0 +1,11 @@
/// Represents the verification status of an attire item photo.
enum AttireVerificationStatus {
/// The photo is waiting for review.
pending,
/// The photo was rejected.
failed,
/// The photo was approved.
success,
}

View File

@@ -74,18 +74,26 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
state.photoUrl ?? widget.initialPhotoUrl; state.photoUrl ?? widget.initialPhotoUrl;
final bool hasUploadedPhoto = currentPhotoUrl != null; final bool hasUploadedPhoto = currentPhotoUrl != null;
final String statusText = final String statusText = switch (widget
widget.item.verificationStatus ?? .item
(hasUploadedPhoto .verificationStatus) {
? 'Pending Verification' AttireVerificationStatus.success => 'Approved',
: 'Not Uploaded'); AttireVerificationStatus.failed => 'Rejected',
AttireVerificationStatus.pending => 'Pending Verification',
_ =>
hasUploadedPhoto ? 'Pending Verification' : 'Not Uploaded',
};
final Color statusColor = final Color statusColor =
widget.item.verificationStatus == 'SUCCESS' switch (widget.item.verificationStatus) {
? UiColors.textPrimary AttireVerificationStatus.success => UiColors.textSuccess,
: (hasUploadedPhoto AttireVerificationStatus.failed => UiColors.textError,
AttireVerificationStatus.pending => UiColors.textWarning,
_ =>
hasUploadedPhoto
? UiColors.textWarning ? UiColors.textWarning
: UiColors.textInactive); : UiColors.textInactive,
};
return Column( return Column(
children: <Widget>[ children: <Widget>[

View File

@@ -19,7 +19,12 @@ class AttireItemCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool hasPhoto = item.photoUrl != null; final bool hasPhoto = item.photoUrl != null;
final String statusText = item.verificationStatus ?? 'Not Uploaded'; final String statusText = switch (item.verificationStatus) {
AttireVerificationStatus.success => 'Approved',
AttireVerificationStatus.failed => 'Rejected',
AttireVerificationStatus.pending => 'Pending',
_ => hasPhoto ? 'Pending' : 'To Do',
};
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,

View File

@@ -2,12 +2,14 @@ mutation upsertStaffAttire(
$staffId: UUID! $staffId: UUID!
$attireOptionId: UUID! $attireOptionId: UUID!
$verificationPhotoUrl: String $verificationPhotoUrl: String
$verificationId: String
) @auth(level: USER) { ) @auth(level: USER) {
staffAttire_upsert( staffAttire_upsert(
data: { data: {
staffId: $staffId staffId: $staffId
attireOptionId: $attireOptionId attireOptionId: $attireOptionId
verificationPhotoUrl: $verificationPhotoUrl verificationPhotoUrl: $verificationPhotoUrl
verificationId: $verificationId
verificationStatus: PENDING verificationStatus: PENDING
} }
) )

View File

@@ -3,5 +3,6 @@ query getStaffAttire($staffId: UUID!) @auth(level: USER) {
attireOptionId attireOptionId
verificationStatus verificationStatus
verificationPhotoUrl verificationPhotoUrl
verificationId
} }
} }

View File

@@ -15,6 +15,7 @@ type StaffAttire @table(name: "staff_attires", key: ["staffId", "attireOptionId"
verificationStatus: AttireVerificationStatus @default(expr: "'PENDING'") verificationStatus: AttireVerificationStatus @default(expr: "'PENDING'")
verifiedAt: Timestamp verifiedAt: Timestamp
verificationPhotoUrl: String # Proof of ownership verificationPhotoUrl: String # Proof of ownership
verificationId: String
createdAt: Timestamp @default(expr: "request.time") createdAt: Timestamp @default(expr: "request.time")
updatedAt: Timestamp @default(expr: "request.time") updatedAt: Timestamp @default(expr: "request.time")