From c32ed5119860089b664eb01187c53675e8fc1eec Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 1 Aug 2023 16:20:38 +0800 Subject: [PATCH 1/3] Implemented attachment features to profile screens --- assets/svgs/jpg.svg | 33 + assets/svgs/pdf.svg | 32 + assets/svgs/png.svg | 28 + ios/Podfile.lock | 50 ++ .../profile/education/education_bloc.dart | 34 +- .../profile/education/education_event.dart | 11 + .../profile/education/education_state.dart | 3 +- .../profile/eligibility/eligibility_bloc.dart | 62 +- .../eligibility/eligibility_event.dart | 27 + .../eligibility/eligibility_state.dart | 50 +- .../learning_development_bloc.dart | 32 +- .../learning_development_event.dart | 11 + .../learning_development_state.dart | 3 +- .../voluntary_works/voluntary_work_event.dart | 10 + .../profile/workHistory/workHistory_bloc.dart | 31 +- .../workHistory/workHistory_event.dart | 10 + .../workHistory/workHistory_state.dart | 3 +- lib/model/profile/attachment.dart | 122 ++++ lib/model/profile/educational_background.dart | 6 +- lib/model/profile/eligibility.dart | 6 +- lib/model/profile/learning_development.dart | 5 +- lib/model/profile/work_history.dart | 7 +- .../profile/components/education_screen.dart | 581 ++++++++++++++---- .../components/eligibility_screen.dart | 462 +++++++++++--- .../learning_and_development_screen.dart | 546 ++++++++++++---- .../components/work_history_screen.dart | 534 ++++++++++++---- .../profile/shared/multiple_attachment.dart | 150 +++++ .../profile/shared/single_attachment.dart | 49 ++ lib/sevices/profile/eligibility_services.dart | 136 ++-- lib/utils/attachment_categories.dart | 77 +++ lib/utils/urls.dart | 6 + pubspec.lock | 8 + pubspec.yaml | 1 + 33 files changed, 2586 insertions(+), 540 deletions(-) create mode 100644 assets/svgs/jpg.svg create mode 100644 assets/svgs/pdf.svg create mode 100644 assets/svgs/png.svg create mode 100644 lib/model/profile/attachment.dart create mode 100644 lib/screens/profile/shared/multiple_attachment.dart create mode 100644 lib/screens/profile/shared/single_attachment.dart create mode 100644 lib/utils/attachment_categories.dart diff --git a/assets/svgs/jpg.svg b/assets/svgs/jpg.svg new file mode 100644 index 0000000..f6e0591 --- /dev/null +++ b/assets/svgs/jpg.svg @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/assets/svgs/pdf.svg b/assets/svgs/pdf.svg new file mode 100644 index 0000000..5226e09 --- /dev/null +++ b/assets/svgs/pdf.svg @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/assets/svgs/png.svg b/assets/svgs/png.svg new file mode 100644 index 0000000..77671a4 --- /dev/null +++ b/assets/svgs/png.svg @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index cdf552e..0bcedcc 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -11,8 +11,42 @@ PODS: - SwiftProtobuf - device_info (0.0.1): - Flutter + - DKImagePickerController/Core (4.3.4): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.4) + - DKImagePickerController/PhotoGallery (4.3.4): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.4) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif - easy_app_installer (0.0.1): - Flutter + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter - Flutter (1.0.0) - fluttertoast (0.0.2): - Flutter @@ -36,6 +70,9 @@ PODS: - Flutter - rive_common (0.0.1): - Flutter + - SDWebImage (5.17.0): + - SDWebImage/Core (= 5.17.0) + - SDWebImage/Core (5.17.0) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -43,6 +80,7 @@ PODS: - Flutter - FMDB (>= 2.7.5) - SwiftProtobuf (1.20.3) + - SwiftyGif (5.4.4) - Toast (4.0.0) DEPENDENCIES: @@ -52,6 +90,7 @@ DEPENDENCIES: - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - device_info (from `.symlinks/plugins/device_info/ios`) - easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - location (from `.symlinks/plugins/location/ios`) @@ -66,9 +105,13 @@ DEPENDENCIES: SPEC REPOS: trunk: + - DKImagePickerController + - DKPhotoGallery - FMDB - MTBBarcodeScanner + - SDWebImage - SwiftProtobuf + - SwiftyGif - Toast EXTERNAL SOURCES: @@ -84,6 +127,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/device_info/ios" easy_app_installer: :path: ".symlinks/plugins/easy_app_installer/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter fluttertoast: @@ -113,7 +158,10 @@ SPEC CHECKSUMS: audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 + DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 easy_app_installer: 29abe397da7d86721fee853281202f414373f45c + file_picker: ce3938a0df3cc1ef404671531facef740d03f920 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a @@ -125,9 +173,11 @@ SPEC CHECKSUMS: permission_handler_apple: 8f116445eff3c0e7c65ad60f5fef5490aa94b4e4 platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 rive_common: 60ae7896ab40f9513974f36f015de33f70d2c5c5 + SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9 shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 + SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index e965101..cb43d54 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -3,6 +3,9 @@ import 'package:equatable/equatable.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/sevices/profile/education_services.dart'; +import '../../../model/profile/attachment.dart'; +import '../../../utils/attachment_categories.dart'; + part 'education_event.dart'; part 'education_state.dart'; @@ -11,20 +14,25 @@ class EducationBloc extends Bloc { List schools = []; List programs = []; List honors = []; + List attachmentCategories = []; EducationBloc() : super(EducationInitial()) { on((event, emit) async { emit(EducationalBackgroundLoadingState()); try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } if (educationalBackgrounds.isEmpty) { List educations = await EducationService .instace .getEducationalBackground(event.profileId, event.token); educationalBackgrounds = educations; emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + educationalBackground: educationalBackgrounds, attachmentCategory: attachmentCategories)); } else { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); } } catch (e) { emit(EducationalBackgroundErrorState(message: e.toString())); @@ -89,7 +97,7 @@ class EducationBloc extends Bloc { ////LOAD on((event, emit) { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); }); //// SHOW EDIT FORM on((event, emit) async { @@ -134,5 +142,25 @@ class EducationBloc extends Bloc { emit(EducationalBackgroundErrorState(message: e.toString())); } }); + ////Add attachment + on((event, emit) async { + emit(EducationalBackgroundLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(EducationAddedState(response: status)); + } else { + emit(EducationAddedState(response: status)); + } + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/education/education_event.dart b/lib/bloc/profile/education/education_event.dart index 59a431b..667befb 100644 --- a/lib/bloc/profile/education/education_event.dart +++ b/lib/bloc/profile/education/education_event.dart @@ -63,3 +63,14 @@ class DeleteEducation extends EducationEvent{ @override List get props => [educationalBackground, profileId, token]; } +////Add attachment +class AddAttachment extends EducationEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/education/education_state.dart b/lib/bloc/profile/education/education_state.dart index 2a0f1ec..a8bebe0 100644 --- a/lib/bloc/profile/education/education_state.dart +++ b/lib/bloc/profile/education/education_state.dart @@ -10,8 +10,9 @@ abstract class EducationState extends Equatable { class EducationInitial extends EducationState {} class EducationalBackgroundLoadedState extends EducationState { + final List< AttachmentCategory> attachmentCategory; final List educationalBackground; - const EducationalBackgroundLoadedState({required this.educationalBackground}); + const EducationalBackgroundLoadedState({required this.educationalBackground, required this.attachmentCategory}); @override List get props => [educationalBackground]; } diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index e405355..c47b309 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -1,9 +1,11 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/utils/attachment_categories.dart'; import '../../../model/location/city.dart'; import '../../../model/location/country.dart'; import '../../../model/location/provinces.dart'; import '../../../model/location/region.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/profile/eligibility.dart'; import '../../../model/utils/eligibility.dart'; import '../../../sevices/profile/eligibility_services.dart'; @@ -18,10 +20,13 @@ class EligibilityBloc extends Bloc { List globalRegions = []; List globalEligibilities = []; List eligibilities = []; + List attachmentCategories = []; //// LOAD ELIGIBILTY on((event, emit) { emit(EligibilityLoadingState()); - emit(EligibilityLoaded(eligibilities: eligibilities)); + emit(EligibilityLoaded( + eligibilities: eligibilities, + attachmentCategory: attachmentCategories)); }); //// DELETE @@ -48,13 +53,21 @@ class EligibilityBloc extends Bloc { //// GET ELIGIBILITY on((event, emit) async { try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } if (eligibilities.isNotEmpty) { - emit(EligibilityLoaded(eligibilities: eligibilities)); + emit(EligibilityLoaded( + eligibilities: eligibilities, + attachmentCategory: attachmentCategories)); } else { emit(EligibilityLoadingState()); eligibilities = await EligibilityService.instance .getEligibilities(event.profileId, event.token); - emit(EligibilityLoaded(eligibilities: eligibilities)); + emit(EligibilityLoaded( + eligibilities: eligibilities, + attachmentCategory: attachmentCategories)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); @@ -208,5 +221,48 @@ class EligibilityBloc extends Bloc { emit(const EligibilityErrorState( message: "Something went wrong. Please try again")); }); + ////Add attachment + on((event, emit) async { + emit(EligibilityLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(EligibilityAddedState(response: status)); + } else { + emit(EligibilityAddedState(response: status)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); + + on((event, emit) async { + // try { + final bool success = await EligibilityService.instance.deleteAttachment( + moduleId: int.parse(event.moduleId), + attachment: event.attachment, + profileId: event.profileId, + token: event.token); + if (success) { + EligibityCert eligibityCert = eligibilities.firstWhere((element) =>element.id.toString() == event.moduleId); + eligibityCert.attachments!.removeWhere((element){ + return event.attachment.id == element.id; + }); + emit(DeletedState( + success: success, + )); + } else { + emit(DeletedState(success: success)); + } + // } catch (e) { + // emit(EligibilityErrorState(message: e.toString())); + // } + }); } } diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index cd57562..8d17236 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -55,6 +55,7 @@ class DeleteEligibility extends EligibilityEvent { final String profileId; final int eligibilityId; final String token; + const DeleteEligibility( { required this.eligibilityId, @@ -68,4 +69,30 @@ class CallErrorState extends EligibilityEvent{ } +class AddAttachment extends EligibilityEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} + +class DeleteAttachment extends EligibilityEvent{ + final String profileId; + final String token; + final Attachment attachment; + final String moduleId; + const DeleteAttachment({required this.attachment,required this.moduleId, required this.profileId, required this.token}); +} + + + + + + + + diff --git a/lib/bloc/profile/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart index 3b57f6d..3a1994a 100644 --- a/lib/bloc/profile/eligibility/eligibility_state.dart +++ b/lib/bloc/profile/eligibility/eligibility_state.dart @@ -2,14 +2,13 @@ part of 'eligibility_bloc.dart'; abstract class EligibilityState extends Equatable { const EligibilityState(); - + @override List get props => []; } class EligibilityInitial extends EligibilityState {} - class EditEligibilityState extends EligibilityState { final EligibityCert eligibityCert; final List eligibilities; @@ -23,13 +22,14 @@ class EditEligibilityState extends EligibilityState { final Province? currentProvince; final CityMunicipality? currentCity; final Country selectedCountry; - const EditEligibilityState({ - required this.provinces, - required this.cities, - required this.currentProvince, - required this.currentCity, - required this.currentRegion, - required this.currentEligibility, + + const EditEligibilityState({ + required this.provinces, + required this.cities, + required this.currentProvince, + required this.currentCity, + required this.currentRegion, + required this.currentEligibility, required this.isOverseas, required this.eligibityCert, required this.eligibilities, @@ -53,41 +53,43 @@ class AddEligibilityState extends EligibilityState { final List eligibilities; final List countries; final List regions; - const AddEligibilityState({ + const AddEligibilityState({ required this.eligibilities, required this.countries, required this.regions, }); @override - List get props => [eligibilities,countries,regions]; + List get props => [eligibilities, countries, regions]; } -class EligibilityEditedState extends EligibilityState{ - final Map response; + +class EligibilityEditedState extends EligibilityState { + final Map response; const EligibilityEditedState({required this.response}); @override - List get props =>[ response]; + List get props => [response]; } -class EligibilityAddedState extends EligibilityState{ - final Map response; +class EligibilityAddedState extends EligibilityState { + final Map response; - const EligibilityAddedState({ required this.response}); + const EligibilityAddedState({required this.response}); @override - List get props =>[response]; + List get props => [response]; } -class EligibilityLoadingState extends EligibilityState{ -} -class EligibilityErrorState extends EligibilityState{ +class EligibilityLoadingState extends EligibilityState {} + +class EligibilityErrorState extends EligibilityState { final String message; const EligibilityErrorState({required this.message}); - @override - List get props =>[message]; + @override + List get props => [message]; } class EligibilityLoaded extends EligibilityState { + final List< AttachmentCategory> attachmentCategory; final List eligibilities; - const EligibilityLoaded({required this.eligibilities}); + const EligibilityLoaded({required this.eligibilities, required this.attachmentCategory}); @override List get props => [eligibilities]; } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index 6ef27c0..e7c145d 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -6,9 +6,11 @@ import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; import '../../../model/location/barangay.dart'; import '../../../model/location/city.dart'; import '../../../model/location/provinces.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/profile/learning_development.dart'; import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; +import '../../../utils/attachment_categories.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; part 'learning_development_event.dart'; @@ -27,7 +29,7 @@ class LearningDevelopmentBloc List globalBarangay = []; List agencies = []; List agencyCategory = []; - + List attachmentCategories = []; Region? currentRegion; Country? currentCountry; Province? currentProvince; @@ -37,12 +39,16 @@ class LearningDevelopmentBloc on((event, emit) async { emit(LearningDevelopmentLoadingState()); try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } List learnings = await LearningDevelopmentServices .instance .getLearningDevelopments(event.profileId, event.token); learningsAndDevelopments = learnings; emit(LearningDevelopmentLoadedState( - learningsAndDevelopment: learningsAndDevelopments)); + learningsAndDevelopment: learningsAndDevelopments,attachmentCategory: attachmentCategories)); } catch (e) { emit(LearningDevelopmentErrorState(message: e.toString())); } @@ -50,7 +56,7 @@ class LearningDevelopmentBloc ////load on((event, emit) { emit(LearningDevelopmentLoadedState( - learningsAndDevelopment: learningsAndDevelopments)); + learningsAndDevelopment: learningsAndDevelopments,attachmentCategory: attachmentCategories)); }); //// show add form on((event, emit) async { @@ -266,5 +272,25 @@ class LearningDevelopmentBloc on((event, emit) { emit(LearningDevelopmentErrorState(message: event.message)); }); + ////Add attachment + on((event, emit) async { + emit(LearningDevelopmentLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(LearningDevelopmentAddedState(response: status)); + } else { + emit(LearningDevelopmentAddedState(response: status)); + } + } catch (e) { + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart index 1b1453f..cfde6bb 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -66,3 +66,14 @@ class CallErrorState extends LearningDevelopmentEvent{ final String message; const CallErrorState({required this.message}); } + +class AddAttachment extends LearningDevelopmentEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index e498dc9..323d076 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -10,8 +10,9 @@ abstract class LearningDevelopmentState extends Equatable { class LearningDevelopmentInitial extends LearningDevelopmentState {} class LearningDevelopmentLoadedState extends LearningDevelopmentState { + final List< AttachmentCategory> attachmentCategory; final List learningsAndDevelopment; - const LearningDevelopmentLoadedState({required this.learningsAndDevelopment}); + const LearningDevelopmentLoadedState({required this.learningsAndDevelopment, required this.attachmentCategory}); @override List get props => [learningsAndDevelopment]; } diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart index 02ed79b..ba83ba5 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart @@ -72,3 +72,13 @@ class DeleteVoluntaryWork extends VoluntaryWorkEvent { @override List get props => [profileId, token, work]; } +class AddAttachment extends VoluntaryWorkEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 2f8a28e..71f81ce 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -8,7 +8,9 @@ import 'package:unit2/model/utils/position.dart'; import 'package:unit2/sevices/profile/work_history_services.dart'; import 'package:unit2/utils/profile_utilities.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/utils/category.dart'; +import '../../../utils/attachment_categories.dart'; part 'workHistory_event.dart'; part 'workHistory_state.dart'; @@ -19,17 +21,22 @@ class WorkHistoryBloc extends Bloc { List agencies = []; List appointmentStatus = []; List agencyCategory = []; + List attachmentCategories = []; WorkHistoryBloc() : super(EducationInitial()) { ////GET WORK HISTORIES on((event, emit) async { emit(WorkHistoryLoadingState()); try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } if (workExperiences.isEmpty) { List works = await WorkHistoryService.instance .getWorkExperiences(event.profileId, event.token); workExperiences = works; } - emit(WorkHistoryLoaded(workExperiences: workExperiences)); + emit(WorkHistoryLoaded(workExperiences: workExperiences, attachmentCategory: attachmentCategories)); } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); } @@ -37,7 +44,7 @@ class WorkHistoryBloc extends Bloc { ///// LOAD WORK HISTORIES on((event, emit) { emit(WorkHistoryLoadingState()); - emit(WorkHistoryLoaded(workExperiences: workExperiences)); + emit(WorkHistoryLoaded(workExperiences: workExperiences,attachmentCategory: attachmentCategories)); }); ////DELETE on((event, emit) async { @@ -182,5 +189,25 @@ class WorkHistoryBloc extends Bloc { emit(WorkHistoryErrorState(message: e.toString())); } }); + ////Add Attachment + on((event, emit) async { + emit(WorkHistoryLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(WorkHistoryAddedState(response: status)); + } else { + emit(WorkHistoryAddedState(response: status)); + } + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index 0f394b7..ebc1efd 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -60,5 +60,15 @@ class AddWorkHostory extends WorkHistorytEvent{ @override List get props => [workHistory,profileId,token,isPrivate]; } +class AddAttachment extends WorkHistorytEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index 2008857..d5905a8 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -11,7 +11,8 @@ class EducationInitial extends WorkHistoryState {} class WorkHistoryLoaded extends WorkHistoryState{ final List workExperiences; - const WorkHistoryLoaded({required this.workExperiences}); + final List< AttachmentCategory> attachmentCategory; + const WorkHistoryLoaded({required this.workExperiences,required this.attachmentCategory}); @override List get props => [workExperiences]; } diff --git a/lib/model/profile/attachment.dart b/lib/model/profile/attachment.dart new file mode 100644 index 0000000..17169e2 --- /dev/null +++ b/lib/model/profile/attachment.dart @@ -0,0 +1,122 @@ +// To parse this JSON data, do +// +// final attachment = attachmentFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +Attachment attachmentFromJson(String str) => + Attachment.fromJson(json.decode(str)); + +String attachmentToJson(Attachment data) => json.encode(data.toJson()); + +class Attachment { + final int? id; + final String? source; + final AttachmentCategory? category; + final String? filename; + final Subclass? subclass; + final DateTime? createdAt; + + Attachment({ + required this.id, + required this.source, + required this.category, + required this.filename, + required this.subclass, + required this.createdAt, + }); + + factory Attachment.fromJson(Map json) => Attachment( + id: json["id"], + source: json["source"], + category: json['category'] == null + ? null + : AttachmentCategory.fromJson(json["category"]), + filename: json["filename"], + subclass: json['subclass'] == null + ? null + : Subclass.fromJson(json["subclass"]), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json["created_at"]), + ); + + Map toJson() => { + "id": id, + "source": source, + "category": category?.toJson(), + "filename": filename, + "subclass": subclass?.toJson(), + "created_at": createdAt?.toIso8601String(), + }; +} + +class AttachmentCategory { + final int id; + final Subclass subclass; + final String description; + + AttachmentCategory({ + required this.id, + required this.subclass, + required this.description, + }); + + factory AttachmentCategory.fromJson(Map json) => AttachmentCategory( + id: json["id"], + subclass: Subclass.fromJson(json["subclass"]), + description: json["description"], + ); + + Map toJson() => { + "id": id, + "subclass": subclass.toJson(), + "description": description, + }; +} + +class Subclass { + final int id; + final String name; + final AttachmentClass attachmentClass; + + Subclass({ + required this.id, + required this.name, + required this.attachmentClass, + }); + + factory Subclass.fromJson(Map json) => Subclass( + id: json["id"], + name: json["name"], + attachmentClass: AttachmentClass.fromJson(json["attachment_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "attachment_class": attachmentClass.toJson(), + }; +} + +class AttachmentClass { + final int id; + final String name; + + AttachmentClass({ + required this.id, + required this.name, + }); + + factory AttachmentClass.fromJson(Map json) => + AttachmentClass( + id: json["id"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "name": name, + }; +} diff --git a/lib/model/profile/educational_background.dart b/lib/model/profile/educational_background.dart index 4deaee4..ef061a0 100644 --- a/lib/model/profile/educational_background.dart +++ b/lib/model/profile/educational_background.dart @@ -4,6 +4,8 @@ import 'dart:convert'; +import 'package:unit2/model/profile/attachment.dart'; + EducationalBackground educationalBackgroundFromJson(String str) => EducationalBackground.fromJson(json.decode(str)); String educationalBackgroundToJson(EducationalBackground data) => json.encode(data.toJson()); @@ -24,7 +26,7 @@ class EducationalBackground { final List? honors; final Education? education; final String? periodTo; - final dynamic attachments; + final List? attachments; final String? periodFrom; final int? unitsEarned; final String? yearGraduated; @@ -34,7 +36,7 @@ class EducationalBackground { honors: json["honors"] == null ? [] : List.from(json["honors"]!.map((x) => Honor.fromJson(x))), education: json["education"] == null ? null : Education.fromJson(json["education"]), periodTo: json["period_to"], - attachments: json["attachments"], + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), periodFrom: json["period_from"], unitsEarned: json["units_earned"], yearGraduated: json["year_graduated"], diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index f57007d..96dbb54 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -6,6 +6,7 @@ import 'package:meta/meta.dart'; import 'dart:convert'; import 'package:unit2/model/location/region.dart'; +import 'package:unit2/model/profile/attachment.dart'; import '../location/address_category.dart'; import '../location/city.dart'; @@ -33,7 +34,8 @@ class EligibityCert { final int? id; final double? rating; final DateTime? examDate; - final dynamic attachments; + final List? attachments; + final Eligibility? eligibility; final ExamAddress? examAddress; final DateTime? validityDate; @@ -45,7 +47,7 @@ class EligibityCert { examDate: json['exam_date'] == null ? null : DateTime.parse(json["exam_date"]), - attachments: null, + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), eligibility: json['eligibility'] == null ? null : Eligibility.fromJson(json["eligibility"]), diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart index c00f276..17362f4 100644 --- a/lib/model/profile/learning_development.dart +++ b/lib/model/profile/learning_development.dart @@ -10,6 +10,7 @@ import '../location/city.dart'; import '../location/country.dart'; import '../utils/agency.dart'; import '../utils/industry_class.dart'; +import 'attachment.dart'; LearningDevelopement learningDevelopementFromJson(String str) => LearningDevelopement.fromJson(json.decode(str)); @@ -23,13 +24,13 @@ class LearningDevelopement { this.totalHoursAttended, }); - final dynamic attachments; + final List? attachments; final Agency? sponsoredBy; final ConductedTraining? conductedTraining; final double? totalHoursAttended; factory LearningDevelopement.fromJson(Map json) => LearningDevelopement( - attachments: json["attachments"], + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), sponsoredBy: json["sponsored_by"] == null ? null : Agency.fromJson(json["sponsored_by"]), conductedTraining: json["conducted_training"] == null ? null : ConductedTraining.fromJson(json["conducted_training"]), totalHoursAttended: json["total_hours_attended"], diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 821e603..076cf47 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -8,6 +8,7 @@ import '../utils/agency.dart'; import '../utils/category.dart'; import '../utils/industry_class.dart'; import '../utils/position.dart'; +import 'attachment.dart'; WorkHistory workHistoryFromJson(String str) => WorkHistory.fromJson(json.decode(str)); @@ -21,7 +22,7 @@ class WorkHistory { this.toDate, this.position, this.fromDate, - // this.attachments, + this.attachments, this.salaryGrade, this.monthlySalary, this.appointmentStatus, @@ -33,7 +34,7 @@ class WorkHistory { final DateTime? toDate; final Position? position; final DateTime? fromDate; - // final dynamic attachments; + final List? attachments; final int? salaryGrade; final double? monthlySalary; final String? appointmentStatus; @@ -45,7 +46,7 @@ class WorkHistory { toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), position: json["position"] == null ? null : Position.fromJson(json["position"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), - // attachments: json["attachments"], + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), salaryGrade: json["salary_grade"], monthlySalary: json["monthly_salary"], appointmentStatus: json["appointment_status"], diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 4514284..8f8363f 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -1,8 +1,12 @@ import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; @@ -12,14 +16,26 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; + import '../../../bloc/profile/education/education_bloc.dart'; +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; import '../../../widgets/Leadings/close_leading.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; import 'education/edit_modal.dart'; + class EducationScreen extends StatelessWidget { const EducationScreen({super.key}); @override Widget build(BuildContext context) { + final parent = context; + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; int profileId; String? token; return Scaffold( @@ -144,6 +160,11 @@ class EducationScreen extends StatelessWidget { }, builder: (context, state) { if (state is EducationalBackgroundLoadedState) { + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 1) { + attachmentCategories.add(cat); + } + } if (state.educationalBackground.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -182,54 +203,91 @@ class EducationScreen extends StatelessWidget { decoration: box1(), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), - child: Row( + child: Column( children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Row( + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, children: [ - Expanded( - child: Text(level, - style: Theme.of( - context) - .textTheme - .titleSmall!)), + Row( + children: [ + Expanded( + child: Text( + level, + style: Theme.of( + context) + .textTheme + .titleSmall!)), + Text( + "$periodFrom - $periodTo", + style: Theme.of( + context) + .textTheme + .bodyMedium, + ), + ], + ), + const SizedBox( + height: 5, + ), Text( - "$periodFrom - $periodTo", + school, style: Theme.of( context) .textTheme - .bodyMedium, + .titleMedium! + .copyWith( + color: + primary, + fontWeight: + FontWeight + .w500), ), - ], - ), - const SizedBox( - height: 5, - ), - Text( - school, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - color: primary, - fontWeight: - FontWeight - .w500), - ), - Container( - padding: - const EdgeInsets - .only(top: 8), - child: honors - .isNotEmpty - ? Column( + Container( + padding: + const EdgeInsets + .only( + top: 8), + child: honors + .isNotEmpty + ? Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const SizedBox( + height: + 8, + ), + const Text( + " honors: ", + style: + TextStyle(fontWeight: FontWeight.w600), + ), + Column( + children: honors + .map((Honor honor) => Text( + "-${honor.name!.trim()}", + style: Theme.of(context).textTheme.labelMedium, + )) + .toList(), + ), + ], + ) + : const SizedBox()), + program == null + ? const SizedBox() + : Column( mainAxisAlignment: MainAxisAlignment .start, @@ -238,104 +296,357 @@ class EducationScreen extends StatelessWidget { .start, children: [ const SizedBox( - height: 8, - ), - const Text( - " honors: ", - style: TextStyle( - fontWeight: - FontWeight.w600), - ), - Column( - children: honors - .map((Honor honor) => Text( - "-${honor.name!.trim()}", - style: Theme.of(context).textTheme.labelMedium, - )) - .toList(), + height: 5, ), + Text( + program), ], - ) - : const SizedBox()), - program == null - ? const SizedBox() - : Column( - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - const SizedBox( - height: 5, ), - Text(program), - ], - ), - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete -= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - context - .read() - .add(DeleteEducation( - educationalBackground: - state.educationalBackground[ - index], - profileId: - profileId, - token: token!)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - ////edit -= = = = = = = = =>> - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - context - .read() - .add(ShowEditEducationForm( - profileId: - profileId, - token: token!, - educationalBackground: - state.educationalBackground[ - index])); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), + ]), + ), + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, + () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + context + .read< + EducationBloc>() + .add(DeleteEducation( + educationalBackground: + state.educationalBackground[ + index], + profileId: + profileId, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit -= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + context + .read< + EducationBloc>() + .add(ShowEditEducationForm( + profileId: + profileId, + token: token!, + educationalBackground: + state.educationalBackground[ + index])); + } + if (value == 3) { + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + program ?? + level, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + school, + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "$periodFrom - $periodTo", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 12,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.educationalBackground[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons + .attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) + ), + ////Show Attachments + SizedBox( + child: state + .educationalBackground[ + index] + .attachments == + null || + state + .educationalBackground[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state + .educationalBackground[ + index] + .attachments != + null && + state + .educationalBackground[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: () { + confirmAlert( + context, + () {}, + "Delete?", + "Confirm Delete?"); + }, + attachment: state + .educationalBackground[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .educationalBackground[ + index] + .education! + .school! + .name!, + attachments: state + .educationalBackground[ + index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index e3ddd67..fc6b279 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -1,16 +1,24 @@ +import 'dart:io'; + import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/profile/attachment.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/screens/profile/components/eligibility/add_modal.dart'; import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; @@ -19,21 +27,27 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../utils/alerts.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; class EligibiltyScreen extends StatelessWidget { const EligibiltyScreen({super.key}); @override Widget build(BuildContext context) { + BuildContext parent = context; String? token; int? profileId; + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return WillPopScope( onWillPop: () async { return true; }, child: Scaffold( - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddEligibilityState ? const Text("Add Eligiblity") @@ -42,31 +56,37 @@ class EligibiltyScreen extends StatelessWidget { : const Text(elibilityScreenTitle), centerTitle: true, backgroundColor: primary, - actions: (context.watch().state is EligibilityLoaded) - - ? [ - AddLeading(onPressed: () { - context - .read() - .add(ShowAddEligibilityForm()); - }) - ] - :(context.watch().state is AddEligibilityState || context.watch().state is EditEligibilityState)? [ - CloseLeading(onPressed: () { - context.read().add(const LoadEligibility()); - }) - ]:[], + actions: + (context.watch().state is EligibilityLoaded) + ? [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddEligibilityForm()); + }) + ] + : (context.watch().state + is AddEligibilityState || + context.watch().state + is EditEligibilityState) + ? [ + CloseLeading(onPressed: () { + context + .read() + .add(const LoadEligibility()); + }) + ] + : [], ), body: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { token = state.userData!.user!.login!.token; - profileId = - state.userData!.user!.login!.user!.profileId; + profileId = state.userData!.user!.login!.user!.profileId; return BlocBuilder( - builder: (context, state) { - if(state is ProfileLoaded){ - return ProgressHUD( + builder: (context, state) { + if (state is ProfileLoaded) { + return ProgressHUD( padding: const EdgeInsets.all(24), indicatorWidget: const SpinKitFadingCircle( color: Colors.white, @@ -84,8 +104,7 @@ class EligibiltyScreen extends StatelessWidget { state is DeletedState || state is EligibilityAddedState || state is EligibilityEditedState || - state is EligibilityErrorState - ) { + state is EligibilityErrorState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -96,15 +115,17 @@ class EligibiltyScreen extends StatelessWidget { "Eligibility has been deleted successfully", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } else { errorAlert(context, "Deletion Failed", "Error deleting eligibility", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } } @@ -114,16 +135,18 @@ class EligibiltyScreen extends StatelessWidget { successAlert(context, "Adding Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } else { errorAlert(context, "Adding Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } } @@ -133,16 +156,18 @@ class EligibiltyScreen extends StatelessWidget { successAlert(context, "Update Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } else { errorAlert(context, "Update Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } } @@ -151,7 +176,11 @@ class EligibiltyScreen extends StatelessWidget { return BlocBuilder( builder: (context, state) { if (state is EligibilityLoaded) { - + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 3) { + attachmentCategories.add(cat); + } + } if (state.eligibilities.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -164,6 +193,7 @@ class EligibiltyScreen extends StatelessWidget { .eligibility! .title; return Column( + mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: @@ -180,6 +210,8 @@ class EligibiltyScreen extends StatelessWidget { children: [ Expanded( child: Column( + mainAxisSize: + MainAxisSize.min, mainAxisAlignment: MainAxisAlignment .start, @@ -196,9 +228,10 @@ class EligibiltyScreen extends StatelessWidget { .copyWith( fontWeight: FontWeight - .w500,color: primary), + .w500, + color: + primary), ), - const SizedBox( height: 5, ), @@ -212,11 +245,52 @@ class EligibiltyScreen extends StatelessWidget { height: 3, ), Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", style: Theme.of( context) .textTheme - .titleSmall) + .titleSmall), + const Divider(), + ////Show Attachments + SizedBox( + child: state.eligibilities[index].attachments == + null || + state + .eligibilities[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state.eligibilities[index].attachments != + null && + state.eligibilities[index].attachments!.length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: + () { + confirmAlert(context, + () { + parent.read().add(DeleteAttachment(attachment: state.eligibilities[index].attachments!.first, moduleId: state.eligibilities[index].attachments!.first.id.toString(), profileId: profileId.toString(), token: token!)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .eligibilities[index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .eligibilities[index] + .eligibility! + .title, + attachments: state + .eligibilities[index] + .attachments!, + )) ]), ), AppPopupMenu( @@ -224,29 +298,27 @@ class EligibiltyScreen extends StatelessWidget { const Offset(-10, -10), elevation: 3, onSelected: (value) { - ////delete eligibilty-= = = = = = = = =>> if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); BlocProvider.of< EligibilityBloc>( context) .add(DeleteEligibility( - - eligibilityId: state .eligibilities[ index] .id!, profileId: - profileId.toString(), + profileId + .toString(), token: token!)); }, "Delete?", @@ -254,11 +326,11 @@ class EligibiltyScreen extends StatelessWidget { } if (value == 1) { ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); EligibityCert eligibityCert = state.eligibilities[ @@ -278,27 +350,247 @@ class EligibiltyScreen extends StatelessWidget { overseas; context - .read() + .read< + EligibilityBloc>() .add(ShowEditEligibilityForm( eligibityCert: eligibityCert)); } + ////Attachment + if (value == 3) { + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + title, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 8,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.eligibilities[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), - - ], + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons + .attach_file), + ], icon: const Icon( Icons.more_vert, color: Colors.grey, @@ -310,7 +602,7 @@ class EligibiltyScreen extends StatelessWidget { ), const SizedBox( height: 5, - ) + ), ], ); }); @@ -322,17 +614,25 @@ class EligibiltyScreen extends StatelessWidget { } if (state is EditEligibilityState) { return EditEligibilityScreen( - profileId: profileId!, - token: token!, + profileId: profileId!, + token: token!, eligibityCert: state.eligibityCert); } if (state is AddEligibilityState) { - return AddEligibilityScreen(token: token!,profileId: profileId!,); + return AddEligibilityScreen( + token: token!, + profileId: profileId!, + ); } if (state is EligibilityErrorState) { - return SomethingWentWrong(message: state.message, onpressed: (){ - context.read().add(GetEligibilities(token: token!,profileId: profileId!)); - }); + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add( + GetEligibilities( + token: token!, + profileId: profileId!)); + }); } return Container( color: Colors.grey.shade200, @@ -342,11 +642,9 @@ class EligibiltyScreen extends StatelessWidget { }, ), ); - - } - return Container(); } - ); + return Container(); + }); } return Container(); }, diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index fc8df3b..c7cea00 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -1,11 +1,15 @@ import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -19,8 +23,13 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; import '../../../widgets/Leadings/close_leading.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; import 'learning_development/add_modal.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { @@ -32,7 +41,11 @@ class LearningAndDevelopmentScreen extends StatelessWidget { Widget build(BuildContext context) { String token; int profileId; + BuildContext parent = context; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return Scaffold( appBar: AppBar( title: context.watch().state @@ -167,9 +180,13 @@ class LearningAndDevelopmentScreen extends StatelessWidget { // TODO: implement listener }, builder: (context, state) { - print(state); + if (state is LearningDevelopmentLoadedState) { - + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 2) { + attachmentCategories.add(cat); + } + } if (state.learningsAndDevelopment.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -208,140 +225,403 @@ class LearningAndDevelopmentScreen extends StatelessWidget { padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), width: screenWidth, - child: Row( + child: Column( children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - training, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w600, - color: primary), - ), - const SizedBox( - height: 8, - ), - Text( - provider, - style: Theme.of(context) - .textTheme - .titleSmall, - ), - const SizedBox( - height: 5, - ), - Text( - "$duration: $start to $end", - style: Theme.of(context) - .textTheme - .labelMedium, - ), - const SizedBox( - height: 5, - ), - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete -= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - BlocProvider.of< - LearningDevelopmentBloc>( - context) - .add(DeleteLearningDevelopment( - trainingId: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .id!, - hours: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .totalHours!, - sponsorId: state - .learningsAndDevelopment[ - index] - .sponsoredBy - ?.id, - profileId: - profileId, - token: token)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - bool isOverseas; - ////edit = = = = = = = =>> - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + training, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w600, + color: primary), + ), + const SizedBox( + height: 8, + ), + Text( + provider, + style: Theme.of(context) + .textTheme + .titleSmall, + ), + const SizedBox( + height: 5, + ), + Text( + "$duration: $start to $end", + style: Theme.of(context) + .textTheme + .labelMedium, + ), + const SizedBox( + height: 5, + ), + ]), + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + BlocProvider.of< + LearningDevelopmentBloc>( + context) + .add(DeleteLearningDevelopment( + trainingId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .id!, + hours: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .totalHours!, + sponsorId: state + .learningsAndDevelopment[ + index] + .sponsoredBy + ?.id, + profileId: + profileId, + token: token)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + bool isOverseas; + ////edit = = = = = = = =>> + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); - if (state - .learningsAndDevelopment[ - index] - .conductedTraining - ?.venue - ?.cityMunicipality == - null) { - isOverseas = true; - } else { - isOverseas = false; - } - context - .read< - LearningDevelopmentBloc>() - .add(ShowEditLearningDevelopmentForm( - profileId: - profileId, - token: token, - learningDevelopment: - state.learningsAndDevelopment[ - index], - isOverseas: - isOverseas)); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), + if (state + .learningsAndDevelopment[ + index] + .conductedTraining + ?.venue + ?.cityMunicipality == + null) { + isOverseas = true; + } else { + isOverseas = false; + } + context + .read< + LearningDevelopmentBloc>() + .add(ShowEditLearningDevelopmentForm( + profileId: + profileId, + token: token, + learningDevelopment: + state.learningsAndDevelopment[ + index], + isOverseas: + isOverseas)); + } + if(value == 3){ + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + training, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + provider, + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "$start TO $end", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + + width: double.infinity, + child: Row( + children: [ + Flexible( + flex: 1, + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 12,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.learningsAndDevelopment[index].conductedTraining!.id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons.attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) + ), + SizedBox( + child: state.learningsAndDevelopment[index].attachments == + null || + state + .learningsAndDevelopment[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state.learningsAndDevelopment[index].attachments != + null && + state.learningsAndDevelopment[index].attachments!.length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: + () { + confirmAlert(context, + () { + + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .learningsAndDevelopment[index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .learningsAndDevelopment[index] + .conductedTraining!.title!.title!, + + attachments: state + .learningsAndDevelopment[index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 5372185..153f851 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,10 +1,14 @@ import 'dart:io'; import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -19,9 +23,14 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/profile/work_history.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; class WorkHistoryScreen extends StatelessWidget { const WorkHistoryScreen({super.key}); @@ -29,8 +38,12 @@ class WorkHistoryScreen extends StatelessWidget { @override Widget build(BuildContext context) { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + BuildContext parent = context; String? token; - int profileId; + int? profileId; + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return Scaffold( appBar: AppBar( title: context.watch().state is AddWorkHistoryState @@ -160,6 +173,11 @@ class WorkHistoryScreen extends StatelessWidget { }, builder: (context, state) { if (state is WorkHistoryLoaded) { + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 4) { + attachmentCategories.add(cat); + } + } if (state.workExperiences.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -183,116 +201,421 @@ class WorkHistoryScreen extends StatelessWidget { .workExperiences[index] .toDate!); return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ Container( width: screenWidth, decoration: box1(), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), - child: Row(children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - position, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight.w600, - color: primary), - ), - const SizedBox( - height: 8, - ), - Text( - agency, - style: Theme.of(context) - .textTheme - .titleSmall! - .copyWith( - fontWeight: - FontWeight.w500), - ), - const SizedBox( - height: 5, - ), - Text( - "$from - $to ", - style: Theme.of(context) - .textTheme - .labelMedium, - ), - ], - )), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete workhistory-= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - BlocProvider.of< - WorkHistoryBloc>( - context) - .add(DeleteWorkHistory( - profileId: profileId, - token: token!, - workHistory: - state.workExperiences[ + child: Column( + children: [ + Row(children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + position, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w600, + color: primary), + ), + const SizedBox( + height: 8, + ), + Text( + agency, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const SizedBox( + height: 5, + ), + Text( + "$from - $to ", + style: Theme.of(context) + .textTheme + .labelMedium, + ), + ], + )), + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete workhistory-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + BlocProvider.of< + WorkHistoryBloc>( + context) + .add( + DeleteWorkHistory( + profileId: profileId!, + token: token!, + workHistory: state + .workExperiences[ index], - )); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - WorkHistory workHistory = - state.workExperiences[ - index]; - context - .read() - .add( - ShowEditWorkHistoryForm( + )); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + WorkHistory workHistory = + state.workExperiences[ + index]; + context + .read< + WorkHistoryBloc>() + .add(ShowEditWorkHistoryForm( workHistory: workHistory)); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), - - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) - ]), + } + ////Attachment + if (value == 3) { + showDialog( + context: context, + builder: (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: const Icon( + Icons.file_copy, + size: 32, + color: primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: (context, + setState) { + return Padding( + padding: + const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const Divider(), + Text( + position, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.w600, color: primary), + ), + const SizedBox( + height: + 8, + ), + Text( + agency, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith(fontWeight: FontWeight.w500), + ), + const SizedBox( + height: + 5, + ), + Text( + "$from - $to ", + style: Theme.of(context) + .textTheme + .labelMedium, + ), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .always, + decoration: normalTextFieldStyle( + "attachment category", "attachment category"), + name: + 'attachments_categorues', + validator: + FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: Theme.of(context) + .textTheme + .bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: Theme.of(context) + .textTheme + .bodySmall, + ), + const Divider(), + ElevatedButton( + style: + ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: + () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: + 100, + width: + double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 12,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.workExperiences[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: + Icons.attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ]), + const Divider(), + ////Show Attachments + SizedBox( + child: state + .workExperiences[ + index] + .attachments == + null || + state + .workExperiences[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state + .workExperiences[ + index] + .attachments != + null && + state + .workExperiences[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: () { + confirmAlert( + context, + () {}, + "Delete?", + "Confirm Delete?"); + }, + attachment: state + .workExperiences[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .workExperiences[ + index] + .position! + .title!, + attachments: state + .workExperiences[ + index] + .attachments!, + )) + ], + ), ), const SizedBox( height: 5, @@ -308,13 +631,13 @@ class WorkHistoryScreen extends StatelessWidget { } if (state is AddWorkHistoryState) { return AddWorkHistoryScreen( - profileId: profileId, + profileId: profileId!, token: token!, ); } if (state is EditWorkHistoryState) { return EditWorkHistoryScreen( - profileId: profileId, + profileId: profileId!, token: token!, ); } @@ -324,7 +647,8 @@ class WorkHistoryScreen extends StatelessWidget { onpressed: () { context.read().add( GetWorkHistories( - profileId: profileId, token: token!)); + profileId: profileId!, + token: token!)); }); } return Container(); diff --git a/lib/screens/profile/shared/multiple_attachment.dart b/lib/screens/profile/shared/multiple_attachment.dart new file mode 100644 index 0000000..a818bf7 --- /dev/null +++ b/lib/screens/profile/shared/multiple_attachment.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/entypo_icons.dart'; + +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../utils/global.dart'; + +class MultipleAttachments extends StatelessWidget { + final List attachments; + final String eligibilityName; + const MultipleAttachments({ + super.key, + required this.attachments, + required this.eligibilityName + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Flexible( + flex: + 2, + child: Container( + padding: const EdgeInsets.all(5), + decoration: box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: Text( + attachments.first.filename!, + overflow: TextOverflow.ellipsis, + ))), + const SizedBox( + width: + 8, + ), + Flexible( + child: + FittedBox( + child: + Container( + padding: + const EdgeInsets.all(3), + decoration: + box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: + InkWell( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("$eligibilityName Attachments",textAlign: TextAlign.center,), + content: Column( + mainAxisSize: MainAxisSize.min, + children: attachments.map((e) { + String ext = e.filename!.substring(e.filename!.lastIndexOf(".")); + + return Column( + children: [ + Row( + children: [ + Flexible( + child: SizedBox( + child: ext == '.pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: true, + ) + : ext == '.png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 5.5, + allowDrawingOutsideViewBox: true, + ) + : ext == '.jpg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: true, + ) + : SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: true, + ), + ), + ), + const SizedBox(width: 8,), + Flexible( + flex: 4, + child: Tooltip( + message: e.filename, + child: Text( + e.filename!, + overflow: TextOverflow.ellipsis, + )), + ), + const SizedBox(width: 8,), + Flexible( + child: Row( + children: [ + Flexible( + child: IconButton( + icon: const Icon( + Entypo.right_open_mini, + color: Colors.black87, + ), + onPressed: () {}, + ), + ), + const SizedBox(width: 8,), + Flexible( + child: IconButton( + icon: const Icon( + Icons.delete, + color: primary, + ), + onPressed: () {}, + ), + ), + ], + ), + ), + ], + ), + const Divider() + ], + ); + }).toList(), + )); + }); + }, + child: Row( + children: const [ + Text(" See more.."), + Icon( + Icons.keyboard_arrow_right, + color: Colors.black54, + ), + ], + ), + ), + ), + )), + ], + ); + } +} diff --git a/lib/screens/profile/shared/single_attachment.dart b/lib/screens/profile/shared/single_attachment.dart new file mode 100644 index 0000000..92f46e7 --- /dev/null +++ b/lib/screens/profile/shared/single_attachment.dart @@ -0,0 +1,49 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../utils/alerts.dart'; + +class SingleAttachment extends StatelessWidget { + final Function()? onpressed; + final Attachment attachment; + const SingleAttachment({ + required this.attachment, + required this.onpressed, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: + const EdgeInsets.all( + 5), + decoration: box1().copyWith( + color: Colors + .grey + .shade300, + boxShadow: []), + child: + Row( + children: [ + Expanded( + child: + Text( + attachment.filename!, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox( + width: + 8, + ), + GestureDetector( + onTap:onpressed, + child: const Icon(Icons.delete,color: primary,)) + ], + )); + } +} \ No newline at end of file diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart index adfdb8d..9738e8a 100644 --- a/lib/sevices/profile/eligibility_services.dart +++ b/lib/sevices/profile/eligibility_services.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:unit2/model/profile/attachment.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; @@ -8,33 +9,33 @@ import 'package:http/http.dart' as http; class EligibilityService { static final EligibilityService _instance = EligibilityService(); static EligibilityService get instance => _instance; - - - Future> getEligibilities(int profileId, String token)async{ - List eligibilities = []; + + Future> getEligibilities( + int profileId, String token) async { + List eligibilities = []; String authToken = "Token $token"; - String path = "${Url.instance.getEligibilities()}$profileId/"; - Map headers = { + String path = "${Url.instance.getEligibilities()}$profileId/"; + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken - }; - try{ - http.Response response = await Request.instance.getRequest(path: path,headers: headers,param: {}); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); - if (data['data']!= null) { - data['data'].forEach((var cert) { - EligibityCert eligibility = EligibityCert.fromJson(cert); - eligibilities.add(eligibility); - }); - } + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var cert) { + EligibityCert eligibility = EligibityCert.fromJson(cert); + eligibilities.add(eligibility); + }); } - }catch(e){ - throw e.toString(); } - return eligibilities; + } catch (e) { + throw e.toString(); + } + return eligibilities; } Future delete( @@ -50,18 +51,17 @@ class EligibilityService { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken }; - try{ - http.Response response = await Request.instance - .deleteRequest(path: path, headers: headers, body: body, param: params); - if (response.statusCode == 200) { - Map data = jsonDecode(response.body); - success = data['success']; - } else { - success = false; - } - - }catch(e){ - throw(e.toString()); + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + } catch (e) { + throw (e.toString()); } return success!; } @@ -70,7 +70,7 @@ class EligibilityService { {required EligibityCert eligibityCert, required String token, required int profileId}) async { - Map? _response={}; + Map? _response = {}; String authtoken = "Token $token"; String path = '${Url.instance.addEligibility()}$profileId/'; Map headers = { @@ -94,7 +94,7 @@ class EligibilityService { Map data = jsonDecode(response.body); _response = data; } else { - _response.addAll({'success':false}); + _response.addAll({'success': false}); } return _response; @@ -103,11 +103,12 @@ class EligibilityService { } } - Future> update( + Future> update( {required EligibityCert eligibityCert, required String token, - required int profileId, required int oldEligibility}) async { - Map? response={}; + required int profileId, + required int oldEligibility}) async { + Map? response = {}; String authtoken = "Token $token"; String path = '${Url.instance.addEligibility()}$profileId/'; Map headers = { @@ -132,7 +133,7 @@ class EligibilityService { Map data = jsonDecode(res.body); response = data; } else { - response.addAll({'success':false}); + response.addAll({'success': false}); } return response; @@ -140,4 +141,63 @@ class EligibilityService { throw e.toString(); } } + + + + Future deleteAttachment( + {required Attachment attachment, + required int moduleId, + required String profileId, + required String token}) async { + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.attachments()}$profileId/"; + Map? body; + try{ + body = { + "attachment_module": moduleId, + attachment: [ + { + "id": attachment.id, + "created_at": attachment.createdAt, + "source": attachment.source, + "filename": attachment.filename, + "category": { + "id": attachment.category?.id, + "subclass": { + "id": attachment.category?.subclass.id, + "name": attachment.category?.subclass.name, + "attachment_class": { + "id": attachment.category?.subclass.attachmentClass.id, + "name": attachment.category?.subclass.attachmentClass.name + } + }, + "description": attachment.category?.description + } + } + ] + }; + }catch(e){ + print("body error"+e.toString()); + } + + Map params = {"force_mode": "true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + // try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + // } catch (e) { + // throw (e.toString()); + // } + return success!; + } } diff --git a/lib/utils/attachment_categories.dart b/lib/utils/attachment_categories.dart new file mode 100644 index 0000000..cce37ff --- /dev/null +++ b/lib/utils/attachment_categories.dart @@ -0,0 +1,77 @@ +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; + +import '../model/profile/attachment.dart'; +import 'package:http/http.dart' as http; + +class AttachmentServices { + static final AttachmentServices _instance = AttachmentServices(); + static AttachmentServices get instance => _instance; + + Future> getCategories() async { + List attachmentCategories = []; + String path = Url.instance.attachmentCategories(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + for (var cat in data['data']) { + AttachmentCategory newCat = AttachmentCategory.fromJson(cat); + attachmentCategories.add(newCat); + } + } + } catch (e) { + throw e.toString(); + } + return attachmentCategories; + } + + Future> attachment( + {required String categoryId, + required String module, + required List paths, + required String token, + required String profileId}) async { + String authtoken = "Token $token"; + Map headers = {'Authorization': authtoken}; + String path = Url.instance.attachments(); + Map? response = {}; + Map body = { + "attachment_category_id": categoryId.toString(), + "attachment_module": module.toString() + }; + + try { + var request = http.MultipartRequest( + 'POST', Uri.parse('http://${Url.instance.host()}$path$profileId/')); + request.fields.addAll(body); + request.headers.addAll(headers); + paths.forEach((element) async { + request.files + .add(await http.MultipartFile.fromPath('attachments', element)); + }); + + http.StreamedResponse res = await request.send(); + final steamResponse = await res.stream.bytesToString(); + Map data = jsonDecode(steamResponse); + if (res.statusCode == 201) { + response = data; + } else { + String message = data['response']['details']; + response.addAll({'message': message}); + response.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return response; + } +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 9669a61..bd6932e 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -167,6 +167,9 @@ class Url { String getServiceTypes() { return "/api/jobnet_app/comm_service_type/"; } + String attachments(){ + return "/api/jobnet_app/profile/attachment/"; + } //// address path String addressPath() { @@ -319,4 +322,7 @@ class Url { String getAddressCategory() { return "/api/jobnet_app/address_categories/"; } + String attachmentCategories(){ + return "/api/jobnet_app/attachment_categories/"; + } } diff --git a/pubspec.lock b/pubspec.lock index 9004766..fd4f759 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -449,6 +449,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf" + url: "https://pub.dev" + source: hosted + version: "5.3.1" file_utils: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fcaa5d5..c5a2cc0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -86,6 +86,7 @@ dependencies: flutter_staggered_animations: ^1.1.1 group_list_view: ^1.1.1 search_page: ^2.3.0 + file_picker: ^5.3.1 dev_dependencies: From 018f3760a5a16f5ceb2e5d9e65ec8430baec3968 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 7 Aug 2023 14:33:38 +0800 Subject: [PATCH 2/3] Adding attachments feature for profile --- .../profile/education/education_bloc.dart | 62 +- .../profile/education/education_event.dart | 13 +- .../profile/education/education_state.dart | 21 +- .../profile/eligibility/eligibility_bloc.dart | 53 +- .../eligibility/eligibility_event.dart | 11 +- .../eligibility/eligibility_state.dart | 18 +- .../learning_development_bloc.dart | 37 +- .../learning_development_event.dart | 15 +- .../learning_development_state.dart | 24 +- .../voluntary_works/voluntary_work_state.dart | 6 + .../profile/workHistory/workHistory_bloc.dart | 59 +- .../workHistory/workHistory_event.dart | 16 +- .../workHistory/workHistory_state.dart | 19 + lib/main.dart | 11 - lib/model/profile/attachment.dart | 28 +- lib/model/profile/educational_background.dart | 2 +- lib/model/profile/eligibility.dart | 2 +- lib/model/profile/learning_development.dart | 2 +- lib/model/profile/work_history.dart | 2 +- .../profile/components/education_screen.dart | 185 +++- .../components/eligibility_screen.dart | 857 ++++++++++-------- .../learning_and_development_screen.dart | 692 ++++++++------ .../components/work_history_screen.dart | 162 +++- .../profile/shared/multiple_attachment.dart | 332 ++++--- .../profile/shared/single_attachment.dart | 6 +- lib/sevices/profile/eligibility_services.dart | 8 +- ...tegories.dart => attachment_services.dart} | 55 +- lib/utils/urls.dart | 6 +- 28 files changed, 1741 insertions(+), 963 deletions(-) rename lib/utils/{attachment_categories.dart => attachment_services.dart} (58%) diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index cb43d54..0117b6d 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -4,7 +4,7 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/sevices/profile/education_services.dart'; import '../../../model/profile/attachment.dart'; -import '../../../utils/attachment_categories.dart'; +import '../../../utils/attachment_services.dart'; part 'education_event.dart'; part 'education_state.dart'; @@ -14,12 +14,12 @@ class EducationBloc extends Bloc { List schools = []; List programs = []; List honors = []; - List attachmentCategories = []; + List attachmentCategories = []; EducationBloc() : super(EducationInitial()) { on((event, emit) async { emit(EducationalBackgroundLoadingState()); try { - if (attachmentCategories.isEmpty) { + if (attachmentCategories.isEmpty) { attachmentCategories = await AttachmentServices.instance.getCategories(); } @@ -28,11 +28,13 @@ class EducationBloc extends Bloc { .instace .getEducationalBackground(event.profileId, event.token); educationalBackgrounds = educations; - emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds, attachmentCategory: attachmentCategories)); + emit(EducationalBackgroundLoadedState( + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); } else { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); } } catch (e) { emit(EducationalBackgroundErrorState(message: e.toString())); @@ -85,7 +87,8 @@ class EducationBloc extends Bloc { token: event.token, profileId: event.profileId); if (status['success']) { - educationalBackgrounds.removeWhere((element) => event.educationalBackground.id == element.id); + educationalBackgrounds.removeWhere( + (element) => event.educationalBackground.id == element.id); EducationalBackground educationalBackground = EducationalBackground.fromJson(status['data']); educationalBackgrounds.add(educationalBackground); @@ -97,7 +100,8 @@ class EducationBloc extends Bloc { ////LOAD on((event, emit) { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); }); //// SHOW EDIT FORM on((event, emit) async { @@ -143,8 +147,12 @@ class EducationBloc extends Bloc { } }); ////Add attachment - on((event, emit) async { + on((event, emit) async { emit(EducationalBackgroundLoadingState()); + EducationalBackground educationalBackground = + educationalBackgrounds.firstWhere( + (element) => element.id.toString() == event.attachmentModule); + List attachments = []; try { Map status = await AttachmentServices.instance .attachment( @@ -153,7 +161,18 @@ class EducationBloc extends Bloc { paths: event.filePaths, token: event.token, profileId: event.profileId); + if (status['success']) { + status['data'].forEach((element) { + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + educationalBackground.attachments == null + ? educationalBackground.attachments = attachments + : educationalBackground.attachments = [ + ...educationalBackground.attachments!, + ...attachments + ]; emit(EducationAddedState(response: status)); } else { emit(EducationAddedState(response: status)); @@ -162,5 +181,30 @@ class EducationBloc extends Bloc { emit(EducationalBackgroundErrorState(message: e.toString())); } }); + on((event, emit) async { + emit(EducationalBackgroundLoadingState()); + try { + final bool success = await AttachmentServices.instance.deleteAttachment( + attachment: event.attachment, + moduleId: event.moduleId, + profileId: event.profileId.toString(), + token: event.token); + if (success) { + final EducationalBackground educationalBackground = + educationalBackgrounds + .firstWhere((element) => element.id == event.moduleId); + educationalBackground.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + educationalBackgrounds + .removeWhere((element) => element.id == event.moduleId); + educationalBackgrounds.add(educationalBackground); + emit(EducationAttachmentDeletedState(success: success)); + } else { + emit(EducationAttachmentDeletedState(success: success)); + } + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/education/education_event.dart b/lib/bloc/profile/education/education_event.dart index 667befb..9b68a0f 100644 --- a/lib/bloc/profile/education/education_event.dart +++ b/lib/bloc/profile/education/education_event.dart @@ -64,13 +64,22 @@ class DeleteEducation extends EducationEvent{ List get props => [educationalBackground, profileId, token]; } ////Add attachment -class AddAttachment extends EducationEvent{ +class AddEducationAttachment extends EducationEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddEducationAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } + +////Delete Attachment +class DeleteEducationAttachment extends EducationEvent{ + final int moduleId; + final Attachment attachment; + final String token; + final int profileId; + const DeleteEducationAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); +} diff --git a/lib/bloc/profile/education/education_state.dart b/lib/bloc/profile/education/education_state.dart index a8bebe0..3cfe505 100644 --- a/lib/bloc/profile/education/education_state.dart +++ b/lib/bloc/profile/education/education_state.dart @@ -10,9 +10,10 @@ abstract class EducationState extends Equatable { class EducationInitial extends EducationState {} class EducationalBackgroundLoadedState extends EducationState { - final List< AttachmentCategory> attachmentCategory; + final List attachmentCategory; final List educationalBackground; - const EducationalBackgroundLoadedState({required this.educationalBackground, required this.attachmentCategory}); + const EducationalBackgroundLoadedState( + {required this.educationalBackground, required this.attachmentCategory}); @override List get props => [educationalBackground]; } @@ -58,6 +59,8 @@ class EducationAddedState extends EducationState { @override List get props => [response]; } + + //// Edited State class EditedEducationState extends EducationState { final Map response; @@ -73,3 +76,17 @@ class EducationDeletedState extends EducationState { @override List get props => [success]; } + +////Attachment AddedState +class EducationAttachmentAddedState extends EducationState { + final Map response; + const EducationAttachmentAddedState({required this.response}); +} + +////Attachment Deleted State State +class EducationAttachmentDeletedState extends EducationState { + final bool success; + const EducationAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} \ No newline at end of file diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index c47b309..1be36b4 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -1,6 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/utils/attachment_categories.dart'; +import 'package:unit2/utils/attachment_services.dart'; import '../../../model/location/city.dart'; import '../../../model/location/country.dart'; import '../../../model/location/provinces.dart'; @@ -39,11 +39,11 @@ class EligibilityBloc extends Bloc { if (success) { eligibilities.removeWhere( ((EligibityCert element) => element.id == event.eligibilityId)); - emit(DeletedState( + emit(EligibilityDeletedState( success: success, )); } else { - emit(DeletedState(success: success)); + emit(EligibilityDeletedState(success: success)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); @@ -222,8 +222,11 @@ class EligibilityBloc extends Bloc { message: "Something went wrong. Please try again")); }); ////Add attachment - on((event, emit) async { + on((event, emit) async { emit(EligibilityLoadingState()); + List attachments = []; + EligibityCert eligibityCert = eligibilities.firstWhere( + (element) => element.id.toString() == event.attachmentModule); try { Map status = await AttachmentServices.instance .attachment( @@ -233,32 +236,44 @@ class EligibilityBloc extends Bloc { token: event.token, profileId: event.profileId); if (status['success']) { - emit(EligibilityAddedState(response: status)); + status['data'].forEach((element) { + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + eligibityCert.attachments == null + ? eligibityCert.attachments = attachments + : eligibityCert.attachments = [ + ...eligibityCert.attachments!, + ...attachments + ]; + emit(EligibilityAttachmentAddedState(response: status)); } else { - emit(EligibilityAddedState(response: status)); + emit(EligibilityAttachmentAddedState(response: status)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); } }); - - on((event, emit) async { + + on((event, emit) async { + emit(EligibilityLoadingState()); // try { - final bool success = await EligibilityService.instance.deleteAttachment( - moduleId: int.parse(event.moduleId), + final bool success = await AttachmentServices.instance.deleteAttachment( attachment: event.attachment, - profileId: event.profileId, + moduleId: int.parse(event.moduleId), + profileId: event.profileId.toString(), token: event.token); if (success) { - EligibityCert eligibityCert = eligibilities.firstWhere((element) =>element.id.toString() == event.moduleId); - eligibityCert.attachments!.removeWhere((element){ - return event.attachment.id == element.id; - }); - emit(DeletedState( - success: success, - )); + final EligibityCert eligibityCert = eligibilities + .firstWhere((element) => element.id.toString() == event.moduleId); + eligibityCert.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + eligibilities.removeWhere( + (element) => element.id.toString() == event.moduleId); + eligibilities.add(eligibityCert); + emit(EligibilitytAttachmentDeletedState(success: success)); } else { - emit(DeletedState(success: success)); + emit(EligibilitytAttachmentDeletedState(success: success)); } // } catch (e) { // emit(EligibilityErrorState(message: e.toString())); diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index 8d17236..a9b6ca8 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -68,24 +68,25 @@ class DeleteEligibility extends EligibilityEvent { class CallErrorState extends EligibilityEvent{ } - -class AddAttachment extends EligibilityEvent{ +////Add Attachment +class AddEligibiltyAttachment extends EligibilityEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddEligibiltyAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } -class DeleteAttachment extends EligibilityEvent{ +////Delete Attachment +class DeleteEligibyAttachment extends EligibilityEvent{ final String profileId; final String token; final Attachment attachment; final String moduleId; - const DeleteAttachment({required this.attachment,required this.moduleId, required this.profileId, required this.token}); + const DeleteEligibyAttachment({required this.attachment,required this.moduleId, required this.profileId, required this.token}); } diff --git a/lib/bloc/profile/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart index 3a1994a..7d746d9 100644 --- a/lib/bloc/profile/eligibility/eligibility_state.dart +++ b/lib/bloc/profile/eligibility/eligibility_state.dart @@ -42,9 +42,9 @@ class EditEligibilityState extends EligibilityState { [isOverseas, eligibityCert, eligibilities, regions, countries]; } -class DeletedState extends EligibilityState { +class EligibilityDeletedState extends EligibilityState { final bool success; - const DeletedState({required this.success}); + const EligibilityDeletedState({required this.success}); @override List get props => [success]; } @@ -93,3 +93,17 @@ class EligibilityLoaded extends EligibilityState { @override List get props => [eligibilities]; } + +////Attachment AddedState +class EligibilityAttachmentAddedState extends EligibilityState { + final Map response; + const EligibilityAttachmentAddedState({required this.response}); +} + +////Attachment Deleted State State +class EligibilitytAttachmentDeletedState extends EligibilityState { + final bool success; + const EligibilitytAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index e7c145d..465cefb 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -10,7 +10,7 @@ import '../../../model/profile/attachment.dart'; import '../../../model/profile/learning_development.dart'; import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; -import '../../../utils/attachment_categories.dart'; +import '../../../utils/attachment_services.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; part 'learning_development_event.dart'; @@ -273,8 +273,10 @@ class LearningDevelopmentBloc emit(LearningDevelopmentErrorState(message: event.message)); }); ////Add attachment - on((event, emit) async { + on((event, emit) async { emit(LearningDevelopmentLoadingState()); + List attachments = []; + LearningDevelopement learningDevelopement = learningsAndDevelopments.firstWhere((element) => element.conductedTraining!.id.toString () == event.attachmentModule); try { Map status = await AttachmentServices.instance .attachment( @@ -284,6 +286,11 @@ class LearningDevelopmentBloc token: event.token, profileId: event.profileId); if (status['success']) { + status['data'].forEach((element){ + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + learningDevelopement.attachments == null? learningDevelopement.attachments = attachments:learningDevelopement.attachments = [...learningDevelopement.attachments!,...attachments]; emit(LearningDevelopmentAddedState(response: status)); } else { emit(LearningDevelopmentAddedState(response: status)); @@ -292,5 +299,31 @@ class LearningDevelopmentBloc emit(LearningDevelopmentErrorState(message: e.toString())); } }); + ////Delete Attachment + on((event, emit) async { + emit(LearningDevelopmentLoadingState()); + try { + final bool success = await AttachmentServices.instance.deleteAttachment( + attachment: event.attachment, + moduleId: event.moduleId, + profileId: event.profileId.toString(), + token: event.token); + if (success) { + final LearningDevelopement learningDevelopement = + learningsAndDevelopments + .firstWhere((element) => element.conductedTraining!.id == event.moduleId); + learningDevelopement.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + learningsAndDevelopments + .removeWhere((element) => element.conductedTraining!.id == event.moduleId); + learningsAndDevelopments.add(learningDevelopement); + emit(LearningDevAttachmentDeletedState(success: success)); + } else { + emit(LearningDevAttachmentDeletedState(success: success)); + } + } catch (e) { + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart index cfde6bb..c8528ac 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -67,13 +67,24 @@ class CallErrorState extends LearningDevelopmentEvent{ const CallErrorState({required this.message}); } -class AddAttachment extends LearningDevelopmentEvent{ +////Add Attachment +class AddALearningDevttachment extends LearningDevelopmentEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddALearningDevttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } + +//// Delete Attachment +class DeleteLearningDevAttachment extends LearningDevelopmentEvent{ + final int moduleId; + final Attachment attachment; + final String token; + final int profileId; + const DeleteLearningDevAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); +} + diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index 323d076..2699470 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -10,9 +10,11 @@ abstract class LearningDevelopmentState extends Equatable { class LearningDevelopmentInitial extends LearningDevelopmentState {} class LearningDevelopmentLoadedState extends LearningDevelopmentState { - final List< AttachmentCategory> attachmentCategory; + final List attachmentCategory; final List learningsAndDevelopment; - const LearningDevelopmentLoadedState({required this.learningsAndDevelopment, required this.attachmentCategory}); + const LearningDevelopmentLoadedState( + {required this.learningsAndDevelopment, + required this.attachmentCategory}); @override List get props => [learningsAndDevelopment]; } @@ -113,3 +115,21 @@ class LearningDevelopmentErrorState extends LearningDevelopmentState { final String message; const LearningDevelopmentErrorState({required this.message}); } + +////Attachment AddedState +class LearningDevAttachmentAddedState extends LearningDevelopmentState { + final Map response; + const LearningDevAttachmentAddedState({required this.response}); + @override + List get props => [response]; +} + + + +////Attachment Deleted State State +class LearningDevAttachmentDeletedState extends LearningDevelopmentState { + final bool success; + const LearningDevAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart index efa0fd7..4c3fc88 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart @@ -60,6 +60,7 @@ class EditVoluntaryWorks extends VoluntaryWorkState{ } + ////Adding State class AddVoluntaryWorkState extends VoluntaryWorkState{ final List positions; @@ -71,6 +72,11 @@ class AddVoluntaryWorkState extends VoluntaryWorkState{ @override List get props => [positions,agencies,countries,regions]; } +////Add Attachment +class AttachmentAddedState extends VoluntaryWorkState { + final Map response; + const AttachmentAddedState({required this.response}); +} //// Deleted State class VoluntaryWorkDeletedState extends VoluntaryWorkState{ final bool success; diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 71f81ce..e9a44c8 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -10,7 +10,7 @@ import 'package:unit2/utils/profile_utilities.dart'; import '../../../model/profile/attachment.dart'; import '../../../model/utils/category.dart'; -import '../../../utils/attachment_categories.dart'; +import '../../../utils/attachment_services.dart'; part 'workHistory_event.dart'; part 'workHistory_state.dart'; @@ -21,13 +21,13 @@ class WorkHistoryBloc extends Bloc { List agencies = []; List appointmentStatus = []; List agencyCategory = []; - List attachmentCategories = []; + List attachmentCategories = []; WorkHistoryBloc() : super(EducationInitial()) { ////GET WORK HISTORIES on((event, emit) async { emit(WorkHistoryLoadingState()); try { - if (attachmentCategories.isEmpty) { + if (attachmentCategories.isEmpty) { attachmentCategories = await AttachmentServices.instance.getCategories(); } @@ -36,7 +36,9 @@ class WorkHistoryBloc extends Bloc { .getWorkExperiences(event.profileId, event.token); workExperiences = works; } - emit(WorkHistoryLoaded(workExperiences: workExperiences, attachmentCategory: attachmentCategories)); + emit(WorkHistoryLoaded( + workExperiences: workExperiences, + attachmentCategory: attachmentCategories)); } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); } @@ -44,7 +46,9 @@ class WorkHistoryBloc extends Bloc { ///// LOAD WORK HISTORIES on((event, emit) { emit(WorkHistoryLoadingState()); - emit(WorkHistoryLoaded(workExperiences: workExperiences,attachmentCategory: attachmentCategories)); + emit(WorkHistoryLoaded( + workExperiences: workExperiences, + attachmentCategory: attachmentCategories)); }); ////DELETE on((event, emit) async { @@ -190,8 +194,11 @@ class WorkHistoryBloc extends Bloc { } }); ////Add Attachment - on((event, emit) async { + on((event, emit) async { emit(WorkHistoryLoadingState()); + List attachments = []; + WorkHistory workHistory = workExperiences.firstWhere( + (element) => element.id.toString() == event.attachmentModule); try { Map status = await AttachmentServices.instance .attachment( @@ -201,9 +208,45 @@ class WorkHistoryBloc extends Bloc { token: event.token, profileId: event.profileId); if (status['success']) { - emit(WorkHistoryAddedState(response: status)); + status['data'].forEach((element) { + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + workHistory.attachments == null + ? workHistory.attachments = attachments + : workHistory.attachments = [ + ...workHistory.attachments!, + ...attachments + ]; + emit(WorkHistoryDevAttachmentAddedState(response: status)); } else { - emit(WorkHistoryAddedState(response: status)); + emit(WorkHistoryDevAttachmentAddedState(response: status)); + } + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); + ////Delete Attachment + on((event, emit) async { + emit(WorkHistoryLoadingState()); + try { + final bool success = await AttachmentServices.instance.deleteAttachment( + attachment: event.attachment, + moduleId: event.moduleId, + profileId: event.profileId.toString(), + token: event.token); + if (success) { + final WorkHistory workHistory = + workExperiences + .firstWhere((element) => element.id == event.moduleId); + workHistory.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + workExperiences + .removeWhere((element) => element.id == event.moduleId); + workExperiences.add(workHistory); + emit(WorkHistoryDevAttachmentDeletedState(success: success)); + } else { + emit(WorkHistoryDevAttachmentDeletedState(success: success)); } } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index ebc1efd..1895bfe 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -60,15 +60,27 @@ class AddWorkHostory extends WorkHistorytEvent{ @override List get props => [workHistory,profileId,token,isPrivate]; } -class AddAttachment extends WorkHistorytEvent{ + +////Add Attachment +class AddWorkHistoryAttachment extends WorkHistorytEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddWorkHistoryAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } +////Delete Attachment +class DeleteWorkHistoryAttachment extends WorkHistorytEvent{ + final int moduleId; + final Attachment attachment; + final String token; + final int profileId; + const DeleteWorkHistoryAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); +} + + diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index d5905a8..117d984 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -73,3 +73,22 @@ class WorkHistoryAddedState extends WorkHistoryState{ @override List get props => [response]; } + + +////Attachment AddedState +class WorkHistoryDevAttachmentAddedState extends WorkHistoryState { + final Map response; + const WorkHistoryDevAttachmentAddedState({required this.response}); + @override + List get props => [response]; +} + + + +////Attachment Deleted State State +class WorkHistoryDevAttachmentDeletedState extends WorkHistoryState { + final bool success; + const WorkHistoryDevAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 5144a73..d88fa6c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,24 +1,14 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:hive/hive.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/app_router.dart'; import 'package:unit2/utils/global_context.dart'; -import 'package:unit2/utils/global_context.dart'; import 'package:path_provider/path_provider.dart' as path_provider; -import './utils/router.dart'; import './utils/global.dart'; - - Future main() async { WidgetsFlutterBinding.ensureInitialized(); var appDirectory = await path_provider.getApplicationDocumentsDirectory(); @@ -92,4 +82,3 @@ class MyApp extends StatelessWidget { ); } } - diff --git a/lib/model/profile/attachment.dart b/lib/model/profile/attachment.dart index 17169e2..f0676f8 100644 --- a/lib/model/profile/attachment.dart +++ b/lib/model/profile/attachment.dart @@ -1,8 +1,4 @@ -// To parse this JSON data, do -// -// final attachment = attachmentFromJson(jsonString); -import 'package:meta/meta.dart'; import 'dart:convert'; Attachment attachmentFromJson(String str) => @@ -53,9 +49,9 @@ class Attachment { } class AttachmentCategory { - final int id; - final Subclass subclass; - final String description; + final int? id; + final Subclass? subclass; + final String? description; AttachmentCategory({ required this.id, @@ -65,21 +61,21 @@ class AttachmentCategory { factory AttachmentCategory.fromJson(Map json) => AttachmentCategory( id: json["id"], - subclass: Subclass.fromJson(json["subclass"]), + subclass:json['subclass'] == null? null: Subclass.fromJson(json["subclass"]), description: json["description"], ); Map toJson() => { "id": id, - "subclass": subclass.toJson(), + "subclass": subclass?.toJson(), "description": description, }; } class Subclass { - final int id; - final String name; - final AttachmentClass attachmentClass; + final int? id; + final String? name; + final AttachmentClass? attachmentClass; Subclass({ required this.id, @@ -90,19 +86,19 @@ class Subclass { factory Subclass.fromJson(Map json) => Subclass( id: json["id"], name: json["name"], - attachmentClass: AttachmentClass.fromJson(json["attachment_class"]), + attachmentClass: json['attachment_class'] == null? null: AttachmentClass.fromJson(json["attachment_class"]), ); Map toJson() => { "id": id, "name": name, - "attachment_class": attachmentClass.toJson(), + "attachment_class": attachmentClass?.toJson(), }; } class AttachmentClass { - final int id; - final String name; + final int? id; + final String? name; AttachmentClass({ required this.id, diff --git a/lib/model/profile/educational_background.dart b/lib/model/profile/educational_background.dart index ef061a0..843aed0 100644 --- a/lib/model/profile/educational_background.dart +++ b/lib/model/profile/educational_background.dart @@ -26,7 +26,7 @@ class EducationalBackground { final List? honors; final Education? education; final String? periodTo; - final List? attachments; + List? attachments; final String? periodFrom; final int? unitsEarned; final String? yearGraduated; diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index 96dbb54..5a8f7c7 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -34,7 +34,7 @@ class EligibityCert { final int? id; final double? rating; final DateTime? examDate; - final List? attachments; + List? attachments; final Eligibility? eligibility; final ExamAddress? examAddress; diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart index 17362f4..58fa2ed 100644 --- a/lib/model/profile/learning_development.dart +++ b/lib/model/profile/learning_development.dart @@ -24,7 +24,7 @@ class LearningDevelopement { this.totalHoursAttended, }); - final List? attachments; + List? attachments; final Agency? sponsoredBy; final ConductedTraining? conductedTraining; final double? totalHoursAttended; diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 076cf47..b479a24 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -34,7 +34,7 @@ class WorkHistory { final DateTime? toDate; final Position? position; final DateTime? fromDate; - final List? attachments; + List? attachments; final int? salaryGrade; final double? monthlySalary; final String? appointmentStatus; diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 8f8363f..fb32a9a 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -7,7 +7,10 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/screens/profile/components/education/add_modal.dart'; @@ -16,7 +19,6 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../bloc/profile/education/education_bloc.dart'; import '../../../model/profile/attachment.dart'; import '../../../theme-data.dart/btn-style.dart'; @@ -33,7 +35,8 @@ class EducationScreen extends StatelessWidget { @override Widget build(BuildContext context) { final parent = context; - FilePickerResult? result; + + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; int profileId; @@ -115,6 +118,28 @@ class EducationScreen extends StatelessWidget { }); } } + ////ATTACHMENT ADDED STATE + + if (state is EducationAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } + } ////EDITED STATE if (state is EditedEducationState) { if (state.response['success']) { @@ -157,11 +182,32 @@ class EducationScreen extends StatelessWidget { }); } } + ////ATTACHMENT DELETED STATE + if (state is EducationAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } + } }, builder: (context, state) { if (state is EducationalBackgroundLoadedState) { for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 1) { + if (cat.subclass!.id == 1) { attachmentCategories.add(cat); } } @@ -352,6 +398,7 @@ class EducationScreen extends StatelessWidget { index])); } if (value == 3) { + results.clear(); showDialog( context: context, builder: @@ -423,7 +470,7 @@ class EducationScreen extends StatelessWidget { selectedAttachmentCategory = value; }, items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); + return DropdownMenuItem(value: e, child: Text(e.description!)); }).toList()), const SizedBox( height: @@ -463,7 +510,9 @@ class EducationScreen extends StatelessWidget { ]); setState(() { if (newResult != null) { - result = newResult; + newResult.files.forEach((element) { + results.add(element); + }); } }); }, @@ -479,13 +528,13 @@ class EducationScreen extends StatelessWidget { SizedBox( height: 100, width: double.maxFinite, - child: result == null + child: results.isEmpty ? const SizedBox() : Expanded( child: ListView.builder( - itemCount: result!.files.length, + itemCount: results.length, itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; + final kb = results[index].size / 1024; final mb = kb / 1024; final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; return Column( @@ -497,19 +546,19 @@ class EducationScreen extends StatelessWidget { children: [ Flexible( child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' + child: results[index].extension!.toLowerCase() == 'pdf' ? SvgPicture.asset( 'assets/svgs/pdf.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'png' + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' ? SvgPicture.asset( 'assets/svgs/jpg.svg', height: blockSizeVertical * 3, @@ -519,18 +568,36 @@ class EducationScreen extends StatelessWidget { const SizedBox( width: 12, ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + flex: 2, + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, ), - const SizedBox(width: 12,), - Flexible( - flex: 2, - child: Text(size)) + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) ], )), const Divider() @@ -554,12 +621,15 @@ class EducationScreen extends StatelessWidget { onPressed: () { List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { paths.add(res.path!); } + setState(() { + results.clear(); + }); Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.educationalBackground[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + parent.read().add(AddEducationAttachment(attachmentModule: state.educationalBackground[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); } }, child: const Text("Submit")), @@ -594,6 +664,7 @@ class EducationScreen extends StatelessWidget { ) ], ), + ////Show Attachments SizedBox( child: state @@ -620,22 +691,45 @@ class EducationScreen extends StatelessWidget { 1 ? ////Single Attachment view - SingleAttachment( - onpressed: () { - confirmAlert( - context, - () {}, - "Delete?", - "Confirm Delete?"); - }, - attachment: state - .educationalBackground[ - index] - .attachments! - .first, + Column( + children: [ + const Divider(), + SingleAttachment( + onpressed: + () { + confirmAlert( + context, + () { + parent.read().add(DeleteEducationAttachment( + attachment: state + .educationalBackground[ + index] + .attachments! + .first, + moduleId: state + .educationalBackground[ + index] + .id!, + profileId: + profileId, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .educationalBackground[ + index] + .attachments! + .first, + ), + ], ) ////Multiple Attachments View : MultipleAttachments( + profileId: + profileId, + token: token!, eligibilityName: state .educationalBackground[ index] @@ -646,6 +740,21 @@ class EducationScreen extends StatelessWidget { .educationalBackground[ index] .attachments!, + educationBloc: + BlocProvider.of< + EducationBloc>( + parent), + eligibilityBloc: + null, + workHistoryBloc: + null, + learningDevelopmentBloc: + null, + blocId: 1, + moduleId: state + .educationalBackground[ + index] + .id!, )) ], ), diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index fc6b279..5b10940 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -9,7 +8,10 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/attachment.dart'; import 'package:unit2/model/profile/eligibility.dart'; @@ -38,7 +40,7 @@ class EligibiltyScreen extends StatelessWidget { BuildContext parent = context; String? token; int? profileId; - FilePickerResult? result; + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; @@ -101,7 +103,7 @@ class EligibiltyScreen extends StatelessWidget { if (state is EligibilityLoaded || state is AddEligibilityState || state is EditEligibilityState || - state is DeletedState || + state is EligibilityDeletedState || state is EligibilityAddedState || state is EligibilityEditedState || state is EligibilityErrorState) { @@ -109,7 +111,7 @@ class EligibiltyScreen extends StatelessWidget { progress!.dismiss(); } ////DELETED STATE - if (state is DeletedState) { + if (state is EligibilityDeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", "Eligibility has been deleted successfully", @@ -129,6 +131,28 @@ class EligibiltyScreen extends StatelessWidget { }); } } + ////ATTACHMENT ADDED STATE + + if (state is EligibilityAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } + } ////ADDED STATE if (state is EligibilityAddedState) { if (state.response['success']) { @@ -171,13 +195,34 @@ class EligibiltyScreen extends StatelessWidget { }); } } + ////ATTACHMENT DELETED STATE + if (state is EligibilitytAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } + } }, builder: (context, state) { return BlocBuilder( builder: (context, state) { if (state is EligibilityLoaded) { for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 3) { + if (cat.subclass!.id == 3) { attachmentCategories.add(cat); } } @@ -193,410 +238,460 @@ class EligibiltyScreen extends StatelessWidget { .eligibility! .title; return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, children: [ Container( - width: screenWidth, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), decoration: box1(), - child: Row( + child: Column( children: [ - Expanded( - child: Column( - mainAxisSize: - MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - title, - style: Theme.of( - context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500, - color: - primary), - ), - const SizedBox( - height: 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", - style: Theme.of( - context) - .textTheme - .titleSmall), - const SizedBox( - height: 3, - ), - Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", - style: Theme.of( - context) - .textTheme - .titleSmall), - const Divider(), - ////Show Attachments - SizedBox( - child: state.eligibilities[index].attachments == - null || - state + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + title, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500, + color: + primary), + ), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of( + context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", + style: Theme.of( + context) + .textTheme + .titleSmall), + ]), + ), + AppPopupMenu( + offset: const Offset( + -10, -10), + elevation: 3, + onSelected: (value) { + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + confirmAlert( + context, () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + BlocProvider + .of( + context) + .add(DeleteEligibility( + eligibilityId: state .eligibilities[ index] - .attachments! - .isEmpty - ? const SizedBox() - : state.eligibilities[index].attachments != - null && - state.eligibilities[index].attachments!.length == - 1 - ? - ////Single Attachment view - SingleAttachment( - onpressed: - () { - confirmAlert(context, - () { - parent.read().add(DeleteAttachment(attachment: state.eligibilities[index].attachments!.first, moduleId: state.eligibilities[index].attachments!.first.id.toString(), profileId: profileId.toString(), token: token!)); - }, "Delete?", - "Confirm Delete?"); - }, - attachment: state - .eligibilities[index] - .attachments! - .first, - ) - ////Multiple Attachments View - : MultipleAttachments( - eligibilityName: state - .eligibilities[index] - .eligibility! - .title, - attachments: state - .eligibilities[index] - .attachments!, - )) - ]), - ), - AppPopupMenu( - offset: - const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete eligibilty-= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, - () { - final progress = - ProgressHUD.of( - context); - progress! - .showWithText( - "Loading..."); - BlocProvider.of< - EligibilityBloc>( - context) - .add(DeleteEligibility( - eligibilityId: state - .eligibilities[ - index] - .id!, - profileId: - profileId - .toString(), - token: - token!)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - EligibityCert - eligibityCert = - state.eligibilities[ - index]; - bool overseas = eligibityCert - .examAddress! - .country! - .id - .toString() == - '175' - ? false - : true; - eligibityCert.overseas = - overseas; + .id!, + profileId: + profileId + .toString(), + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + EligibityCert + eligibityCert = + state.eligibilities[ + index]; + bool overseas = eligibityCert + .examAddress! + .country! + .id + .toString() == + '175' + ? false + : true; + eligibityCert + .overseas = + overseas; - eligibityCert.overseas = - overseas; + eligibityCert + .overseas = + overseas; - context - .read< - EligibilityBloc>() - .add(ShowEditEligibilityForm( - eligibityCert: - eligibityCert)); - } - ////Attachment - if (value == 3) { - showDialog( - context: context, - builder: - (BuildContext - context) { - return AlertDialog( - contentPadding: - const EdgeInsets - .all(0), - backgroundColor: - Colors.grey - .shade100, - icon: - const Icon( - Icons - .file_copy, - size: 32, - color: - primary, - ), - title: const Text( - "File Attachment:"), - content: StatefulBuilder( - builder: + context + .read< + EligibilityBloc>() + .add(ShowEditEligibilityForm( + eligibityCert: + eligibityCert)); + } + ////Attachment + if (value == 3) { + results.clear(); + showDialog( + context: + context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors + .grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder(builder: (context, setState) { - return Padding( - padding: const EdgeInsets - .all( - 16.0), - child: Column( - mainAxisSize: - MainAxisSize - .min, - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Divider(), - Text( - title, - style: - Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), - ), - const SizedBox( - height: - 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", - style: Theme.of(context).textTheme.titleSmall), - const SizedBox( - height: - 3, - ), - Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", - style: Theme.of(context).textTheme.titleSmall), - const Divider(), - FormBuilderDropdown( - autovalidateMode: AutovalidateMode.always, - decoration: normalTextFieldStyle("attachment category", "attachment category"), - name: 'attachments_categorues', - validator: FormBuilderValidators.required(errorText: "This field is required"), - onChanged: (value) { - selectedAttachmentCategory = value; - }, - items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); - }).toList()), - const SizedBox( - height: - 8, - ), - Text( - "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", - style: - Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), - ), - const SizedBox( - height: - 5, - ), - Text( - "Acceptable Files: PDF, JPEG, PNG", - style: - Theme.of(context).textTheme.bodySmall, - ), - Text( - "Max File Size (per attachment): 1MB", - style: - Theme.of(context).textTheme.bodySmall, - ), - const Divider(), - ElevatedButton( - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - backgroundColor: MaterialStateProperty.all(Colors.white), + return Padding( + padding: + const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + title, + style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), ), - onPressed: () async { - FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ - 'jpg', - 'png', - 'jpeg', - 'pdf' - ]); - setState(() { - if (newResult != null) { - result = newResult; - } - }); - }, - child: const Center( - child: Text( - "Select Files", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.black), - ))), - const Divider(), - SingleChildScrollView( - child: - SizedBox( - height: 100, - width: double.maxFinite, - child: result == null - ? const SizedBox() - : Expanded( - child: ListView.builder( - itemCount: result!.files.length, - itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; - final mb = kb / 1024; - final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: double.infinity, - child: Row( - children: [ - Flexible( - child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' - ? SvgPicture.asset( - 'assets/svgs/pdf.svg', - height: blockSizeVertical * 3, - allowDrawingOutsideViewBox: true, - ) - : result!.files[index].extension!.toLowerCase() == 'png' + const SizedBox( + height: 5, + ), + Text("$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: 3, + ), + Text("Rating : ${state.eligibilities[index].rating ?? 'N/A'}", style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description!)); + }).toList()), + const SizedBox( + height: 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + FilePickerResult? x; + if (newResult != null) { + newResult.files.forEach((element) { + results.add(element); + }); + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: SizedBox( + height: 100, + width: double.maxFinite, + child: results.isEmpty + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, index) { + final kb = results[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: results[index].extension!.toLowerCase() == 'pdf' ? SvgPicture.asset( - 'assets/svgs/png.svg', + 'assets/svgs/pdf.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( - 'assets/svgs/jpg.svg', + 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : const SizedBox())), - const SizedBox( - width: 12, - ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), ), - const SizedBox(width: 8,), - Flexible( + ), + const SizedBox( + width: 8, + ), + Flexible( flex: 2, - child: Text(size)) - ], - )), - const Divider() - ], - ); - }), - ), - ), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: - double.maxFinite, - height: - 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - List paths = []; + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, + ), + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.maxFinite, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { - paths.add(res.path!); - } - Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.eligibilities[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); - } - }, - child: const Text("Submit")), - ) - ]), + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { + paths.add(res.path!); + } + setState(() { + results.clear(); + }); + Navigator.pop(context); + parent.read().add(AddEligibiltyAttachment(attachmentModule: state.eligibilities[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), ); - }), - ); - }); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 3, - icon: Icons - .attach_file), + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: + Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons + .attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) + ), + const Divider(), + ////Show Attachments + SizedBox( + width: screenWidth, + child: state + .eligibilities[ + index] + .attachments == + null || + state + .eligibilities[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state.eligibilities[index].attachments != + null && + state + .eligibilities[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: + () { + confirmAlert( + context, + () { + parent.read().add(DeleteEligibyAttachment( + attachment: state + .eligibilities[ + index] + .attachments! + .first, + moduleId: state + .eligibilities[ + index] + .id + .toString(), + profileId: profileId + .toString(), + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .eligibilities[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + profileId: + profileId!, + token: token!, + moduleId: state + .eligibilities[ + index] + .eligibility! + .id, + educationBloc: + null, + learningDevelopmentBloc: + null, + workHistoryBloc: + null, + eligibilityBloc: + BlocProvider.of< + EligibilityBloc>( + context), + blocId: 2, + eligibilityName: state + .eligibilities[ + index] + .eligibility! + .title, + attachments: state + .eligibilities[ + index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index c7cea00..0e70a88 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -1,19 +1,18 @@ import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/screens/profile/components/learning_development/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -41,9 +40,9 @@ class LearningAndDevelopmentScreen extends StatelessWidget { Widget build(BuildContext context) { String token; int profileId; - BuildContext parent = context; + BuildContext parent = context; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); - FilePickerResult? result; + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; return Scaffold( @@ -89,13 +88,11 @@ class LearningAndDevelopmentScreen extends StatelessWidget { backgroundColor: Colors.black87, child: BlocBuilder( builder: (context, state) { - if (state is UserLoggedIn) { token = state.userData!.user!.login!.token!; profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { - if (state is ProfileLoaded) { return BlocConsumer( @@ -134,6 +131,27 @@ class LearningAndDevelopmentScreen extends StatelessWidget { }); } } + ////Attachment Added State + if (state is LearningDevAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } + } ////Updated State if (state is LearningDevelopmentUpdatedState) { if (state.response['success']) { @@ -177,16 +195,36 @@ class LearningAndDevelopmentScreen extends StatelessWidget { }); } } + ////ATTACHMENT DELETED STATE + if (state is LearningDevAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } + } // TODO: implement listener }, builder: (context, state) { - if (state is LearningDevelopmentLoadedState) { - for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 2) { - attachmentCategories.add(cat); - } - } + for (var cat in state.attachmentCategory) { + if (cat.subclass!.id == 2) { + attachmentCategories.add(cat); + } + } if (state.learningsAndDevelopment.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -232,28 +270,32 @@ class LearningAndDevelopmentScreen extends StatelessWidget { Expanded( child: Column( mainAxisAlignment: - MainAxisAlignment.start, + MainAxisAlignment + .start, crossAxisAlignment: CrossAxisAlignment .start, children: [ Text( training, - style: Theme.of(context) + style: Theme.of( + context) .textTheme .titleMedium! .copyWith( fontWeight: FontWeight .w600, - color: primary), + color: + primary), ), const SizedBox( height: 8, ), Text( provider, - style: Theme.of(context) + style: Theme.of( + context) .textTheme .titleSmall, ), @@ -262,7 +304,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ), Text( "$duration: $start to $end", - style: Theme.of(context) + style: Theme.of( + context) .textTheme .labelMedium, ), @@ -272,39 +315,39 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ]), ), AppPopupMenu( - offset: const Offset(-10, -10), + offset: + const Offset(-10, -10), elevation: 3, onSelected: (value) { ////delete -= = = = = = = = =>> if (value == 2) { - confirmAlert(context, () { + confirmAlert(context, + () { final progress = ProgressHUD.of( context); - progress!.showWithText( - "Loading..."); - BlocProvider.of< - LearningDevelopmentBloc>( - context) - .add(DeleteLearningDevelopment( - trainingId: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .id!, - hours: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .totalHours!, - sponsorId: state - .learningsAndDevelopment[ - index] - .sponsoredBy - ?.id, - profileId: - profileId, - token: token)); + progress! + .showWithText( + "Loading..."); + BlocProvider.of(context).add(DeleteLearningDevelopment( + trainingId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .id!, + hours: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .totalHours!, + sponsorId: state + .learningsAndDevelopment[ + index] + .sponsoredBy + ?.id, + profileId: + profileId, + token: token)); }, "Delete?", "Confirm Delete?"); } @@ -312,7 +355,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { bool isOverseas; ////edit = = = = = = = =>> final progress = - ProgressHUD.of(context); + ProgressHUD.of( + context); progress!.showWithText( "Loading..."); @@ -340,225 +384,246 @@ class LearningAndDevelopmentScreen extends StatelessWidget { isOverseas: isOverseas)); } - if(value == 3){ - showDialog( - context: context, - builder: - (BuildContext - context) { - return AlertDialog( - contentPadding: - const EdgeInsets - .all(0), - backgroundColor: - Colors.grey - .shade100, - icon: - const Icon( - Icons - .file_copy, - size: 32, - color: - primary, - ), - title: const Text( - "File Attachment:"), - content: StatefulBuilder( - builder: - (context, - setState) { - return Padding( - padding: const EdgeInsets - .all( - 16.0), - child: Column( - mainAxisSize: - MainAxisSize - .min, - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Divider(), - Text( - training, - style: - Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + if (value == 3) { + results.clear(); + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + training, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + provider, + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "$start TO $end", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description!)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), ), - const SizedBox( - height: - 5, - ), - Text( - provider, - style: Theme.of(context).textTheme.titleSmall), - const SizedBox( - height: - 3, - ), - Text( - "$start TO $end", - style: Theme.of(context).textTheme.titleSmall), - const Divider(), - FormBuilderDropdown( - autovalidateMode: AutovalidateMode.always, - decoration: normalTextFieldStyle("attachment category", "attachment category"), - name: 'attachments_categorues', - validator: FormBuilderValidators.required(errorText: "This field is required"), - onChanged: (value) { - selectedAttachmentCategory = value; - }, - items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); - }).toList()), - const SizedBox( - height: - 8, - ), - Text( - "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", - style: - Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), - ), - const SizedBox( - height: - 5, - ), - Text( - "Acceptable Files: PDF, JPEG, PNG", - style: - Theme.of(context).textTheme.bodySmall, - ), - Text( - "Max File Size (per attachment): 1MB", - style: - Theme.of(context).textTheme.bodySmall, - ), - const Divider(), - ElevatedButton( - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - backgroundColor: MaterialStateProperty.all(Colors.white), - ), - onPressed: () async { - FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ - 'jpg', - 'png', - 'jpeg', - 'pdf' - ]); - setState(() { - if (newResult != null) { - result = newResult; - } + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + newResult.files.forEach((element) { + results.add(element); }); - }, - child: const Center( - child: Text( - "Select Files", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.black), - ))), - const Divider(), - SingleChildScrollView( - child: - SizedBox( - height: 100, - width: double.maxFinite, - child: result == null - ? const SizedBox() - : Expanded( - child: ListView.builder( - itemCount: result!.files.length, - itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; - final mb = kb / 1024; - final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - - width: double.infinity, - child: Row( - children: [ - Flexible( - flex: 1, - child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: results.isEmpty + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, index) { + final kb = results[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + flex: 1, + child: SizedBox( + child: results[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( - 'assets/svgs/pdf.svg', + 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'png' + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' ? SvgPicture.asset( - 'assets/svgs/png.svg', + 'assets/svgs/jpg.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' - ? SvgPicture.asset( - 'assets/svgs/jpg.svg', - height: blockSizeVertical * 3, - allowDrawingOutsideViewBox: true, - ) - : const SizedBox())), - const SizedBox( - width: 12, - ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), - ), - const SizedBox(width: 12,), - Flexible( - flex: 2, - child: Text(size)) - ], - )), - const Divider() - ], - ); - }), - ), - ), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: - double.maxFinite, - height: - 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - List paths = []; + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 2, + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, + ), + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { - paths.add(res.path!); - } - Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.learningsAndDevelopment[index].conductedTraining!.id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); - } - }, - child: const Text("Submit")), - ) - ]), - ); - }), + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { + paths.add(res.path!); + } + results.clear(); + Navigator.pop(context); + parent.read().add(AddALearningDevttachment(attachmentModule: state.learningsAndDevelopment[index].conductedTraining!.id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), ); - }); + }), + ); + }); } }, menuItems: [ @@ -573,7 +638,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { popMenuItem( text: "Attach", value: 3, - icon: Icons.attach_file), + icon: Icons + .attach_file), ], icon: const Icon( Icons.more_vert, @@ -584,44 +650,92 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ], ), SizedBox( - child: state.learningsAndDevelopment[index].attachments == - null || - state + child: state + .learningsAndDevelopment[ + index] + .attachments == + null || + state + .learningsAndDevelopment[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state + .learningsAndDevelopment[ + index] + .attachments != + null && + state + .learningsAndDevelopment[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: () { + confirmAlert( + context, + () { + parent.read().add(DeleteLearningDevAttachment( + attachment: state .learningsAndDevelopment[ index] .attachments! - .isEmpty - ? const SizedBox() - : state.learningsAndDevelopment[index].attachments != - null && - state.learningsAndDevelopment[index].attachments!.length == - 1 - ? - ////Single Attachment view - SingleAttachment( - onpressed: - () { - confirmAlert(context, - () { - - }, "Delete?", - "Confirm Delete?"); - }, - attachment: state - .learningsAndDevelopment[index] - .attachments! - .first, - ) - ////Multiple Attachments View - : MultipleAttachments( - eligibilityName: state - .learningsAndDevelopment[index] - .conductedTraining!.title!.title!, - - attachments: state - .learningsAndDevelopment[index] - .attachments!, - )) + .first, + moduleId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .id!, + profileId: + profileId, + token: + token)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .learningsAndDevelopment[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + profileId: + profileId, + token: token, + moduleId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .title! + .id!, + educationBloc: + null, + eligibilityBloc: + null, + learningDevelopmentBloc: + BlocProvider.of< + LearningDevelopmentBloc>( + context), + workHistoryBloc: + null, + blocId: 4, + eligibilityName: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .title! + .title!, + attachments: state + .learningsAndDevelopment[ + index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 153f851..b234467 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -7,9 +7,11 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/work_history/add_modal.dart'; @@ -21,7 +23,6 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; import '../../../model/profile/attachment.dart'; import '../../../model/profile/work_history.dart'; @@ -38,10 +39,11 @@ class WorkHistoryScreen extends StatelessWidget { @override Widget build(BuildContext context) { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + BuildContext parent = context; String? token; int? profileId; - FilePickerResult? result; + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; return Scaffold( @@ -108,7 +110,7 @@ class WorkHistoryScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.dismiss(); } - //DELETED STATE + ////DELETED STATE if (state is DeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", @@ -149,6 +151,48 @@ class WorkHistoryScreen extends StatelessWidget { }); } } + if (state is WorkHistoryDevAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } + } + ////ATTACHMENT DELETED STATE + if (state is WorkHistoryDevAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } + } + //// EDITED STATE if (state is WorkHistoryEditedState) { if (state.response['success']) { @@ -174,7 +218,7 @@ class WorkHistoryScreen extends StatelessWidget { builder: (context, state) { if (state is WorkHistoryLoaded) { for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 4) { + if (cat.subclass!.id == 4) { attachmentCategories.add(cat); } } @@ -201,14 +245,8 @@ class WorkHistoryScreen extends StatelessWidget { .workExperiences[index] .toDate!); return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, children: [ Container( - width: screenWidth, decoration: box1(), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), @@ -303,6 +341,7 @@ class WorkHistoryScreen extends StatelessWidget { } ////Attachment if (value == 3) { + results.clear(); showDialog( context: context, builder: (BuildContext @@ -383,7 +422,7 @@ class WorkHistoryScreen extends StatelessWidget { selectedAttachmentCategory = value; }, items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); + return DropdownMenuItem(value: e, child: Text(e.description!)); }).toList()), const SizedBox( height: @@ -429,7 +468,9 @@ class WorkHistoryScreen extends StatelessWidget { ]); setState(() { if (newResult != null) { - result = newResult; + newResult.files.forEach((element) { + results.add(element); + }); } }); }, @@ -447,13 +488,13 @@ class WorkHistoryScreen extends StatelessWidget { 100, width: double.maxFinite, - child: result == null + child: results.isEmpty ? const SizedBox() : Expanded( child: ListView.builder( - itemCount: result!.files.length, + itemCount: results.length, itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; + final kb = results[index].size / 1024; final mb = kb / 1024; final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; return Column( @@ -465,19 +506,19 @@ class WorkHistoryScreen extends StatelessWidget { children: [ Flexible( child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' + child: results[index].extension!.toLowerCase() == 'pdf' ? SvgPicture.asset( 'assets/svgs/pdf.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'png' + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' ? SvgPicture.asset( 'assets/svgs/jpg.svg', height: blockSizeVertical * 3, @@ -487,18 +528,36 @@ class WorkHistoryScreen extends StatelessWidget { const SizedBox( width: 12, ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox( + width: 6, + ), + Flexible( + flex: 2, + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, ), - const SizedBox(width: 12,), - Flexible( - flex: 2, - child: Text(size)) + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) ], )), const Divider() @@ -522,12 +581,15 @@ class WorkHistoryScreen extends StatelessWidget { onPressed: () { List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { paths.add(res.path!); } + setState(() { + results.clear(); + }); Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.workExperiences[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + parent.read().add(AddWorkHistoryAttachment(attachmentModule: state.workExperiences[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); } }, child: const Text("Submit")), @@ -592,8 +654,22 @@ class WorkHistoryScreen extends StatelessWidget { onpressed: () { confirmAlert( context, - () {}, - "Delete?", + () { + parent.read().add(DeleteWorkHistoryAttachment( + attachment: state + .workExperiences[ + index] + .attachments! + .first, + moduleId: state + .workExperiences[ + index] + .id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", "Confirm Delete?"); }, attachment: state @@ -604,6 +680,22 @@ class WorkHistoryScreen extends StatelessWidget { ) ////Multiple Attachments View : MultipleAttachments( + profileId: + profileId!, + token: token!, + moduleId: state + .workExperiences[ + index] + .id!, + educationBloc: + null, + workHistoryBloc: + BlocProvider.of(context), + eligibilityBloc: + null, + learningDevelopmentBloc: + null, + blocId: 3, eligibilityName: state .workExperiences[ index] diff --git a/lib/screens/profile/shared/multiple_attachment.dart b/lib/screens/profile/shared/multiple_attachment.dart index a818bf7..22b04c3 100644 --- a/lib/screens/profile/shared/multiple_attachment.dart +++ b/lib/screens/profile/shared/multiple_attachment.dart @@ -1,150 +1,234 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/entypo_icons.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; +import 'package:unit2/utils/global_context.dart'; import '../../../model/profile/attachment.dart'; import '../../../theme-data.dart/box_shadow.dart'; import '../../../theme-data.dart/colors.dart'; +import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; class MultipleAttachments extends StatelessWidget { final List attachments; final String eligibilityName; - const MultipleAttachments({ - super.key, - required this.attachments, - required this.eligibilityName - }); + final EducationBloc? educationBloc; + final EligibilityBloc? eligibilityBloc; + final LearningDevelopmentBloc? learningDevelopmentBloc; + final WorkHistoryBloc? workHistoryBloc; + final int blocId; + final int moduleId; + final int profileId; + final String token; + const MultipleAttachments( + {super.key, + required this.blocId, + required this.educationBloc, + required this.eligibilityBloc, + required this.learningDevelopmentBloc, + required this.workHistoryBloc, + required this.attachments, + required this.eligibilityName, + required this.moduleId, + required this.profileId, + required this.token}); @override Widget build(BuildContext context) { return Row( - children: [ - Flexible( - flex: - 2, - child: Container( - padding: const EdgeInsets.all(5), - decoration: box1().copyWith(color: Colors.grey.shade300, boxShadow: []), - child: Text( - attachments.first.filename!, - overflow: TextOverflow.ellipsis, - ))), - const SizedBox( - width: - 8, - ), - Flexible( - child: - FittedBox( - child: - Container( - padding: - const EdgeInsets.all(3), - decoration: - box1().copyWith(color: Colors.grey.shade300, boxShadow: []), - child: - InkWell( - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text("$eligibilityName Attachments",textAlign: TextAlign.center,), - content: Column( - mainAxisSize: MainAxisSize.min, - children: attachments.map((e) { - String ext = e.filename!.substring(e.filename!.lastIndexOf(".")); - - return Column( - children: [ - Row( - children: [ - Flexible( - child: SizedBox( - child: ext == '.pdf' - ? SvgPicture.asset( - 'assets/svgs/pdf.svg', - height: blockSizeVertical * 5, - allowDrawingOutsideViewBox: true, - ) - : ext == '.png' - ? SvgPicture.asset( - 'assets/svgs/png.svg', - height: blockSizeVertical * 5.5, - allowDrawingOutsideViewBox: true, - ) - : ext == '.jpg' - ? SvgPicture.asset( - 'assets/svgs/jpg.svg', - height: blockSizeVertical * 5, - allowDrawingOutsideViewBox: true, - ) - : SvgPicture.asset( - 'assets/svgs/jpg.svg', - height: blockSizeVertical * 5, - allowDrawingOutsideViewBox: true, - ), - ), - ), - const SizedBox(width: 8,), - Flexible( - flex: 4, - child: Tooltip( - message: e.filename, - child: Text( - e.filename!, - overflow: TextOverflow.ellipsis, - )), - ), - const SizedBox(width: 8,), - Flexible( - child: Row( + children: [ + Flexible( + flex: 3, + child: Container( + padding: const EdgeInsets.all(5), + decoration: + box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: AutoSizeText( + + attachments.first.filename!, + wrapWords: false, + maxLines: 1, + ))), + const SizedBox( + width: 8, + ), + Flexible( + child: FittedBox( + child: Container( + padding: const EdgeInsets.all(3), + decoration: + box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: InkWell( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + "$eligibilityName Attachments", + textAlign: TextAlign.center, + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: attachments.map((e) { + String ext = e.filename! + .substring(e.filename!.lastIndexOf(".")); + + return Column( + children: [ + Row( children: [ - Flexible( - child: IconButton( - icon: const Icon( - Entypo.right_open_mini, - color: Colors.black87, - ), - onPressed: () {}, - ), - ), - const SizedBox(width: 8,), Flexible( - child: IconButton( - icon: const Icon( - Icons.delete, - color: primary, - ), - onPressed: () {}, + child: SizedBox( + child: ext == '.pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: + true, + ) + : ext == '.png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: + blockSizeVertical * + 5.5, + allowDrawingOutsideViewBox: + true, + ) + : ext == '.jpg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: + blockSizeVertical * + 5, + allowDrawingOutsideViewBox: + true, + ) + : SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: + blockSizeVertical * + 5, + allowDrawingOutsideViewBox: + true, + ), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 4, + child: Tooltip( + message: e.filename, + child: Text( + e.filename!, + overflow: TextOverflow.ellipsis, + )), + ), + const SizedBox( + width: 8, + ), + Flexible( + child: Row( + children: [ + Flexible( + child: IconButton( + icon: const Icon( + Icons.delete, + color: primary, + ), + onPressed: () { + confirmAlert(context, () { + if (blocId == 1) { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + educationBloc!.add( + DeleteEducationAttachment( + attachment: e, + moduleId: + moduleId, + profileId: + profileId, + token: token)); + } else if (blocId == 2) { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + eligibilityBloc!.add( + DeleteEligibyAttachment( + attachment: e, + moduleId: moduleId + .toString(), + profileId: profileId + .toString(), + token: token)); + } else if (blocId == 3) { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + workHistoryBloc!.add( + DeleteWorkHistoryAttachment( + attachment: e, + moduleId: + moduleId, + profileId: + profileId, + token: token)); + } else { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + learningDevelopmentBloc!.add( + DeleteLearningDevAttachment( + attachment: e, + moduleId: + moduleId, + profileId: + profileId, + token: token)); + } + }, "Delete?", + "Confirm Delete?"); + }), + ) + ], ), ), ], ), - ), - ], - ), - const Divider() - ], - ); - }).toList(), - )); - }); - }, - child: Row( - children: const [ - Text(" See more.."), - Icon( - Icons.keyboard_arrow_right, - color: Colors.black54, - ), - ], - ), + const Divider() + ], + ); + }).toList(), + )); + }); + }, + child: Row( + children: const [ + Text(" See more.."), + Icon( + Icons.keyboard_arrow_right, + color: Colors.black54, + ), + ], ), ), - )), - ], - ); + ), + )), + ], + ); } } diff --git a/lib/screens/profile/shared/single_attachment.dart b/lib/screens/profile/shared/single_attachment.dart index 92f46e7..64f9c91 100644 --- a/lib/screens/profile/shared/single_attachment.dart +++ b/lib/screens/profile/shared/single_attachment.dart @@ -1,3 +1,4 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -31,9 +32,10 @@ class SingleAttachment extends StatelessWidget { children: [ Expanded( child: - Text( + AutoSizeText( attachment.filename!, - overflow: TextOverflow.ellipsis, + wrapWords: false, + maxLines: 1, ), ), const SizedBox( diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart index 9738e8a..d9a94ea 100644 --- a/lib/sevices/profile/eligibility_services.dart +++ b/lib/sevices/profile/eligibility_services.dart @@ -165,11 +165,11 @@ class EligibilityService { "category": { "id": attachment.category?.id, "subclass": { - "id": attachment.category?.subclass.id, - "name": attachment.category?.subclass.name, + "id": attachment.category?.subclass?.id, + "name": attachment.category?.subclass?.name, "attachment_class": { - "id": attachment.category?.subclass.attachmentClass.id, - "name": attachment.category?.subclass.attachmentClass.name + "id": attachment.category?.subclass?.attachmentClass?.id, + "name": attachment.category?.subclass?.attachmentClass?.name } }, "description": attachment.category?.description diff --git a/lib/utils/attachment_categories.dart b/lib/utils/attachment_services.dart similarity index 58% rename from lib/utils/attachment_categories.dart rename to lib/utils/attachment_services.dart index cce37ff..83f7a1c 100644 --- a/lib/utils/attachment_categories.dart +++ b/lib/utils/attachment_services.dart @@ -32,7 +32,7 @@ class AttachmentServices { return attachmentCategories; } - Future> attachment( + Future> attachment( {required String categoryId, required String module, required List paths, @@ -74,4 +74,57 @@ class AttachmentServices { } return response; } + + Future deleteAttachment( + {required Attachment attachment, + required int moduleId, + required String profileId, + required String token}) async { + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.attachments()}$profileId/"; + Map? body; + body = { + "attachment_module": moduleId, + "attachments": [ + { + "id": attachment.id, + "created_at": attachment.createdAt?.toString(), + "source": attachment.source, + "filename": attachment.filename, + "category": { + "id": attachment.category?.id, + "subclass": { + "id": attachment.category?.subclass?.id, + "name": attachment.category?.subclass?.name, + "attachment_class": { + "id": attachment.category?.subclass?.attachmentClass?.id, + "name": attachment.category?.subclass?.attachmentClass?.name + } + }, + "description": attachment.category?.description + } + } + ] + }; + + Map params = {"force_mode": "true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + // try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + // } catch (e) { + // throw (e.toString()); + // } + return success!; + } } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index bd6932e..3201fd6 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -6,9 +6,9 @@ class Url { String host() { // return '192.168.10.183:3000'; // return 'agusandelnorte.gov.ph'; - // return "192.168.10.219:3000"; - // return "192.168.10.241"; - return "192.168.10.221:3004"; + return "192.168.10.219:3000"; + // // return "192.168.10.241"; + // return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } From c675be937460d86fc82e4dad52a79148da620f0c Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 7 Aug 2023 14:36:58 +0800 Subject: [PATCH 3/3] change http to https --- lib/utils/attachment_services.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/attachment_services.dart b/lib/utils/attachment_services.dart index 83f7a1c..be51a10 100644 --- a/lib/utils/attachment_services.dart +++ b/lib/utils/attachment_services.dart @@ -49,7 +49,7 @@ class AttachmentServices { try { var request = http.MultipartRequest( - 'POST', Uri.parse('http://${Url.instance.host()}$path$profileId/')); + 'POST', Uri.parse('https://${Url.instance.host()}$path$profileId/')); request.fields.addAll(body); request.headers.addAll(headers); paths.forEach((element) async {