feat: localization, file restriction banners, test credentials, edit icon fix
- #553: Audit and verify localizations (en/es), replace hardcoded strings - #549: Incomplete profile banner in Find Shifts (staff app) - #550: File restriction banner on document upload page - #551: File restriction banner on certificate upload page - #552: File restriction banner on attire upload page - #492: Hide edit icon for past/completed orders (client app) - #524: Display worker benefits in staff app - Add test credentials to seed: testclient@gmail.com, staff +1-555-555-1234 - Fix document upload validation (context arg in _validatePdfFile on submit) - Add PR_LOCALIZATION.md Made-with: Cursor
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -55,18 +57,44 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
static const int _kMaxFileSizeBytes = 10 * 1024 * 1024;
|
||||
|
||||
Future<void> _pickFile() async {
|
||||
final String? path = await _filePicker.pickFile(
|
||||
allowedExtensions: <String>['pdf', 'jpg', 'png'],
|
||||
allowedExtensions: <String>['pdf'],
|
||||
);
|
||||
|
||||
if (path != null) {
|
||||
final String? error = _validatePdfFile(context, path);
|
||||
if (error != null && mounted) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: error,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_selectedFilePath = path;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String? _validatePdfFile(BuildContext context, String path) {
|
||||
final File file = File(path);
|
||||
if (!file.existsSync()) return context.t.common.file_not_found;
|
||||
final String ext = path.split('.').last.toLowerCase();
|
||||
if (ext != 'pdf') {
|
||||
return context.t.staff_documents.upload.pdf_banner;
|
||||
}
|
||||
final int size = file.lengthSync();
|
||||
if (size > _kMaxFileSizeBytes) {
|
||||
return context.t.staff_documents.upload.pdf_banner;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> _selectDate() async {
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: context,
|
||||
@@ -117,12 +145,8 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
t
|
||||
.staff_documents
|
||||
.upload
|
||||
.instructions, // Reusing instructions logic
|
||||
style: UiTypography.body1m.textPrimary,
|
||||
_PdfFileTypesBanner(
|
||||
message: t.staff_documents.upload.pdf_banner,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
|
||||
@@ -135,7 +159,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
TextField(
|
||||
controller: _nameController,
|
||||
decoration: InputDecoration(
|
||||
hintText: "e.g. Food Handler Permit",
|
||||
hintText: t.staff_certificates.upload_modal.name_hint,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
@@ -193,7 +217,7 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
TextField(
|
||||
controller: _issuerController,
|
||||
decoration: InputDecoration(
|
||||
hintText: "e.g. Department of Health",
|
||||
hintText: t.staff_certificates.upload_modal.issuer_hint,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
@@ -247,19 +271,33 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
(_selectedFilePath != null &&
|
||||
state.isAttested &&
|
||||
_nameController.text.isNotEmpty)
|
||||
? () =>
|
||||
BlocProvider.of<CertificateUploadCubit>(
|
||||
? () {
|
||||
final String? err =
|
||||
_validatePdfFile(context, _selectedFilePath!);
|
||||
if (err != null) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
).uploadCertificate(
|
||||
UploadCertificateParams(
|
||||
certificationType: _selectedType!,
|
||||
name: _nameController.text,
|
||||
filePath: _selectedFilePath!,
|
||||
expiryDate: _selectedExpiryDate,
|
||||
issuer: _issuerController.text,
|
||||
certificateNumber: _numberController.text,
|
||||
message: err,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(
|
||||
UiConstants.space4,
|
||||
),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
BlocProvider.of<CertificateUploadCubit>(
|
||||
context,
|
||||
).uploadCertificate(
|
||||
UploadCertificateParams(
|
||||
certificationType: _selectedType!,
|
||||
name: _nameController.text,
|
||||
filePath: _selectedFilePath!,
|
||||
expiryDate: _selectedExpiryDate,
|
||||
issuer: _issuerController.text,
|
||||
certificateNumber: _numberController.text,
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
@@ -291,6 +329,42 @@ class _CertificateUploadPageState extends State<CertificateUploadPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Banner displaying accepted file types and size limit for PDF upload.
|
||||
class _PdfFileTypesBanner extends StatelessWidget {
|
||||
const _PdfFileTypesBanner({required this.message});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagActive,
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
border: Border.all(color: UiColors.primary.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.info, size: 20, color: UiColors.primary),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Text(
|
||||
message,
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FileSelector extends StatelessWidget {
|
||||
const _FileSelector({this.selectedFilePath, required this.onTap});
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -37,18 +39,44 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
String? _selectedFilePath;
|
||||
final FilePickerService _filePicker = Modular.get<FilePickerService>();
|
||||
|
||||
static const int _kMaxFileSizeBytes = 10 * 1024 * 1024;
|
||||
|
||||
Future<void> _pickFile() async {
|
||||
final String? path = await _filePicker.pickFile(
|
||||
allowedExtensions: <String>['pdf'],
|
||||
);
|
||||
|
||||
if (path != null) {
|
||||
final String? error = _validatePdfFile(context, path);
|
||||
if (error != null && mounted) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: error,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_selectedFilePath = path;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String? _validatePdfFile(BuildContext context, String path) {
|
||||
final File file = File(path);
|
||||
if (!file.existsSync()) return context.t.common.file_not_found;
|
||||
final String ext = path.split('.').last.toLowerCase();
|
||||
if (ext != 'pdf') {
|
||||
return context.t.staff_documents.upload.pdf_banner;
|
||||
}
|
||||
final int size = file.lengthSync();
|
||||
if (size > _kMaxFileSizeBytes) {
|
||||
return context.t.staff_documents.upload.pdf_banner;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<DocumentUploadCubit>(
|
||||
@@ -82,9 +110,8 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
t.staff_documents.upload.instructions,
|
||||
style: UiTypography.body1m.textPrimary,
|
||||
_PdfFileTypesBanner(
|
||||
message: t.staff_documents.upload.pdf_banner,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
DocumentFileSelector(
|
||||
@@ -116,9 +143,22 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
isUploading:
|
||||
state.status == DocumentUploadStatus.uploading,
|
||||
canSubmit: _selectedFilePath != null && state.isAttested,
|
||||
onSubmit: () => BlocProvider.of<DocumentUploadCubit>(
|
||||
context,
|
||||
).uploadDocument(widget.document.id, _selectedFilePath!),
|
||||
onSubmit: () {
|
||||
final String? err =
|
||||
_validatePdfFile(context, _selectedFilePath!);
|
||||
if (err != null) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: err,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
return;
|
||||
}
|
||||
BlocProvider.of<DocumentUploadCubit>(
|
||||
context,
|
||||
).uploadDocument(widget.document.id, _selectedFilePath!);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -130,3 +170,39 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Banner displaying accepted file types and size limit for PDF upload.
|
||||
class _PdfFileTypesBanner extends StatelessWidget {
|
||||
const _PdfFileTypesBanner({required this.message});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagActive,
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
border: Border.all(color: UiColors.primary.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.info, size: 20, color: UiColors.primary),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Text(
|
||||
message,
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class DocumentsPage extends StatelessWidget {
|
||||
: t.staff_documents.list.error(
|
||||
message: state.errorMessage!,
|
||||
))
|
||||
: t.staff_documents.list.error(message: 'Unknown'),
|
||||
: t.staff_documents.list.error(message: t.staff_documents.list.unknown),
|
||||
textAlign: TextAlign.center,
|
||||
style: UiTypography.body1m.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -31,6 +33,12 @@ class AttireCapturePage extends StatefulWidget {
|
||||
State<AttireCapturePage> createState() => _AttireCapturePageState();
|
||||
}
|
||||
|
||||
/// Maximum file size for attire upload (10MB).
|
||||
const int _kMaxFileSizeBytes = 10 * 1024 * 1024;
|
||||
|
||||
/// Allowed file extensions for attire upload.
|
||||
const Set<String> _kAllowedExtensions = <String>{'jpeg', 'jpg', 'png'};
|
||||
|
||||
class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
String? _selectedLocalPath;
|
||||
|
||||
@@ -57,6 +65,16 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
final GalleryService service = Modular.get<GalleryService>();
|
||||
final String? path = await service.pickImage();
|
||||
if (path != null && context.mounted) {
|
||||
final String? error = _validateFile(path);
|
||||
if (error != null) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: error,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_selectedLocalPath = path;
|
||||
});
|
||||
@@ -84,6 +102,16 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
final CameraService service = Modular.get<CameraService>();
|
||||
final String? path = await service.takePhoto();
|
||||
if (path != null && context.mounted) {
|
||||
final String? error = _validateFile(path);
|
||||
if (error != null) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: error,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_selectedLocalPath = path;
|
||||
});
|
||||
@@ -105,7 +133,7 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: const Icon(Icons.photo_library),
|
||||
title: const Text('Gallery'),
|
||||
title: Text(t.common.gallery),
|
||||
onTap: () {
|
||||
Modular.to.pop();
|
||||
_onGallery(context);
|
||||
@@ -113,7 +141,7 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.camera_alt),
|
||||
title: const Text('Camera'),
|
||||
title: Text(t.common.camera),
|
||||
onTap: () {
|
||||
Modular.to.pop();
|
||||
_onCamera(context);
|
||||
@@ -128,17 +156,33 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
void _showAttestationWarning(BuildContext context) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: 'Please attest that you own this item.',
|
||||
message: t.staff_profile_attire.capture.attest_please,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
}
|
||||
|
||||
/// Validates file format (JPEG, JPG, PNG) and size (max 10MB).
|
||||
/// Returns an error message if invalid, or null if valid.
|
||||
String? _validateFile(String path) {
|
||||
final File file = File(path);
|
||||
if (!file.existsSync()) return t.common.file_not_found;
|
||||
final String ext = path.split('.').last.toLowerCase();
|
||||
if (!_kAllowedExtensions.contains(ext)) {
|
||||
return t.staff_profile_attire.upload_file_types_banner;
|
||||
}
|
||||
final int size = file.lengthSync();
|
||||
if (size > _kMaxFileSizeBytes) {
|
||||
return t.staff_profile_attire.capture.file_size_exceeds;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _showError(BuildContext context, String message) {
|
||||
debugPrint(message);
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: 'Could not access camera or gallery. Please try again.',
|
||||
message: t.staff_profile_attire.capture.could_not_access_media,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
@@ -150,6 +194,17 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
);
|
||||
if (_selectedLocalPath == null) return;
|
||||
|
||||
final String? error = _validateFile(_selectedLocalPath!);
|
||||
if (error != null) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: error,
|
||||
type: UiSnackbarType.error,
|
||||
margin: const EdgeInsets.all(UiConstants.space4),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await cubit.uploadPhoto(widget.item.id, _selectedLocalPath!);
|
||||
if (context.mounted && cubit.state.status == AttireCaptureStatus.success) {
|
||||
setState(() {
|
||||
@@ -160,10 +215,10 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
|
||||
String _getStatusText(bool hasUploadedPhoto) {
|
||||
return switch (widget.item.verificationStatus) {
|
||||
AttireVerificationStatus.approved => 'Approved',
|
||||
AttireVerificationStatus.rejected => 'Rejected',
|
||||
AttireVerificationStatus.pending => 'Pending Verification',
|
||||
_ => hasUploadedPhoto ? 'Pending Verification' : 'Not Uploaded',
|
||||
AttireVerificationStatus.approved => t.staff_profile_attire.capture.approved,
|
||||
AttireVerificationStatus.rejected => t.staff_profile_attire.capture.rejected,
|
||||
AttireVerificationStatus.pending => t.staff_profile_attire.capture.pending_verification,
|
||||
_ => hasUploadedPhoto ? t.staff_profile_attire.capture.pending_verification : t.staff_profile_attire.capture.not_uploaded,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,7 +262,7 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
if (state.status == AttireCaptureStatus.success) {
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: 'Attire image submitted for verification',
|
||||
message: t.staff_profile_attire.capture.attire_submitted,
|
||||
type: UiSnackbarType.success,
|
||||
);
|
||||
Modular.to.toAttire();
|
||||
@@ -225,6 +280,10 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_FileTypesBanner(
|
||||
message: t.staff_profile_attire.upload_file_types_banner,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
ImagePreviewSection(
|
||||
selectedLocalPath: _selectedLocalPath,
|
||||
currentPhotoUrl: currentPhotoUrl,
|
||||
@@ -268,3 +327,43 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Banner displaying accepted file types and size limit for attire upload.
|
||||
class _FileTypesBanner extends StatelessWidget {
|
||||
const _FileTypesBanner({required this.message});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagActive,
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
border: Border.all(color: UiColors.primary.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
UiIcons.info,
|
||||
size: 20,
|
||||
color: UiColors.primary,
|
||||
),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Text(
|
||||
message,
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class AttirePage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
Text(
|
||||
'No items found for this filter.',
|
||||
context.t.staff_profile_attire.capture.no_items_filter,
|
||||
style: UiTypography.body1m.textSecondary,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@@ -28,7 +29,7 @@ class ImagePreviewSection extends StatelessWidget {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'Review the attire item',
|
||||
context.t.staff_profile_attire.capture.review_attire_item,
|
||||
style: UiTypography.body1b.textPrimary,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
@@ -42,7 +43,7 @@ class ImagePreviewSection extends StatelessWidget {
|
||||
if (currentPhotoUrl != null) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Text('Your Uploaded Photo', style: UiTypography.body1b.textPrimary),
|
||||
Text(context.t.staff_profile_attire.capture.your_uploaded_photo, style: UiTypography.body1b.textPrimary),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
AttireImagePreview(imageUrl: currentPhotoUrl),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
@@ -56,7 +57,7 @@ class ImagePreviewSection extends StatelessWidget {
|
||||
AttireImagePreview(imageUrl: referenceImageUrl),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
Text(
|
||||
'Example of the item that you need to upload.',
|
||||
context.t.staff_profile_attire.capture.example_upload_hint,
|
||||
style: UiTypography.body1b.textSecondary,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -77,7 +78,7 @@ class ReferenceExample extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Text('Reference Example', style: UiTypography.body2b.textSecondary),
|
||||
Text(context.t.staff_profile_attire.capture.reference_example, style: UiTypography.body2b.textSecondary),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Center(
|
||||
child: ClipRRect(
|
||||
|
||||
Reference in New Issue
Block a user