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 38051187..b8ab50c9 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 @@ -11,9 +11,8 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { /// Creates a new [StaffConnectorRepositoryImpl]. /// /// Requires a [DataConnectService] instance for backend communication. - StaffConnectorRepositoryImpl({ - DataConnectService? service, - }) : _service = service ?? DataConnectService.instance; + StaffConnectorRepositoryImpl({DataConnectService? service}) + : _service = service ?? DataConnectService.instance; final DataConnectService _service; @@ -22,15 +21,17 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { return _service.run(() async { final String staffId = await _service.getStaffId(); - final QueryResult response = - await _service.connector - .getStaffProfileCompletion(id: staffId) - .execute(); + final QueryResult< + GetStaffProfileCompletionData, + GetStaffProfileCompletionVariables + > + response = await _service.connector + .getStaffProfileCompletion(id: staffId) + .execute(); final GetStaffProfileCompletionStaff? staff = response.data.staff; - final List - emergencyContacts = response.data.emergencyContacts; + final List emergencyContacts = + response.data.emergencyContacts; final List taxForms = response.data.taxForms; @@ -43,11 +44,13 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { return _service.run(() async { final String staffId = await _service.getStaffId(); - final QueryResult response = - await _service.connector - .getStaffPersonalInfoCompletion(id: staffId) - .execute(); + final QueryResult< + GetStaffPersonalInfoCompletionData, + GetStaffPersonalInfoCompletionVariables + > + response = await _service.connector + .getStaffPersonalInfoCompletion(id: staffId) + .execute(); final GetStaffPersonalInfoCompletionStaff? staff = response.data.staff; @@ -60,11 +63,13 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { return _service.run(() async { final String staffId = await _service.getStaffId(); - final QueryResult response = - await _service.connector - .getStaffEmergencyProfileCompletion(id: staffId) - .execute(); + final QueryResult< + GetStaffEmergencyProfileCompletionData, + GetStaffEmergencyProfileCompletionVariables + > + response = await _service.connector + .getStaffEmergencyProfileCompletion(id: staffId) + .execute(); return response.data.emergencyContacts.isNotEmpty; }); @@ -75,11 +80,13 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { return _service.run(() async { final String staffId = await _service.getStaffId(); - final QueryResult response = - await _service.connector - .getStaffExperienceProfileCompletion(id: staffId) - .execute(); + final QueryResult< + GetStaffExperienceProfileCompletionData, + GetStaffExperienceProfileCompletionVariables + > + response = await _service.connector + .getStaffExperienceProfileCompletion(id: staffId) + .execute(); final GetStaffExperienceProfileCompletionStaff? staff = response.data.staff; @@ -93,11 +100,13 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { return _service.run(() async { final String staffId = await _service.getStaffId(); - final QueryResult response = - await _service.connector - .getStaffTaxFormsProfileCompletion(id: staffId) - .execute(); + final QueryResult< + GetStaffTaxFormsProfileCompletionData, + GetStaffTaxFormsProfileCompletionVariables + > + response = await _service.connector + .getStaffTaxFormsProfileCompletion(id: staffId) + .execute(); return response.data.taxForms.isNotEmpty; }); @@ -135,9 +144,7 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { final bool hasExperience = (skills is List && skills.isNotEmpty) || (industries is List && industries.isNotEmpty); - return emergencyContacts.isNotEmpty && - taxForms.isNotEmpty && - hasExperience; + return emergencyContacts.isNotEmpty && taxForms.isNotEmpty && hasExperience; } @override @@ -146,14 +153,10 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { final String staffId = await _service.getStaffId(); final QueryResult response = - await _service.connector - .getStaffById(id: staffId) - .execute(); + await _service.connector.getStaffById(id: staffId).execute(); if (response.data.staff == null) { - throw const ServerException( - technicalMessage: 'Staff not found', - ); + throw const ServerException(technicalMessage: 'Staff not found'); } final GetStaffByIdStaff rawStaff = response.data.staff!; @@ -183,11 +186,13 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { return _service.run(() async { final String staffId = await _service.getStaffId(); - final QueryResult response = - await _service.connector - .listBenefitsDataByStaffId(staffId: staffId) - .execute(); + final QueryResult< + ListBenefitsDataByStaffIdData, + ListBenefitsDataByStaffIdVariables + > + response = await _service.connector + .listBenefitsDataByStaffId(staffId: staffId) + .execute(); return response.data.benefitsDatas.map((data) { final plan = data.vendorBenefitPlan; @@ -200,6 +205,56 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { }); } + @override + Future> getAttireOptions() async { + return _service.run(() async { + final String staffId = await _service.getStaffId(); + + // Fetch all options + final QueryResult optionsResponse = + await _service.connector.listAttireOptions().execute(); + + // Fetch user's attire status + final QueryResult + attiresResponse = await _service.connector + .getStaffAttire(staffId: staffId) + .execute(); + + final Map attireMap = { + for (final item in attiresResponse.data.staffAttires) + item.attireOptionId: item, + }; + + return optionsResponse.data.attireOptions.map((e) { + final GetStaffAttireStaffAttires? userAttire = attireMap[e.id]; + return AttireItem( + id: e.itemId, + label: e.label, + description: e.description, + imageUrl: e.imageUrl, + isMandatory: e.isMandatory ?? false, + verificationStatus: userAttire?.verificationStatus?.stringValue, + photoUrl: userAttire?.verificationPhotoUrl, + ); + }).toList(); + }); + } + + @override + Future upsertStaffAttire({ + required String attireOptionId, + required String photoUrl, + }) async { + await _service.run(() async { + final String staffId = await _service.getStaffId(); + + await _service.connector + .upsertStaffAttire(staffId: staffId, attireOptionId: attireOptionId) + .verificationPhotoUrl(photoUrl) + .execute(); + }); + } + @override Future signOut() async { try { @@ -210,4 +265,3 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository { } } } - 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 e82e69f3..b674b6f1 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 @@ -45,6 +45,17 @@ abstract interface class StaffConnectorRepository { /// Returns a list of [Benefit] entities. Future> getBenefits(); + /// Fetches the attire options for the current authenticated user. + /// + /// Returns a list of [AttireItem] entities. + Future> getAttireOptions(); + + /// Upserts staff attire photo information. + Future upsertStaffAttire({ + required String attireOptionId, + required String photoUrl, + }); + /// Signs out the current user. /// /// Clears the user's session and authentication state. diff --git a/apps/mobile/packages/domain/lib/src/entities/profile/attire_item.dart b/apps/mobile/packages/domain/lib/src/entities/profile/attire_item.dart index adcb0874..40d90b32 100644 --- a/apps/mobile/packages/domain/lib/src/entities/profile/attire_item.dart +++ b/apps/mobile/packages/domain/lib/src/entities/profile/attire_item.dart @@ -11,6 +11,8 @@ class AttireItem extends Equatable { this.description, this.imageUrl, this.isMandatory = false, + this.verificationStatus, + this.photoUrl, }); /// Unique identifier of the attire item. @@ -28,6 +30,12 @@ class AttireItem extends Equatable { /// Whether this item is mandatory for onboarding. final bool isMandatory; + /// The current verification status of the uploaded photo. + final String? verificationStatus; + + /// The URL of the photo uploaded by the staff member. + final String? photoUrl; + @override List get props => [ id, @@ -35,5 +43,7 @@ class AttireItem extends Equatable { description, imageUrl, isMandatory, + verificationStatus, + photoUrl, ]; } 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 3cdd0d94..727c8f77 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,4 +1,3 @@ -import 'package:firebase_data_connect/firebase_data_connect.dart'; import 'package:krow_data_connect/krow_data_connect.dart'; import 'package:krow_domain/krow_domain.dart'; @@ -6,34 +5,19 @@ import '../../domain/repositories/attire_repository.dart'; /// Implementation of [AttireRepository]. /// -/// Delegates data access to [DataConnectService]. +/// Delegates data access to [StaffConnectorRepository]. class AttireRepositoryImpl implements AttireRepository { /// Creates an [AttireRepositoryImpl]. - AttireRepositoryImpl({DataConnectService? service}) - : _service = service ?? DataConnectService.instance; + AttireRepositoryImpl({StaffConnectorRepository? connector}) + : _connector = + connector ?? DataConnectService.instance.getStaffRepository(); - /// The Data Connect service. - final DataConnectService _service; + /// The Staff Connector repository. + final StaffConnectorRepository _connector; @override Future> getAttireOptions() async { - return _service.run(() async { - final QueryResult result = await _service - .connector - .listAttireOptions() - .execute(); - return result.data.attireOptions - .map( - (ListAttireOptionsAttireOptions e) => AttireItem( - id: e.itemId, - label: e.label, - description: e.description, - imageUrl: e.imageUrl, - isMandatory: e.isMandatory ?? false, - ), - ) - .toList(); - }); + return _connector.getAttireOptions(); } @override @@ -41,16 +25,22 @@ class AttireRepositoryImpl implements AttireRepository { required List selectedItemIds, required Map photoUrls, }) async { - // TODO: Connect to actual backend mutation when available. - // For now, simulate network delay as per prototype behavior. - await Future.delayed(const Duration(seconds: 1)); + // We already upsert photos in uploadPhoto (to follow the new flow). + // This could save selections if there was a separate "SelectedAttire" table. + // For now, it's a no-op as the source of truth is the StaffAttire table. } @override Future uploadPhoto(String itemId) async { - // TODO: Connect to actual storage service/mutation when available. - // For now, simulate upload delay and return mock URL. - await Future.delayed(const Duration(seconds: 1)); - return 'mock_url_for_$itemId'; + // In a real app, this would upload to Firebase Storage first. + // Since the prototype returns a mock URL, we'll use that to upsert our record. + final String mockUrl = 'mock_url_for_$itemId'; + + await _connector.upsertStaffAttire( + attireOptionId: itemId, + photoUrl: mockUrl, + ); + + return mockUrl; } } diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart index f8b6df22..ce9862d5 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart @@ -23,18 +23,17 @@ class AttireCubit extends Cubit action: () async { final List options = await _getAttireOptionsUseCase(); - // Auto-select mandatory items initially as per prototype - final List mandatoryIds = options - .where((AttireItem e) => e.isMandatory) - .map((AttireItem e) => e.id) - .toList(); + // Extract photo URLs and selection status from backend data + final Map photoUrls = {}; + final List selectedIds = []; - final List initialSelection = List.from( - state.selectedIds, - ); - for (final String id in mandatoryIds) { - if (!initialSelection.contains(id)) { - initialSelection.add(id); + for (final AttireItem item in options) { + if (item.photoUrl != null) { + photoUrls[item.id] = item.photoUrl!; + } + // If mandatory or has photo, consider it selected initially + if (item.isMandatory || item.photoUrl != null) { + selectedIds.add(item.id); } } @@ -42,7 +41,8 @@ class AttireCubit extends Cubit state.copyWith( status: AttireStatus.success, options: options, - selectedIds: initialSelection, + selectedIds: selectedIds, + photoUrls: photoUrls, ), ); }, @@ -65,20 +65,8 @@ class AttireCubit extends Cubit } void syncCapturedPhoto(String itemId, String url) { - final Map currentPhotos = Map.from( - state.photoUrls, - ); - currentPhotos[itemId] = url; - - // Auto-select item on upload success if not selected - final List currentSelection = List.from(state.selectedIds); - if (!currentSelection.contains(itemId)) { - currentSelection.add(itemId); - } - - emit( - state.copyWith(photoUrls: currentPhotos, selectedIds: currentSelection), - ); + // When a photo is captured, we refresh the options to get the updated status from backend + loadOptions(); } Future save() async { 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 9e420db7..acc0f983 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 @@ -70,14 +70,22 @@ class _AttireCapturePageState extends State { builder: (BuildContext context, AttireCaptureState state) { final bool isUploading = state.status == AttireCaptureStatus.uploading; - final bool hasPhoto = - state.photoUrl != null || widget.initialPhotoUrl != null; - final String statusText = hasPhoto - ? 'Pending Verification' - : 'Not Uploaded'; - final Color statusColor = hasPhoto - ? UiColors.textWarning - : UiColors.textInactive; + final String? currentPhotoUrl = + state.photoUrl ?? widget.initialPhotoUrl; + final bool hasUploadedPhoto = currentPhotoUrl != null; + + final String statusText = + widget.item.verificationStatus ?? + (hasUploadedPhoto + ? 'Pending Verification' + : 'Not Uploaded'); + + final Color statusColor = + widget.item.verificationStatus == 'SUCCESS' + ? UiColors.textPrimary + : (hasUploadedPhoto + ? UiColors.textWarning + : UiColors.textInactive); return Column( children: [ @@ -86,21 +94,54 @@ class _AttireCapturePageState extends State { padding: const EdgeInsets.all(UiConstants.space5), child: Column( children: [ - // Image Preview - AttireImagePreview(imageUrl: widget.item.imageUrl), - const SizedBox(height: UiConstants.space6), + // Image Preview (Toggle between example and uploaded) + if (hasUploadedPhoto) ...[ + Text( + 'Your Uploaded Photo', + style: UiTypography.body1b.textPrimary, + ), + const SizedBox(height: UiConstants.space2), + AttireImagePreview(imageUrl: currentPhotoUrl), + const SizedBox(height: UiConstants.space4), + Text( + 'Reference Example', + style: UiTypography.body2b.textSecondary, + ), + const SizedBox(height: UiConstants.space1), + Center( + child: ClipRRect( + borderRadius: BorderRadius.circular( + UiConstants.radiusBase, + ), + child: Image.network( + widget.item.imageUrl ?? '', + height: 120, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => + const SizedBox.shrink(), + ), + ), + ), + ] else ...[ + AttireImagePreview( + imageUrl: widget.item.imageUrl, + ), + const SizedBox(height: UiConstants.space4), + Text( + 'Example of the item that you need to upload.', + style: UiTypography.body1b.textSecondary, + textAlign: TextAlign.center, + ), + ], - Text( - 'Example of the item that you need to upload.', - style: UiTypography.body1b.textSecondary, - textAlign: TextAlign.center, - ), - Text( - widget.item.description ?? '', - style: UiTypography.body1r.textSecondary, - textAlign: TextAlign.center, - ), const SizedBox(height: UiConstants.space6), + if (widget.item.description != null) + Text( + widget.item.description!, + style: UiTypography.body1r.textSecondary, + textAlign: TextAlign.center, + ), + const SizedBox(height: UiConstants.space8), // Verification info AttireVerificationStatusCard( @@ -118,15 +159,19 @@ class _AttireCapturePageState extends State { const SizedBox(height: UiConstants.space6), if (isUploading) - const Center(child: CircularProgressIndicator()) - else if (!hasPhoto || - true) // Show options even if has photo (allows re-upload) + const Center( + child: Padding( + padding: EdgeInsets.all(UiConstants.space8), + child: CircularProgressIndicator(), + ), + ) + else AttireUploadButtons(onUpload: _onUpload), ], ), ), ), - if (hasPhoto) + if (hasUploadedPhoto) SafeArea( child: Padding( padding: const EdgeInsets.all(UiConstants.space5), @@ -135,7 +180,7 @@ class _AttireCapturePageState extends State { child: UiButton.primary( text: 'Submit Image', onPressed: () { - Modular.to.pop(state.photoUrl); + Modular.to.pop(currentPhotoUrl); }, ), ), diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart index 9f3d62c8..c2782981 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart @@ -80,36 +80,59 @@ class _AttirePageState extends State { const SizedBox(height: UiConstants.space6), // Item List - ...filteredOptions.map((AttireItem item) { - return Padding( - padding: const EdgeInsets.only( - bottom: UiConstants.space3, + if (filteredOptions.isEmpty) + Padding( + padding: const EdgeInsets.symmetric( + vertical: UiConstants.space10, ), - child: AttireItemCard( - item: item, - isUploading: false, - uploadedPhotoUrl: state.photoUrls[item.id], - onTap: () async { - final String? resultUrl = - await Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext ctx) => - AttireCapturePage( - item: item, - initialPhotoUrl: - state.photoUrls[item.id], - ), - ), - ); + child: Center( + child: Column( + children: [ + const Icon( + UiIcons.shirt, + size: 48, + color: UiColors.iconInactive, + ), + const SizedBox(height: UiConstants.space4), + Text( + 'No items found for this filter.', + style: UiTypography.body1m.textSecondary, + ), + ], + ), + ), + ) + else + ...filteredOptions.map((AttireItem item) { + return Padding( + padding: const EdgeInsets.only( + bottom: UiConstants.space3, + ), + child: AttireItemCard( + item: item, + isUploading: false, + uploadedPhotoUrl: state.photoUrls[item.id], + onTap: () async { + final String? resultUrl = + await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext ctx) => + AttireCapturePage( + item: item, + initialPhotoUrl: + state.photoUrls[item.id], + ), + ), + ); - if (resultUrl != null && mounted) { - cubit.syncCapturedPhoto(item.id, resultUrl); - } - }, - ), - ); - }), + if (resultUrl != null && mounted) { + cubit.syncCapturedPhoto(item.id, resultUrl); + } + }, + ), + ); + }), const SizedBox(height: UiConstants.space20), ], ), 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 005fe6a2..3b122a39 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 @@ -18,9 +18,8 @@ class AttireItemCard extends StatelessWidget { @override Widget build(BuildContext context) { - final bool hasPhoto = uploadedPhotoUrl != null; - - final String statusText = hasPhoto ? 'Pending' : 'Not Uploaded'; + final bool hasPhoto = item.photoUrl != null; + final String statusText = item.verificationStatus ?? 'Not Uploaded'; return GestureDetector( onTap: onTap, @@ -85,7 +84,9 @@ class AttireItemCard extends StatelessWidget { UiChip( label: statusText, size: UiChipSize.xSmall, - variant: UiChipVariant.secondary, + variant: item.verificationStatus == 'SUCCESS' + ? UiChipVariant.primary + : UiChipVariant.secondary, ), ], ), @@ -105,9 +106,13 @@ class AttireItemCard extends StatelessWidget { size: 24, ) else if (hasPhoto && !isUploading) - const Icon( - UiIcons.check, - color: UiColors.textWarning, + Icon( + item.verificationStatus == 'SUCCESS' + ? UiIcons.check + : UiIcons.clock, + color: item.verificationStatus == 'SUCCESS' + ? UiColors.textPrimary + : UiColors.textWarning, size: 24, ), ], diff --git a/backend/dataconnect/connector/staffAttire/mutations.gql b/backend/dataconnect/connector/staffAttire/mutations.gql new file mode 100644 index 00000000..54628d89 --- /dev/null +++ b/backend/dataconnect/connector/staffAttire/mutations.gql @@ -0,0 +1,14 @@ +mutation upsertStaffAttire( + $staffId: UUID! + $attireOptionId: UUID! + $verificationPhotoUrl: String +) @auth(level: USER) { + staffAttire_upsert( + data: { + staffId: $staffId + attireOptionId: $attireOptionId + verificationPhotoUrl: $verificationPhotoUrl + verificationStatus: PENDING + } + ) +} diff --git a/backend/dataconnect/connector/staffAttire/queries.gql b/backend/dataconnect/connector/staffAttire/queries.gql new file mode 100644 index 00000000..6a6d8822 --- /dev/null +++ b/backend/dataconnect/connector/staffAttire/queries.gql @@ -0,0 +1,7 @@ +query getStaffAttire($staffId: UUID!) @auth(level: USER) { + staffAttires(where: { staffId: { eq: $staffId } }) { + attireOptionId + verificationStatus + verificationPhotoUrl + } +} diff --git a/backend/dataconnect/functions/seed.gql b/backend/dataconnect/functions/seed.gql index 2293f4b9..065a8246 100644 --- a/backend/dataconnect/functions/seed.gql +++ b/backend/dataconnect/functions/seed.gql @@ -1771,7 +1771,6 @@ mutation seedAll @transaction { } ) - mutation seedAttireOptions @transaction { # Attire Options (Required) attire_1: attireOption_insert( data: { @@ -1930,5 +1929,4 @@ mutation seedAll @transaction { } ) } -} #v.3 \ No newline at end of file diff --git a/backend/dataconnect/schema/staffAttire.gql b/backend/dataconnect/schema/staffAttire.gql index 0f43b460..d1f94ebf 100644 --- a/backend/dataconnect/schema/staffAttire.gql +++ b/backend/dataconnect/schema/staffAttire.gql @@ -12,7 +12,7 @@ type StaffAttire @table(name: "staff_attires", key: ["staffId", "attireOptionId" attireOption: AttireOption! @ref(fields: "attireOptionId", references: "id") # Verification Metadata - verificationStatus: AttireVerificationStatus @default(expr: "PENDING") + verificationStatus: AttireVerificationStatus @default(expr: "'PENDING'") verifiedAt: Timestamp verificationPhotoUrl: String # Proof of ownership