Persist verificationId for staff certificates
Add support for verificationId throughout the certificate flow: schema, GraphQL mutations/queries, domain, repositories, service implementation, and UI. - Backend: add verificationId to Certificate schema and include it in upsert/create mutations; add auth insecureReason notes to related connector operations. - Data layer: add verificationId parameter to StaffConnectorRepository API and propagation in implementation (SDK call remains commented with FIXME until dataconnect SDK is regenerated). - Domain: add verificationId field to StaffCertificate (constructor, copyWith, props). - Certificates flow: create verification via verificationService, pass returned verificationId to upsertStaffCertificate so the verification record is persisted with the certificate. - UI: update certificate upload page to show existing file path, disable editing of name/issuer/number, rearrange fields, move remove button, change file icon and text style. - Misc: minor lambda formatting cleanup in benefits mapping. Note: the generated dataconnect SDK must be refreshed to enable the new .verificationId(...) call (there is a commented FIXME in the connector implementation).
This commit is contained in:
@@ -196,20 +196,17 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
|
|||||||
.listBenefitsDataByStaffId(staffId: staffId)
|
.listBenefitsDataByStaffId(staffId: staffId)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
return response.data.benefitsDatas
|
return response.data.benefitsDatas.map((
|
||||||
.map(
|
dc.ListBenefitsDataByStaffIdBenefitsDatas e,
|
||||||
(dc.ListBenefitsDataByStaffIdBenefitsDatas e) {
|
) {
|
||||||
final total =
|
final total = e.vendorBenefitPlan.total?.toDouble() ?? 0.0;
|
||||||
e.vendorBenefitPlan.total?.toDouble() ?? 0.0;
|
|
||||||
final remaining = e.current.toDouble();
|
final remaining = e.current.toDouble();
|
||||||
return domain.Benefit(
|
return domain.Benefit(
|
||||||
title: e.vendorBenefitPlan.title,
|
title: e.vendorBenefitPlan.title,
|
||||||
entitlementHours: total,
|
entitlementHours: total,
|
||||||
usedHours: (total - remaining).clamp(0.0, total),
|
usedHours: (total - remaining).clamp(0.0, total),
|
||||||
);
|
);
|
||||||
},
|
}).toList();
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,6 +571,7 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
|
|||||||
String? issuer,
|
String? issuer,
|
||||||
String? certificateNumber,
|
String? certificateNumber,
|
||||||
domain.StaffCertificateValidationStatus? validationStatus,
|
domain.StaffCertificateValidationStatus? validationStatus,
|
||||||
|
String? verificationId,
|
||||||
}) async {
|
}) async {
|
||||||
await _service.run(() async {
|
await _service.run(() async {
|
||||||
final String staffId = await _service.getStaffId();
|
final String staffId = await _service.getStaffId();
|
||||||
@@ -590,6 +588,7 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
|
|||||||
.issuer(issuer)
|
.issuer(issuer)
|
||||||
.certificateNumber(certificateNumber)
|
.certificateNumber(certificateNumber)
|
||||||
.validationStatus(_mapToDCValidationStatus(validationStatus))
|
.validationStatus(_mapToDCValidationStatus(validationStatus))
|
||||||
|
// .verificationId(verificationId) // FIXME: Uncomment after running 'make dataconnect-generate-sdk'
|
||||||
.execute();
|
.execute();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ abstract interface class StaffConnectorRepository {
|
|||||||
String? issuer,
|
String? issuer,
|
||||||
String? certificateNumber,
|
String? certificateNumber,
|
||||||
StaffCertificateValidationStatus? validationStatus,
|
StaffCertificateValidationStatus? validationStatus,
|
||||||
|
String? verificationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Deletes a staff certificate.
|
/// Deletes a staff certificate.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class StaffCertificate extends Equatable {
|
|||||||
this.issuer,
|
this.issuer,
|
||||||
this.certificateNumber,
|
this.certificateNumber,
|
||||||
this.validationStatus,
|
this.validationStatus,
|
||||||
|
this.verificationId,
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
this.updatedAt,
|
this.updatedAt,
|
||||||
});
|
});
|
||||||
@@ -56,6 +57,9 @@ class StaffCertificate extends Equatable {
|
|||||||
/// Document number or reference.
|
/// Document number or reference.
|
||||||
final String? certificateNumber;
|
final String? certificateNumber;
|
||||||
|
|
||||||
|
/// The ID of the verification record.
|
||||||
|
final String? verificationId;
|
||||||
|
|
||||||
/// Recent validation/verification results.
|
/// Recent validation/verification results.
|
||||||
final StaffCertificateValidationStatus? validationStatus;
|
final StaffCertificateValidationStatus? validationStatus;
|
||||||
|
|
||||||
@@ -79,6 +83,7 @@ class StaffCertificate extends Equatable {
|
|||||||
issuer,
|
issuer,
|
||||||
certificateNumber,
|
certificateNumber,
|
||||||
validationStatus,
|
validationStatus,
|
||||||
|
verificationId,
|
||||||
createdAt,
|
createdAt,
|
||||||
updatedAt,
|
updatedAt,
|
||||||
];
|
];
|
||||||
@@ -97,6 +102,7 @@ class StaffCertificate extends Equatable {
|
|||||||
String? issuer,
|
String? issuer,
|
||||||
String? certificateNumber,
|
String? certificateNumber,
|
||||||
StaffCertificateValidationStatus? validationStatus,
|
StaffCertificateValidationStatus? validationStatus,
|
||||||
|
String? verificationId,
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
}) {
|
}) {
|
||||||
@@ -113,6 +119,7 @@ class StaffCertificate extends Equatable {
|
|||||||
issuer: issuer ?? this.issuer,
|
issuer: issuer ?? this.issuer,
|
||||||
certificateNumber: certificateNumber ?? this.certificateNumber,
|
certificateNumber: certificateNumber ?? this.certificateNumber,
|
||||||
validationStatus: validationStatus ?? this.validationStatus,
|
validationStatus: validationStatus ?? this.validationStatus,
|
||||||
|
verificationId: verificationId ?? this.verificationId,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -49,14 +49,22 @@ class CertificatesRepositoryImpl implements CertificatesRepository {
|
|||||||
await _signedUrlService.createSignedUrl(fileUri: uploadRes.fileUri);
|
await _signedUrlService.createSignedUrl(fileUri: uploadRes.fileUri);
|
||||||
|
|
||||||
// 3. Initiate verification
|
// 3. Initiate verification
|
||||||
// 3. Initiate verification
|
final List<domain.StaffCertificate> allCerts = await getCertificates();
|
||||||
|
final domain.StaffCertificate currentCert = allCerts.firstWhere(
|
||||||
|
(domain.StaffCertificate c) => c.certificationType == certificationType,
|
||||||
|
);
|
||||||
|
|
||||||
final String staffId = await _service.getStaffId();
|
final String staffId = await _service.getStaffId();
|
||||||
await _verificationService.createVerification(
|
final VerificationResponse verificationRes = await _verificationService
|
||||||
|
.createVerification(
|
||||||
fileUri: uploadRes.fileUri,
|
fileUri: uploadRes.fileUri,
|
||||||
type: certificationType.value,
|
type: certificationType.value,
|
||||||
category: 'CERTIFICATE',
|
category: 'certification',
|
||||||
subjectType: 'STAFF',
|
subjectType: 'worker',
|
||||||
subjectId: staffId,
|
subjectId: staffId,
|
||||||
|
rules: <String, dynamic>{
|
||||||
|
'certificateDescription': currentCert.description,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// 4. Update/Create Certificate in Data Connect
|
// 4. Update/Create Certificate in Data Connect
|
||||||
@@ -70,6 +78,7 @@ class CertificatesRepositoryImpl implements CertificatesRepository {
|
|||||||
certificateNumber: certificateNumber,
|
certificateNumber: certificateNumber,
|
||||||
validationStatus:
|
validationStatus:
|
||||||
domain.StaffCertificateValidationStatus.pendingExpertReview,
|
domain.StaffCertificateValidationStatus.pendingExpertReview,
|
||||||
|
verificationId: verificationRes.verificationId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 5. Return updated list or the specific certificate
|
// 5. Return updated list or the specific certificate
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
_numberController.text = widget.certificate!.certificateNumber ?? '';
|
_numberController.text = widget.certificate!.certificateNumber ?? '';
|
||||||
_nameController.text = widget.certificate!.name;
|
_nameController.text = widget.certificate!.name;
|
||||||
_selectedType = widget.certificate!.certificationType;
|
_selectedType = widget.certificate!.certificationType;
|
||||||
|
_selectedFilePath = widget.certificate?.certificateUrl;
|
||||||
} else {
|
} else {
|
||||||
_selectedType = ComplianceType.other;
|
_selectedType = ComplianceType.other;
|
||||||
}
|
}
|
||||||
@@ -116,6 +117,8 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showRemoveConfirmation(BuildContext context) async {
|
Future<void> _showRemoveConfirmation(BuildContext context) async {
|
||||||
|
final CertificateUploadCubit cubit =
|
||||||
|
BlocProvider.of<CertificateUploadCubit>(context);
|
||||||
final bool? confirmed = await showDialog<bool>(
|
final bool? confirmed = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => AlertDialog(
|
builder: (BuildContext context) => AlertDialog(
|
||||||
@@ -136,9 +139,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (confirmed == true && mounted) {
|
if (confirmed == true && mounted) {
|
||||||
BlocProvider.of<CertificateUploadCubit>(
|
await cubit.deleteCertificate(widget.certificate!.certificationType);
|
||||||
context,
|
|
||||||
).deleteCertificate(widget.certificate!.certificationType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +190,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
const SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _nameController,
|
controller: _nameController,
|
||||||
|
enabled: false,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: t.staff_certificates.upload_modal.name_hint,
|
hintText: t.staff_certificates.upload_modal.name_hint,
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
@@ -198,6 +200,46 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
|
// Issuer Field
|
||||||
|
Text(
|
||||||
|
t.staff_certificates.upload_modal.issuer_label,
|
||||||
|
style: UiTypography.body2m.textPrimary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
TextField(
|
||||||
|
controller: _issuerController,
|
||||||
|
enabled: false,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: t.staff_certificates.upload_modal.issuer_hint,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: UiConstants.radiusLg,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
|
// Certificate Number Field
|
||||||
|
Text(
|
||||||
|
'Certificate Number',
|
||||||
|
style: UiTypography.body2m.textPrimary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
TextField(
|
||||||
|
controller: _numberController,
|
||||||
|
enabled: false,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Enter number if applicable',
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: UiConstants.radiusLg,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
|
const Divider(),
|
||||||
|
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Expiry Date Field
|
// Expiry Date Field
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.expiry_label,
|
t.staff_certificates.upload_modal.expiry_label,
|
||||||
@@ -239,40 +281,6 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Issuer Field
|
|
||||||
Text(
|
|
||||||
t.staff_certificates.upload_modal.issuer_label,
|
|
||||||
style: UiTypography.body2m.textPrimary,
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
TextField(
|
|
||||||
controller: _issuerController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: t.staff_certificates.upload_modal.issuer_hint,
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: UiConstants.radiusLg,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space4),
|
|
||||||
|
|
||||||
// Certificate Number Field
|
|
||||||
Text(
|
|
||||||
'Certificate Number',
|
|
||||||
style: UiTypography.body2m.textPrimary,
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
TextField(
|
|
||||||
controller: _numberController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Enter number if applicable',
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: UiConstants.radiusLg,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space4),
|
|
||||||
|
|
||||||
// File Selector
|
// File Selector
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.upload_file,
|
t.staff_certificates.upload_modal.upload_file,
|
||||||
@@ -283,29 +291,6 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
selectedFilePath: _selectedFilePath,
|
selectedFilePath: _selectedFilePath,
|
||||||
onTap: _pickFile,
|
onTap: _pickFile,
|
||||||
),
|
),
|
||||||
|
|
||||||
// Remove Button (only if existing)
|
|
||||||
if (widget.certificate != null) ...<Widget>[
|
|
||||||
const SizedBox(height: UiConstants.space8),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: TextButton.icon(
|
|
||||||
onPressed: () => _showRemoveConfirmation(context),
|
|
||||||
icon: const Icon(UiIcons.delete, size: 20),
|
|
||||||
label: Text(t.staff_certificates.card.remove),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor: UiColors.destructive,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: UiConstants.space4,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: UiConstants.radiusLg,
|
|
||||||
side: const BorderSide(color: UiColors.destructive),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -314,6 +299,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
padding: const EdgeInsets.all(UiConstants.space5),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
spacing: UiConstants.space4,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// Attestation
|
// Attestation
|
||||||
Row(
|
Row(
|
||||||
@@ -334,7 +320,6 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
@@ -391,6 +376,30 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Remove Button (only if existing)
|
||||||
|
if (widget.certificate != null) ...<Widget>[
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: () => _showRemoveConfirmation(context),
|
||||||
|
icon: const Icon(UiIcons.delete, size: 20),
|
||||||
|
label: Text(t.staff_certificates.card.remove),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: UiColors.destructive,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: UiConstants.radiusLg,
|
||||||
|
side: const BorderSide(
|
||||||
|
color: UiColors.destructive,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -449,18 +458,17 @@ class _FileSelector extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.tagActive,
|
|
||||||
border: Border.all(color: UiColors.primary),
|
border: Border.all(color: UiColors.primary),
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(UiIcons.file, color: UiColors.primary),
|
const Icon(UiIcons.certificate, color: UiColors.primary),
|
||||||
const SizedBox(width: UiConstants.space3),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
selectedFilePath!.split('/').last,
|
selectedFilePath!.split('/').last,
|
||||||
style: UiTypography.body1m.textPrimary,
|
style: UiTypography.body1m.primary,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ query vaidateDayStaffApplication(
|
|||||||
$limit: Int
|
$limit: Int
|
||||||
$dayStart: Timestamp
|
$dayStart: Timestamp
|
||||||
$dayEnd: Timestamp
|
$dayEnd: Timestamp
|
||||||
) @auth(level: USER) {
|
) @auth(level: USER, insecureReason: "The staffId refers to the staff being validated. Ownership is verified at the application layer.") {
|
||||||
applications(
|
applications(
|
||||||
where: {
|
where: {
|
||||||
staffId: { eq: $staffId }
|
staffId: { eq: $staffId }
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ mutation CreateCertificate(
|
|||||||
$staffId: UUID!
|
$staffId: UUID!
|
||||||
$validationStatus: ValidationStatus
|
$validationStatus: ValidationStatus
|
||||||
$certificateNumber: String
|
$certificateNumber: String
|
||||||
) @auth(level: USER) {
|
) @auth(level: USER, insecureReason: "The staffId refers to the staff being modified. Ownership is verified at the application layer.") {
|
||||||
certificate_insert(
|
certificate_insert(
|
||||||
data: {
|
data: {
|
||||||
name: $name
|
name: $name
|
||||||
@@ -40,7 +40,7 @@ mutation UpdateCertificate(
|
|||||||
$issuer: String
|
$issuer: String
|
||||||
$validationStatus: ValidationStatus
|
$validationStatus: ValidationStatus
|
||||||
$certificateNumber: String
|
$certificateNumber: String
|
||||||
) @auth(level: USER) {
|
) @auth(level: USER, insecureReason: "The staffId refers to the staff being modified. Ownership is verified at the application layer.") {
|
||||||
certificate_update(
|
certificate_update(
|
||||||
key: { staffId: $staffId, certificationType: $certificationType }
|
key: { staffId: $staffId, certificationType: $certificationType }
|
||||||
data: {
|
data: {
|
||||||
@@ -58,7 +58,7 @@ mutation UpdateCertificate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutation DeleteCertificate($staffId: UUID!, $certificationType: ComplianceType!)
|
mutation DeleteCertificate($staffId: UUID!, $certificationType: ComplianceType!)
|
||||||
@auth(level: USER) {
|
@auth(level: USER, insecureReason: "The staffId refers to the staff being modified. Ownership is verified at the application layer.") {
|
||||||
certificate_delete(
|
certificate_delete(
|
||||||
key: { staffId: $staffId, certificationType: $certificationType }
|
key: { staffId: $staffId, certificationType: $certificationType }
|
||||||
)
|
)
|
||||||
@@ -88,7 +88,8 @@ mutation upsertStaffCertificate(
|
|||||||
$issuer: String
|
$issuer: String
|
||||||
$certificateNumber: String
|
$certificateNumber: String
|
||||||
$validationStatus: ValidationStatus
|
$validationStatus: ValidationStatus
|
||||||
) @auth(level: USER) {
|
$verificationId: String
|
||||||
|
) @auth(level: USER, insecureReason: "The staffId refers to the staff being modified. Ownership is verified at the application layer.") {
|
||||||
certificate_upsert(
|
certificate_upsert(
|
||||||
data: {
|
data: {
|
||||||
staffId: $staffId
|
staffId: $staffId
|
||||||
@@ -100,6 +101,7 @@ mutation upsertStaffCertificate(
|
|||||||
issuer: $issuer
|
issuer: $issuer
|
||||||
certificateNumber: $certificateNumber
|
certificateNumber: $certificateNumber
|
||||||
validationStatus: $validationStatus
|
validationStatus: $validationStatus
|
||||||
|
verificationId: $verificationId
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ type Certificate @table(name: "certificates", key: ["staffId", "certificationTyp
|
|||||||
certificateNumber: String
|
certificateNumber: String
|
||||||
|
|
||||||
validationStatus: ValidationStatus
|
validationStatus: ValidationStatus
|
||||||
|
verificationId: String
|
||||||
|
|
||||||
staffId: UUID!
|
staffId: UUID!
|
||||||
staff: Staff! @ref(fields: "staffId", references: "id")
|
staff: Staff! @ref(fields: "staffId", references: "id")
|
||||||
|
|||||||
Reference in New Issue
Block a user