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