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..0117b6d 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_services.dart'; + part 'education_event.dart'; part 'education_state.dart'; @@ -11,20 +14,27 @@ 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)); + emit(EducationalBackgroundLoadedState( + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); } else { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); } } catch (e) { emit(EducationalBackgroundErrorState(message: e.toString())); @@ -77,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); @@ -89,7 +100,8 @@ 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 +146,65 @@ class EducationBloc extends Bloc { emit(EducationalBackgroundErrorState(message: e.toString())); } }); + ////Add attachment + 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( + categoryId: event.categoryId, + module: event.attachmentModule, + 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)); + } + } catch (e) { + 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 59a431b..9b68a0f 100644 --- a/lib/bloc/profile/education/education_event.dart +++ b/lib/bloc/profile/education/education_event.dart @@ -63,3 +63,23 @@ class DeleteEducation extends EducationEvent{ @override List get props => [educationalBackground, profileId, token]; } +////Add attachment +class AddEducationAttachment extends EducationEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + 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 2a0f1ec..3cfe505 100644 --- a/lib/bloc/profile/education/education_state.dart +++ b/lib/bloc/profile/education/education_state.dart @@ -10,8 +10,10 @@ abstract class EducationState extends Equatable { class EducationInitial extends EducationState {} class EducationalBackgroundLoadedState extends EducationState { + final List attachmentCategory; final List educationalBackground; - const EducationalBackgroundLoadedState({required this.educationalBackground}); + const EducationalBackgroundLoadedState( + {required this.educationalBackground, required this.attachmentCategory}); @override List get props => [educationalBackground]; } @@ -57,6 +59,8 @@ class EducationAddedState extends EducationState { @override List get props => [response]; } + + //// Edited State class EditedEducationState extends EducationState { final Map response; @@ -72,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 e405355..1be36b4 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_services.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 @@ -34,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())); @@ -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,63 @@ class EligibilityBloc extends Bloc { emit(const EligibilityErrorState( message: "Something went wrong. Please try again")); }); + ////Add attachment + 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( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + 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(EligibilityAttachmentAddedState(response: status)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); + + on((event, emit) async { + emit(EligibilityLoadingState()); + // try { + final bool success = await AttachmentServices.instance.deleteAttachment( + attachment: event.attachment, + moduleId: int.parse(event.moduleId), + profileId: event.profileId.toString(), + token: event.token); + if (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(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 cd57562..a9b6ca8 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, @@ -67,5 +68,32 @@ class DeleteEligibility extends EligibilityEvent { class CallErrorState extends EligibilityEvent{ } +////Add Attachment +class AddEligibiltyAttachment extends EligibilityEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + 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]; +} + +////Delete Attachment +class DeleteEligibyAttachment extends EligibilityEvent{ + final String profileId; + final String token; + final Attachment attachment; + final String moduleId; + 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 3b57f6d..7d746d9 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, @@ -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]; } @@ -53,41 +53,57 @@ 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]; } + +////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 6ef27c0..465cefb 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_services.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,58 @@ class LearningDevelopmentBloc on((event, emit) { emit(LearningDevelopmentErrorState(message: event.message)); }); + ////Add attachment + 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( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + 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)); + } + } catch (e) { + 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 1b1453f..c8528ac 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -66,3 +66,25 @@ class CallErrorState extends LearningDevelopmentEvent{ final String message; const CallErrorState({required this.message}); } + +////Add Attachment +class AddALearningDevttachment extends LearningDevelopmentEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + 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 e498dc9..2699470 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -10,8 +10,11 @@ abstract class LearningDevelopmentState extends Equatable { class LearningDevelopmentInitial extends LearningDevelopmentState {} class LearningDevelopmentLoadedState extends LearningDevelopmentState { + final List attachmentCategory; final List learningsAndDevelopment; - const LearningDevelopmentLoadedState({required this.learningsAndDevelopment}); + const LearningDevelopmentLoadedState( + {required this.learningsAndDevelopment, + required this.attachmentCategory}); @override List get props => [learningsAndDevelopment]; } @@ -112,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_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/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 2f8a28e..e9a44c8 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_services.dart'; part 'workHistory_event.dart'; part 'workHistory_state.dart'; @@ -19,17 +21,24 @@ 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 +46,9 @@ 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 +193,64 @@ class WorkHistoryBloc extends Bloc { emit(WorkHistoryErrorState(message: e.toString())); } }); + ////Add Attachment + 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( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + 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(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 0f394b7..1895bfe 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -61,4 +61,26 @@ class AddWorkHostory extends WorkHistorytEvent{ List get props => [workHistory,profileId,token,isPrivate]; } +////Add Attachment +class AddWorkHistoryAttachment extends WorkHistorytEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + 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 2008857..117d984 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]; } @@ -72,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 new file mode 100644 index 0000000..f0676f8 --- /dev/null +++ b/lib/model/profile/attachment.dart @@ -0,0 +1,118 @@ + +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:json['subclass'] == null? null: 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: json['attachment_class'] == null? null: 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..843aed0 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; + 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..5a8f7c7 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; + 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..58fa2ed 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; + 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..b479a24 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; + 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..fb32a9a 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -1,9 +1,16 @@ 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/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'; @@ -13,13 +20,25 @@ 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; + + List? results = []; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; int profileId; String? token; return Scaffold( @@ -99,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']) { @@ -141,9 +182,35 @@ 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) { + attachmentCategories.add(cat); + } + } if (state.educationalBackground.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -182,54 +249,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 +342,420 @@ 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< + 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) { + 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( + 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) { + 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/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : results[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : 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: 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, + ), + 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 && results.isNotEmpty) { + for (var res in results) { + paths.add(res.path!); + } + setState(() { + results.clear(); + }); + Navigator.pop(context); + 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")), + ) + ]), + ); + }), + ); + }); + } + }, + 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", + ) + ], ), - 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], + + ////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 + 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!)); - }, "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), - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) + token: token!, + eligibilityName: state + .educationalBackground[ + index] + .education! + .school! + .name!, + attachments: state + .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 e3ddd67..5b10940 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -1,16 +1,26 @@ +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/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'; 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 +29,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; + List? results = []; + 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 +58,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, @@ -81,30 +103,53 @@ 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 - ) { + state is EligibilityErrorState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } ////DELETED STATE - if (state is DeletedState) { + if (state is EligibilityDeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", "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()); + }); + } + } + ////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()); }); } } @@ -114,16 +159,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 +180,39 @@ 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()); + }); + } + } + ////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()); }); } } @@ -151,7 +221,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,153 +238,466 @@ class EligibiltyScreen extends StatelessWidget { .eligibility! .title; return Column( - 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( - 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) - ]), + 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] + .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; + + 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), + ), + 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/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : results[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : 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( + 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 && 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), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], ), - 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 + const Divider(), + ////Show Attachments + SizedBox( + width: screenWidth, + child: state .eligibilities[ index] - .id!, + .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.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; - - context - .read() - .add(ShowEditEligibilityForm( - eligibityCert: - eligibityCert)); - } - }, - 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", - ) + 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!, + )) ], ), ), const SizedBox( height: 5, - ) + ), ], ); }); @@ -322,17 +709,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 +737,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..0e70a88 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -1,15 +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:fluttericon/font_awesome_icons.dart'; +import 'package:flutter_svg/svg.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'; @@ -19,8 +22,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 +40,11 @@ class LearningAndDevelopmentScreen extends StatelessWidget { Widget build(BuildContext context) { String token; int profileId; + BuildContext parent = context; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + List? results = []; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return Scaffold( appBar: AppBar( title: context.watch().state @@ -76,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( @@ -121,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']) { @@ -164,12 +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) { - 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,66 +263,73 @@ 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( + 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(context).add(DeleteLearningDevelopment( trainingId: state .learningsAndDevelopment[ index] @@ -286,62 +348,394 @@ class LearningAndDevelopmentScreen extends StatelessWidget { profileId: profileId, token: token)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - bool isOverseas; - ////edit = = = = = = = =>> - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); + }, "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) { + 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), + ), + 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: 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/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : 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( + 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 && 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: [ + 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, + () { + parent.read().add(DeleteLearningDevAttachment( + attachment: 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 5372185..b234467 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,11 +1,17 @@ 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: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'; @@ -17,11 +23,15 @@ 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'; +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 +39,13 @@ class WorkHistoryScreen extends StatelessWidget { @override Widget build(BuildContext context) { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + + BuildContext parent = context; String? token; - int profileId; + int? profileId; + List? results = []; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return Scaffold( appBar: AppBar( title: context.watch().state is AddWorkHistoryState @@ -95,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", @@ -136,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']) { @@ -160,6 +217,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( @@ -185,114 +247,467 @@ class WorkHistoryScreen extends StatelessWidget { return Column( 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) { + 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( + 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) { + 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/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : results[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : 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: 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, + ), + 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 && results.isNotEmpty) { + for (var res in results) { + paths.add(res.path!); + } + setState(() { + results.clear(); + }); + Navigator.pop(context); + 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")), + ) + ]), + ); + }), + ); + }); + } + }, + 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, + () { + parent.read().add(DeleteWorkHistoryAttachment( + attachment: state + .workExperiences[ + index] + .attachments! + .first, + moduleId: state + .workExperiences[ + index] + .id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .workExperiences[ + index] + .attachments! + .first, + ) + ////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] + .position! + .title!, + attachments: state + .workExperiences[ + index] + .attachments!, + )) + ], + ), ), const SizedBox( height: 5, @@ -308,13 +723,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 +739,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..22b04c3 --- /dev/null +++ b/lib/screens/profile/shared/multiple_attachment.dart @@ -0,0 +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; + 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: 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: 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, + ), + ], + ), + ), + ), + )), + ], + ); + } +} diff --git a/lib/screens/profile/shared/single_attachment.dart b/lib/screens/profile/shared/single_attachment.dart new file mode 100644 index 0000000..64f9c91 --- /dev/null +++ b/lib/screens/profile/shared/single_attachment.dart @@ -0,0 +1,51 @@ +import 'package:auto_size_text/auto_size_text.dart'; +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: + AutoSizeText( + attachment.filename!, + wrapWords: false, + maxLines: 1, + ), + ), + 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..d9a94ea 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_services.dart b/lib/utils/attachment_services.dart new file mode 100644 index 0000000..be51a10 --- /dev/null +++ b/lib/utils/attachment_services.dart @@ -0,0 +1,130 @@ +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('https://${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; + } + + 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 9669a61..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'; } @@ -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: