Implemented attachment features to profile screens

feature/passo/PASSO-#1-Sync-data-from-device-to-postgre-and-vice-versa
PGAN-MIS 2023-08-01 16:20:38 +08:00
parent 603275ec6d
commit c32ed51198
33 changed files with 2586 additions and 540 deletions

View File

@ -0,0 +1,33 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="461.000000pt" height="547.000000pt" viewBox="0 0 461.000000 547.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,547.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M291 5399 c-105 -20 -175 -79 -208 -174 -17 -47 -18 -189 -18 -2515
0 -2388 1 -2467 19 -2519 25 -71 86 -132 157 -157 52 -18 121 -19 2069 -19
1948 0 2017 1 2069 19 68 23 131 85 157 152 18 47 19 105 22 1769 2 1189 -1
1740 -8 1785 -6 36 -25 94 -42 130 -28 60 -89 124 -727 761 -523 522 -709 702
-747 722 -112 59 -64 57 -1439 56 -693 -1 -1280 -5 -1304 -10z m2599 -309 c13
-8 16 -85 20 -562 5 -551 5 -553 28 -603 41 -89 97 -132 200 -154 35 -7 225
-11 565 -11 305 0 518 -4 527 -10 13 -8 15 -52 18 -275 l3 -265 -1941 0 -1940
0 2 919 c3 879 4 920 22 939 10 12 28 23 40 25 46 9 2441 6 2456 -3z m-611
-2175 c170 -41 263 -149 264 -308 1 -225 -152 -356 -418 -361 l-95 -1 -2 -185
-3 -185 -117 -3 -118 -3 0 520 0 520 33 5 c66 11 96 13 242 14 103 1 170 -3
214 -13z m1176 -5 c43 -11 80 -22 82 -24 6 -5 -37 -169 -47 -179 -4 -5 -34 0
-66 9 -41 13 -93 18 -169 18 -100 0 -116 -2 -173 -28 -115 -53 -172 -143 -180
-283 -9 -164 53 -281 180 -338 53 -24 77 -29 148 -29 125 -1 120 -6 120 124
l0 110 -85 0 -85 0 0 90 0 90 200 0 200 0 0 -274 0 -275 -52 -14 c-163 -47
-385 -56 -508 -23 -234 64 -370 247 -370 501 0 263 151 459 402 525 104 27
297 27 403 0z m-1867 -392 c-3 -391 -4 -405 -26 -459 -30 -76 -81 -132 -150
-166 -50 -24 -70 -28 -167 -31 -60 -2 -128 1 -150 5 l-40 9 1 50 c0 27 4 70 8
97 l7 47 42 -7 c66 -12 145 -7 173 12 14 9 34 35 45 58 17 39 19 71 19 415 l0
372 121 0 120 0 -3 -402z m2660 -1543 l-3 -604 -23 -23 -23 -23 -1889 0 -1889
0 -23 23 -23 23 -3 604 -3 605 1941 0 1941 0 -3 -605z"/>
<path d="M2030 2590 l0 -160 73 0 c128 1 197 52 204 154 8 109 -60 166 -199
166 l-78 0 0 -160z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,32 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="454.000000pt" height="545.000000pt" viewBox="0 0 454.000000 545.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,545.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M251 5399 c-105 -20 -175 -79 -208 -174 -17 -47 -18 -189 -18 -2515
0 -2388 1 -2467 19 -2519 25 -71 86 -132 157 -157 52 -18 121 -19 2069 -19
1948 0 2017 1 2069 19 68 23 131 85 157 152 18 47 19 105 22 1769 2 1189 -1
1740 -8 1785 -6 36 -25 94 -42 130 -28 60 -89 124 -727 761 -523 522 -709 702
-747 722 -112 59 -64 57 -1439 56 -693 -1 -1280 -5 -1304 -10z m2599 -309 c13
-8 16 -85 20 -562 5 -551 5 -553 28 -603 41 -89 97 -132 200 -154 35 -7 225
-11 565 -11 305 0 518 -4 527 -10 13 -8 15 -52 18 -275 l3 -265 -1941 0 -1940
0 2 919 c3 879 4 920 22 939 10 12 28 23 40 25 46 9 2441 6 2456 -3z m-520
-2165 c77 -8 195 -45 251 -81 139 -88 209 -229 209 -424 0 -175 -55 -317 -158
-411 -117 -107 -225 -140 -480 -146 -100 -3 -207 -2 -237 2 l-55 6 0 520 c0
448 2 520 15 525 21 9 271 22 335 18 30 -2 84 -6 120 -9z m-898 -6 c180 -38
278 -166 265 -349 -14 -211 -175 -330 -449 -330 l-68 0 0 -185 0 -185 -120 0
-120 0 2 523 3 522 45 6 c81 11 388 9 442 -2z m2168 -89 l0 -100 -205 0 -205
0 0 -120 0 -120 190 0 190 0 0 -100 0 -100 -190 0 -190 0 0 -210 0 -210 -120
0 -120 0 0 530 0 530 325 0 325 0 0 -100z m608 -1855 l-3 -604 -23 -23 -23
-23 -1889 0 -1889 0 -23 23 -23 23 -3 604 -3 605 1941 0 1941 0 -3 -605z"/>
<path d="M2105 2739 c-3 -8 -4 -167 -3 -354 l3 -340 86 1 c115 1 179 24 245
89 72 70 97 138 98 270 2 163 -46 255 -160 311 -47 23 -73 28 -160 32 -82 4
-105 2 -109 -9z"/>
<path d="M1180 2590 l0 -160 75 0 c87 0 128 14 170 59 25 27 30 41 33 99 4 63
2 71 -24 101 -39 47 -80 61 -174 61 l-80 0 0 -160z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,28 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="228.000000pt" height="317.000000pt" viewBox="0 0 228.000000 317.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,317.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M95 3120 c-11 -4 -31 -20 -45 -35 l-25 -27 -3 -1465 c-2 -1456 -2
-1466 18 -1498 43 -69 -23 -66 1109 -63 l1021 3 37 38 38 37 3 1003 c1 551 0
1013 -3 1027 -7 28 -923 952 -967 976 -23 12 -121 14 -595 13 -312 0 -577 -4
-588 -9z m1085 -585 l5 -470 473 -3 472 -2 -2 -298 -3 -297 -992 -3 -993 -2 0
768 c0 423 3 772 7 775 3 4 236 6 517 5 l511 -3 5 -470z m-465 -1301 c54 -27
77 -63 83 -128 9 -109 -73 -186 -197 -186 l-61 0 0 -90 0 -90 -60 0 -60 0 0
253 c0 140 3 257 8 261 4 5 61 6 128 4 92 -4 130 -9 159 -24z m378 -146 l112
-171 5 169 5 169 53 3 52 3 0 -260 0 -261 -58 0 -59 0 -116 178 -116 177 -1
-177 0 -178 -55 0 -55 0 0 260 0 260 61 0 60 0 112 -172z m697 163 c22 -6 25
-13 28 -60 3 -48 1 -53 -15 -46 -50 19 -129 27 -170 16 -107 -29 -154 -168
-93 -269 27 -44 70 -64 133 -60 l52 3 3 53 3 52 -50 0 -51 0 0 45 0 45 110 0
110 0 0 -109 c0 -60 -4 -121 -9 -134 -14 -36 -70 -52 -181 -52 -86 0 -100 3
-147 29 -88 49 -129 131 -121 245 8 107 63 187 158 230 35 16 65 20 130 20 47
-1 96 -4 110 -8z m340 -906 l0 -185 -995 0 -995 0 0 185 0 185 995 0 995 0 0
-185z"/>
<path d="M540 1089 l0 -80 51 3 c67 4 95 36 85 93 -6 29 -14 39 -41 51 -19 8
-48 14 -65 14 l-30 0 0 -81z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -11,8 +11,42 @@ PODS:
- SwiftProtobuf - SwiftProtobuf
- device_info (0.0.1): - device_info (0.0.1):
- Flutter - 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): - easy_app_installer (0.0.1):
- Flutter - Flutter
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0) - Flutter (1.0.0)
- fluttertoast (0.0.2): - fluttertoast (0.0.2):
- Flutter - Flutter
@ -36,6 +70,9 @@ PODS:
- Flutter - Flutter
- rive_common (0.0.1): - rive_common (0.0.1):
- Flutter - Flutter
- SDWebImage (5.17.0):
- SDWebImage/Core (= 5.17.0)
- SDWebImage/Core (5.17.0)
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@ -43,6 +80,7 @@ PODS:
- Flutter - Flutter
- FMDB (>= 2.7.5) - FMDB (>= 2.7.5)
- SwiftProtobuf (1.20.3) - SwiftProtobuf (1.20.3)
- SwiftyGif (5.4.4)
- Toast (4.0.0) - Toast (4.0.0)
DEPENDENCIES: DEPENDENCIES:
@ -52,6 +90,7 @@ DEPENDENCIES:
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
- device_info (from `.symlinks/plugins/device_info/ios`) - device_info (from `.symlinks/plugins/device_info/ios`)
- easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`) - easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- location (from `.symlinks/plugins/location/ios`) - location (from `.symlinks/plugins/location/ios`)
@ -66,9 +105,13 @@ DEPENDENCIES:
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- DKImagePickerController
- DKPhotoGallery
- FMDB - FMDB
- MTBBarcodeScanner - MTBBarcodeScanner
- SDWebImage
- SwiftProtobuf - SwiftProtobuf
- SwiftyGif
- Toast - Toast
EXTERNAL SOURCES: EXTERNAL SOURCES:
@ -84,6 +127,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/device_info/ios" :path: ".symlinks/plugins/device_info/ios"
easy_app_installer: easy_app_installer:
:path: ".symlinks/plugins/easy_app_installer/ios" :path: ".symlinks/plugins/easy_app_installer/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
fluttertoast: fluttertoast:
@ -113,7 +158,10 @@ SPEC CHECKSUMS:
audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
easy_app_installer: 29abe397da7d86721fee853281202f414373f45c easy_app_installer: 29abe397da7d86721fee853281202f414373f45c
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
@ -125,9 +173,11 @@ SPEC CHECKSUMS:
permission_handler_apple: 8f116445eff3c0e7c65ad60f5fef5490aa94b4e4 permission_handler_apple: 8f116445eff3c0e7c65ad60f5fef5490aa94b4e4
platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5
rive_common: 60ae7896ab40f9513974f36f015de33f70d2c5c5 rive_common: 60ae7896ab40f9513974f36f015de33f70d2c5c5
SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3

View File

@ -3,6 +3,9 @@ import 'package:equatable/equatable.dart';
import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/educational_background.dart';
import 'package:unit2/sevices/profile/education_services.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_event.dart';
part 'education_state.dart'; part 'education_state.dart';
@ -11,20 +14,25 @@ class EducationBloc extends Bloc<EducationEvent, EducationState> {
List<School> schools = []; List<School> schools = [];
List<Course> programs = []; List<Course> programs = [];
List<Honor> honors = []; List<Honor> honors = [];
List<AttachmentCategory> attachmentCategories = [];
EducationBloc() : super(EducationInitial()) { EducationBloc() : super(EducationInitial()) {
on<GetEducationalBackground>((event, emit) async { on<GetEducationalBackground>((event, emit) async {
emit(EducationalBackgroundLoadingState()); emit(EducationalBackgroundLoadingState());
try { try {
if (attachmentCategories.isEmpty) {
attachmentCategories =
await AttachmentServices.instance.getCategories();
}
if (educationalBackgrounds.isEmpty) { if (educationalBackgrounds.isEmpty) {
List<EducationalBackground> educations = await EducationService List<EducationalBackground> educations = await EducationService
.instace .instace
.getEducationalBackground(event.profileId, event.token); .getEducationalBackground(event.profileId, event.token);
educationalBackgrounds = educations; educationalBackgrounds = educations;
emit(EducationalBackgroundLoadedState( emit(EducationalBackgroundLoadedState(
educationalBackground: educationalBackgrounds)); educationalBackground: educationalBackgrounds, attachmentCategory: attachmentCategories));
} else { } else {
emit(EducationalBackgroundLoadedState( emit(EducationalBackgroundLoadedState(
educationalBackground: educationalBackgrounds)); educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories));
} }
} catch (e) { } catch (e) {
emit(EducationalBackgroundErrorState(message: e.toString())); emit(EducationalBackgroundErrorState(message: e.toString()));
@ -89,7 +97,7 @@ class EducationBloc extends Bloc<EducationEvent, EducationState> {
////LOAD ////LOAD
on<LoadEducations>((event, emit) { on<LoadEducations>((event, emit) {
emit(EducationalBackgroundLoadedState( emit(EducationalBackgroundLoadedState(
educationalBackground: educationalBackgrounds)); educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories));
}); });
//// SHOW EDIT FORM //// SHOW EDIT FORM
on<ShowEditEducationForm>((event, emit) async { on<ShowEditEducationForm>((event, emit) async {
@ -134,5 +142,25 @@ class EducationBloc extends Bloc<EducationEvent, EducationState> {
emit(EducationalBackgroundErrorState(message: e.toString())); emit(EducationalBackgroundErrorState(message: e.toString()));
} }
}); });
////Add attachment
on<AddAttachment>((event, emit) async {
emit(EducationalBackgroundLoadingState());
try {
Map<dynamic, dynamic> 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()));
}
});
} }
} }

View File

@ -63,3 +63,14 @@ class DeleteEducation extends EducationEvent{
@override @override
List<Object> get props => [educationalBackground, profileId, token]; List<Object> get props => [educationalBackground, profileId, token];
} }
////Add attachment
class AddAttachment extends EducationEvent{
final String categoryId;
final String attachmentModule;
final List<String> 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<Object> get props => [categoryId,attachmentModule,filePaths, token,profileId];
}

View File

@ -10,8 +10,9 @@ abstract class EducationState extends Equatable {
class EducationInitial extends EducationState {} class EducationInitial extends EducationState {}
class EducationalBackgroundLoadedState extends EducationState { class EducationalBackgroundLoadedState extends EducationState {
final List< AttachmentCategory> attachmentCategory;
final List<EducationalBackground> educationalBackground; final List<EducationalBackground> educationalBackground;
const EducationalBackgroundLoadedState({required this.educationalBackground}); const EducationalBackgroundLoadedState({required this.educationalBackground, required this.attachmentCategory});
@override @override
List<Object> get props => [educationalBackground]; List<Object> get props => [educationalBackground];
} }

View File

@ -1,9 +1,11 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:unit2/utils/attachment_categories.dart';
import '../../../model/location/city.dart'; import '../../../model/location/city.dart';
import '../../../model/location/country.dart'; import '../../../model/location/country.dart';
import '../../../model/location/provinces.dart'; import '../../../model/location/provinces.dart';
import '../../../model/location/region.dart'; import '../../../model/location/region.dart';
import '../../../model/profile/attachment.dart';
import '../../../model/profile/eligibility.dart'; import '../../../model/profile/eligibility.dart';
import '../../../model/utils/eligibility.dart'; import '../../../model/utils/eligibility.dart';
import '../../../sevices/profile/eligibility_services.dart'; import '../../../sevices/profile/eligibility_services.dart';
@ -18,10 +20,13 @@ class EligibilityBloc extends Bloc<EligibilityEvent, EligibilityState> {
List<Region> globalRegions = []; List<Region> globalRegions = [];
List<Eligibility> globalEligibilities = []; List<Eligibility> globalEligibilities = [];
List<EligibityCert> eligibilities = []; List<EligibityCert> eligibilities = [];
List<AttachmentCategory> attachmentCategories = [];
//// LOAD ELIGIBILTY //// LOAD ELIGIBILTY
on<LoadEligibility>((event, emit) { on<LoadEligibility>((event, emit) {
emit(EligibilityLoadingState()); emit(EligibilityLoadingState());
emit(EligibilityLoaded(eligibilities: eligibilities)); emit(EligibilityLoaded(
eligibilities: eligibilities,
attachmentCategory: attachmentCategories));
}); });
//// DELETE //// DELETE
@ -48,13 +53,21 @@ class EligibilityBloc extends Bloc<EligibilityEvent, EligibilityState> {
//// GET ELIGIBILITY //// GET ELIGIBILITY
on<GetEligibilities>((event, emit) async { on<GetEligibilities>((event, emit) async {
try { try {
if (attachmentCategories.isEmpty) {
attachmentCategories =
await AttachmentServices.instance.getCategories();
}
if (eligibilities.isNotEmpty) { if (eligibilities.isNotEmpty) {
emit(EligibilityLoaded(eligibilities: eligibilities)); emit(EligibilityLoaded(
eligibilities: eligibilities,
attachmentCategory: attachmentCategories));
} else { } else {
emit(EligibilityLoadingState()); emit(EligibilityLoadingState());
eligibilities = await EligibilityService.instance eligibilities = await EligibilityService.instance
.getEligibilities(event.profileId, event.token); .getEligibilities(event.profileId, event.token);
emit(EligibilityLoaded(eligibilities: eligibilities)); emit(EligibilityLoaded(
eligibilities: eligibilities,
attachmentCategory: attachmentCategories));
} }
} catch (e) { } catch (e) {
emit(EligibilityErrorState(message: e.toString())); emit(EligibilityErrorState(message: e.toString()));
@ -208,5 +221,48 @@ class EligibilityBloc extends Bloc<EligibilityEvent, EligibilityState> {
emit(const EligibilityErrorState( emit(const EligibilityErrorState(
message: "Something went wrong. Please try again")); message: "Something went wrong. Please try again"));
}); });
////Add attachment
on<AddAttachment>((event, emit) async {
emit(EligibilityLoadingState());
try {
Map<dynamic, dynamic> 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<DeleteAttachment>((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()));
// }
});
} }
} }

View File

@ -55,6 +55,7 @@ class DeleteEligibility extends EligibilityEvent {
final String profileId; final String profileId;
final int eligibilityId; final int eligibilityId;
final String token; final String token;
const DeleteEligibility( const DeleteEligibility(
{ {
required this.eligibilityId, required this.eligibilityId,
@ -68,4 +69,30 @@ class CallErrorState extends EligibilityEvent{
} }
class AddAttachment extends EligibilityEvent{
final String categoryId;
final String attachmentModule;
final List<String> 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<Object> 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});
}

View File

@ -9,7 +9,6 @@ abstract class EligibilityState extends Equatable {
class EligibilityInitial extends EligibilityState {} class EligibilityInitial extends EligibilityState {}
class EditEligibilityState extends EligibilityState { class EditEligibilityState extends EligibilityState {
final EligibityCert eligibityCert; final EligibityCert eligibityCert;
final List<Eligibility> eligibilities; final List<Eligibility> eligibilities;
@ -23,6 +22,7 @@ class EditEligibilityState extends EligibilityState {
final Province? currentProvince; final Province? currentProvince;
final CityMunicipality? currentCity; final CityMunicipality? currentCity;
final Country selectedCountry; final Country selectedCountry;
const EditEligibilityState({ const EditEligibilityState({
required this.provinces, required this.provinces,
required this.cities, required this.cities,
@ -61,6 +61,7 @@ class AddEligibilityState extends EligibilityState {
@override @override
List<Object> get props => [eligibilities, countries, regions]; List<Object> get props => [eligibilities, countries, regions];
} }
class EligibilityEditedState extends EligibilityState { class EligibilityEditedState extends EligibilityState {
final Map<dynamic, dynamic> response; final Map<dynamic, dynamic> response;
const EligibilityEditedState({required this.response}); const EligibilityEditedState({required this.response});
@ -75,9 +76,9 @@ class EligibilityAddedState extends EligibilityState{
@override @override
List<Object> get props => [response]; List<Object> get props => [response];
} }
class EligibilityLoadingState extends EligibilityState{
} class EligibilityLoadingState extends EligibilityState {}
class EligibilityErrorState extends EligibilityState { class EligibilityErrorState extends EligibilityState {
final String message; final String message;
const EligibilityErrorState({required this.message}); const EligibilityErrorState({required this.message});
@ -86,8 +87,9 @@ class EligibilityErrorState extends EligibilityState{
} }
class EligibilityLoaded extends EligibilityState { class EligibilityLoaded extends EligibilityState {
final List< AttachmentCategory> attachmentCategory;
final List<EligibityCert> eligibilities; final List<EligibityCert> eligibilities;
const EligibilityLoaded({required this.eligibilities}); const EligibilityLoaded({required this.eligibilities, required this.attachmentCategory});
@override @override
List<Object> get props => [eligibilities]; List<Object> get props => [eligibilities];
} }

View File

@ -6,9 +6,11 @@ import 'package:unit2/sevices/profile/learningDevelopment_service.dart';
import '../../../model/location/barangay.dart'; import '../../../model/location/barangay.dart';
import '../../../model/location/city.dart'; import '../../../model/location/city.dart';
import '../../../model/location/provinces.dart'; import '../../../model/location/provinces.dart';
import '../../../model/profile/attachment.dart';
import '../../../model/profile/learning_development.dart'; import '../../../model/profile/learning_development.dart';
import '../../../model/utils/agency.dart'; import '../../../model/utils/agency.dart';
import '../../../model/utils/category.dart'; import '../../../model/utils/category.dart';
import '../../../utils/attachment_categories.dart';
import '../../../utils/location_utilities.dart'; import '../../../utils/location_utilities.dart';
import '../../../utils/profile_utilities.dart'; import '../../../utils/profile_utilities.dart';
part 'learning_development_event.dart'; part 'learning_development_event.dart';
@ -27,7 +29,7 @@ class LearningDevelopmentBloc
List<Barangay> globalBarangay = []; List<Barangay> globalBarangay = [];
List<Agency> agencies = []; List<Agency> agencies = [];
List<Category> agencyCategory = []; List<Category> agencyCategory = [];
List<AttachmentCategory> attachmentCategories = [];
Region? currentRegion; Region? currentRegion;
Country? currentCountry; Country? currentCountry;
Province? currentProvince; Province? currentProvince;
@ -37,12 +39,16 @@ class LearningDevelopmentBloc
on<GetLearningDevelopments>((event, emit) async { on<GetLearningDevelopments>((event, emit) async {
emit(LearningDevelopmentLoadingState()); emit(LearningDevelopmentLoadingState());
try { try {
if (attachmentCategories.isEmpty) {
attachmentCategories =
await AttachmentServices.instance.getCategories();
}
List<LearningDevelopement> learnings = await LearningDevelopmentServices List<LearningDevelopement> learnings = await LearningDevelopmentServices
.instance .instance
.getLearningDevelopments(event.profileId, event.token); .getLearningDevelopments(event.profileId, event.token);
learningsAndDevelopments = learnings; learningsAndDevelopments = learnings;
emit(LearningDevelopmentLoadedState( emit(LearningDevelopmentLoadedState(
learningsAndDevelopment: learningsAndDevelopments)); learningsAndDevelopment: learningsAndDevelopments,attachmentCategory: attachmentCategories));
} catch (e) { } catch (e) {
emit(LearningDevelopmentErrorState(message: e.toString())); emit(LearningDevelopmentErrorState(message: e.toString()));
} }
@ -50,7 +56,7 @@ class LearningDevelopmentBloc
////load ////load
on<LoadLearniningDevelopment>((event, emit) { on<LoadLearniningDevelopment>((event, emit) {
emit(LearningDevelopmentLoadedState( emit(LearningDevelopmentLoadedState(
learningsAndDevelopment: learningsAndDevelopments)); learningsAndDevelopment: learningsAndDevelopments,attachmentCategory: attachmentCategories));
}); });
//// show add form //// show add form
on<ShowAddLearningDevelopmentForm>((event, emit) async { on<ShowAddLearningDevelopmentForm>((event, emit) async {
@ -266,5 +272,25 @@ class LearningDevelopmentBloc
on<CallErrorState>((event, emit) { on<CallErrorState>((event, emit) {
emit(LearningDevelopmentErrorState(message: event.message)); emit(LearningDevelopmentErrorState(message: event.message));
}); });
////Add attachment
on<AddAttachment>((event, emit) async {
emit(LearningDevelopmentLoadingState());
try {
Map<dynamic, dynamic> 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()));
}
});
} }
} }

View File

@ -66,3 +66,14 @@ class CallErrorState extends LearningDevelopmentEvent{
final String message; final String message;
const CallErrorState({required this.message}); const CallErrorState({required this.message});
} }
class AddAttachment extends LearningDevelopmentEvent{
final String categoryId;
final String attachmentModule;
final List<String> 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<Object> get props => [categoryId,attachmentModule,filePaths, token,profileId];
}

View File

@ -10,8 +10,9 @@ abstract class LearningDevelopmentState extends Equatable {
class LearningDevelopmentInitial extends LearningDevelopmentState {} class LearningDevelopmentInitial extends LearningDevelopmentState {}
class LearningDevelopmentLoadedState extends LearningDevelopmentState { class LearningDevelopmentLoadedState extends LearningDevelopmentState {
final List< AttachmentCategory> attachmentCategory;
final List<LearningDevelopement> learningsAndDevelopment; final List<LearningDevelopement> learningsAndDevelopment;
const LearningDevelopmentLoadedState({required this.learningsAndDevelopment}); const LearningDevelopmentLoadedState({required this.learningsAndDevelopment, required this.attachmentCategory});
@override @override
List<Object> get props => [learningsAndDevelopment]; List<Object> get props => [learningsAndDevelopment];
} }

View File

@ -72,3 +72,13 @@ class DeleteVoluntaryWork extends VoluntaryWorkEvent {
@override @override
List<Object> get props => [profileId, token, work]; List<Object> get props => [profileId, token, work];
} }
class AddAttachment extends VoluntaryWorkEvent{
final String categoryId;
final String attachmentModule;
final List<String> 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<Object> get props => [categoryId,attachmentModule,filePaths, token,profileId];
}

View File

@ -8,7 +8,9 @@ import 'package:unit2/model/utils/position.dart';
import 'package:unit2/sevices/profile/work_history_services.dart'; import 'package:unit2/sevices/profile/work_history_services.dart';
import 'package:unit2/utils/profile_utilities.dart'; import 'package:unit2/utils/profile_utilities.dart';
import '../../../model/profile/attachment.dart';
import '../../../model/utils/category.dart'; import '../../../model/utils/category.dart';
import '../../../utils/attachment_categories.dart';
part 'workHistory_event.dart'; part 'workHistory_event.dart';
part 'workHistory_state.dart'; part 'workHistory_state.dart';
@ -19,17 +21,22 @@ class WorkHistoryBloc extends Bloc<WorkHistorytEvent, WorkHistoryState> {
List<Agency> agencies = []; List<Agency> agencies = [];
List<AppoinemtStatus> appointmentStatus = []; List<AppoinemtStatus> appointmentStatus = [];
List<Category> agencyCategory = []; List<Category> agencyCategory = [];
List<AttachmentCategory> attachmentCategories = [];
WorkHistoryBloc() : super(EducationInitial()) { WorkHistoryBloc() : super(EducationInitial()) {
////GET WORK HISTORIES ////GET WORK HISTORIES
on<GetWorkHistories>((event, emit) async { on<GetWorkHistories>((event, emit) async {
emit(WorkHistoryLoadingState()); emit(WorkHistoryLoadingState());
try { try {
if (attachmentCategories.isEmpty) {
attachmentCategories =
await AttachmentServices.instance.getCategories();
}
if (workExperiences.isEmpty) { if (workExperiences.isEmpty) {
List<WorkHistory> works = await WorkHistoryService.instance List<WorkHistory> works = await WorkHistoryService.instance
.getWorkExperiences(event.profileId, event.token); .getWorkExperiences(event.profileId, event.token);
workExperiences = works; workExperiences = works;
} }
emit(WorkHistoryLoaded(workExperiences: workExperiences)); emit(WorkHistoryLoaded(workExperiences: workExperiences, attachmentCategory: attachmentCategories));
} catch (e) { } catch (e) {
emit(WorkHistoryErrorState(message: e.toString())); emit(WorkHistoryErrorState(message: e.toString()));
} }
@ -37,7 +44,7 @@ class WorkHistoryBloc extends Bloc<WorkHistorytEvent, WorkHistoryState> {
///// LOAD WORK HISTORIES ///// LOAD WORK HISTORIES
on<LoadWorkHistories>((event, emit) { on<LoadWorkHistories>((event, emit) {
emit(WorkHistoryLoadingState()); emit(WorkHistoryLoadingState());
emit(WorkHistoryLoaded(workExperiences: workExperiences)); emit(WorkHistoryLoaded(workExperiences: workExperiences,attachmentCategory: attachmentCategories));
}); });
////DELETE ////DELETE
on<DeleteWorkHistory>((event, emit) async { on<DeleteWorkHistory>((event, emit) async {
@ -182,5 +189,25 @@ class WorkHistoryBloc extends Bloc<WorkHistorytEvent, WorkHistoryState> {
emit(WorkHistoryErrorState(message: e.toString())); emit(WorkHistoryErrorState(message: e.toString()));
} }
}); });
////Add Attachment
on<AddAttachment>((event, emit) async {
emit(WorkHistoryLoadingState());
try {
Map<dynamic, dynamic> 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()));
}
});
} }
} }

View File

@ -60,5 +60,15 @@ class AddWorkHostory extends WorkHistorytEvent{
@override @override
List<Object> get props => [workHistory,profileId,token,isPrivate]; List<Object> get props => [workHistory,profileId,token,isPrivate];
} }
class AddAttachment extends WorkHistorytEvent{
final String categoryId;
final String attachmentModule;
final List<String> 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<Object> get props => [categoryId,attachmentModule,filePaths, token,profileId];
}

View File

@ -11,7 +11,8 @@ class EducationInitial extends WorkHistoryState {}
class WorkHistoryLoaded extends WorkHistoryState{ class WorkHistoryLoaded extends WorkHistoryState{
final List<WorkHistory> workExperiences; final List<WorkHistory> workExperiences;
const WorkHistoryLoaded({required this.workExperiences}); final List< AttachmentCategory> attachmentCategory;
const WorkHistoryLoaded({required this.workExperiences,required this.attachmentCategory});
@override @override
List<Object> get props => [workExperiences]; List<Object> get props => [workExperiences];
} }

View File

@ -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<String, dynamic> 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<String, dynamic> 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<String, dynamic> json) => AttachmentCategory(
id: json["id"],
subclass: Subclass.fromJson(json["subclass"]),
description: json["description"],
);
Map<String, dynamic> 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<String, dynamic> json) => Subclass(
id: json["id"],
name: json["name"],
attachmentClass: AttachmentClass.fromJson(json["attachment_class"]),
);
Map<String, dynamic> 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<String, dynamic> json) =>
AttachmentClass(
id: json["id"],
name: json["name"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
};
}

View File

@ -4,6 +4,8 @@
import 'dart:convert'; import 'dart:convert';
import 'package:unit2/model/profile/attachment.dart';
EducationalBackground educationalBackgroundFromJson(String str) => EducationalBackground.fromJson(json.decode(str)); EducationalBackground educationalBackgroundFromJson(String str) => EducationalBackground.fromJson(json.decode(str));
String educationalBackgroundToJson(EducationalBackground data) => json.encode(data.toJson()); String educationalBackgroundToJson(EducationalBackground data) => json.encode(data.toJson());
@ -24,7 +26,7 @@ class EducationalBackground {
final List<Honor>? honors; final List<Honor>? honors;
final Education? education; final Education? education;
final String? periodTo; final String? periodTo;
final dynamic attachments; final List<Attachment>? attachments;
final String? periodFrom; final String? periodFrom;
final int? unitsEarned; final int? unitsEarned;
final String? yearGraduated; final String? yearGraduated;
@ -34,7 +36,7 @@ class EducationalBackground {
honors: json["honors"] == null ? [] : List<Honor>.from(json["honors"]!.map((x) => Honor.fromJson(x))), honors: json["honors"] == null ? [] : List<Honor>.from(json["honors"]!.map((x) => Honor.fromJson(x))),
education: json["education"] == null ? null : Education.fromJson(json["education"]), education: json["education"] == null ? null : Education.fromJson(json["education"]),
periodTo: json["period_to"], periodTo: json["period_to"],
attachments: json["attachments"], attachments: json['attachments'] ==null?null: List<Attachment>.from(json["attachments"].map((x) => Attachment.fromJson(x))),
periodFrom: json["period_from"], periodFrom: json["period_from"],
unitsEarned: json["units_earned"], unitsEarned: json["units_earned"],
yearGraduated: json["year_graduated"], yearGraduated: json["year_graduated"],

View File

@ -6,6 +6,7 @@ import 'package:meta/meta.dart';
import 'dart:convert'; import 'dart:convert';
import 'package:unit2/model/location/region.dart'; import 'package:unit2/model/location/region.dart';
import 'package:unit2/model/profile/attachment.dart';
import '../location/address_category.dart'; import '../location/address_category.dart';
import '../location/city.dart'; import '../location/city.dart';
@ -33,7 +34,8 @@ class EligibityCert {
final int? id; final int? id;
final double? rating; final double? rating;
final DateTime? examDate; final DateTime? examDate;
final dynamic attachments; final List<Attachment>? attachments;
final Eligibility? eligibility; final Eligibility? eligibility;
final ExamAddress? examAddress; final ExamAddress? examAddress;
final DateTime? validityDate; final DateTime? validityDate;
@ -45,7 +47,7 @@ class EligibityCert {
examDate: json['exam_date'] == null examDate: json['exam_date'] == null
? null ? null
: DateTime.parse(json["exam_date"]), : DateTime.parse(json["exam_date"]),
attachments: null, attachments: json['attachments'] ==null?null: List<Attachment>.from(json["attachments"].map((x) => Attachment.fromJson(x))),
eligibility: json['eligibility'] == null eligibility: json['eligibility'] == null
? null ? null
: Eligibility.fromJson(json["eligibility"]), : Eligibility.fromJson(json["eligibility"]),

View File

@ -10,6 +10,7 @@ import '../location/city.dart';
import '../location/country.dart'; import '../location/country.dart';
import '../utils/agency.dart'; import '../utils/agency.dart';
import '../utils/industry_class.dart'; import '../utils/industry_class.dart';
import 'attachment.dart';
LearningDevelopement learningDevelopementFromJson(String str) => LearningDevelopement.fromJson(json.decode(str)); LearningDevelopement learningDevelopementFromJson(String str) => LearningDevelopement.fromJson(json.decode(str));
@ -23,13 +24,13 @@ class LearningDevelopement {
this.totalHoursAttended, this.totalHoursAttended,
}); });
final dynamic attachments; final List<Attachment>? attachments;
final Agency? sponsoredBy; final Agency? sponsoredBy;
final ConductedTraining? conductedTraining; final ConductedTraining? conductedTraining;
final double? totalHoursAttended; final double? totalHoursAttended;
factory LearningDevelopement.fromJson(Map<String, dynamic> json) => LearningDevelopement( factory LearningDevelopement.fromJson(Map<String, dynamic> json) => LearningDevelopement(
attachments: json["attachments"], attachments: json['attachments'] ==null?null: List<Attachment>.from(json["attachments"].map((x) => Attachment.fromJson(x))),
sponsoredBy: json["sponsored_by"] == null ? null : Agency.fromJson(json["sponsored_by"]), sponsoredBy: json["sponsored_by"] == null ? null : Agency.fromJson(json["sponsored_by"]),
conductedTraining: json["conducted_training"] == null ? null : ConductedTraining.fromJson(json["conducted_training"]), conductedTraining: json["conducted_training"] == null ? null : ConductedTraining.fromJson(json["conducted_training"]),
totalHoursAttended: json["total_hours_attended"], totalHoursAttended: json["total_hours_attended"],

View File

@ -8,6 +8,7 @@ import '../utils/agency.dart';
import '../utils/category.dart'; import '../utils/category.dart';
import '../utils/industry_class.dart'; import '../utils/industry_class.dart';
import '../utils/position.dart'; import '../utils/position.dart';
import 'attachment.dart';
WorkHistory workHistoryFromJson(String str) => WorkHistory.fromJson(json.decode(str)); WorkHistory workHistoryFromJson(String str) => WorkHistory.fromJson(json.decode(str));
@ -21,7 +22,7 @@ class WorkHistory {
this.toDate, this.toDate,
this.position, this.position,
this.fromDate, this.fromDate,
// this.attachments, this.attachments,
this.salaryGrade, this.salaryGrade,
this.monthlySalary, this.monthlySalary,
this.appointmentStatus, this.appointmentStatus,
@ -33,7 +34,7 @@ class WorkHistory {
final DateTime? toDate; final DateTime? toDate;
final Position? position; final Position? position;
final DateTime? fromDate; final DateTime? fromDate;
// final dynamic attachments; final List<Attachment>? attachments;
final int? salaryGrade; final int? salaryGrade;
final double? monthlySalary; final double? monthlySalary;
final String? appointmentStatus; final String? appointmentStatus;
@ -45,7 +46,7 @@ class WorkHistory {
toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]),
position: json["position"] == null ? null : Position.fromJson(json["position"]), position: json["position"] == null ? null : Position.fromJson(json["position"]),
fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]),
// attachments: json["attachments"], attachments: json['attachments'] ==null?null: List<Attachment>.from(json["attachments"].map((x) => Attachment.fromJson(x))),
salaryGrade: json["salary_grade"], salaryGrade: json["salary_grade"],
monthlySalary: json["monthly_salary"], monthlySalary: json["monthly_salary"],
appointmentStatus: json["appointment_status"], appointmentStatus: json["appointment_status"],

View File

@ -1,8 +1,12 @@
import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:app_popup_menu/app_popup_menu.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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_progress_hud/flutter_progress_hud.dart';
import 'package:flutter_spinkit/flutter_spinkit.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/profile/profile_bloc.dart';
import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart';
import 'package:unit2/model/profile/educational_background.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/Leadings/add_leading.dart';
import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/empty_data.dart';
import 'package:unit2/widgets/error_state.dart'; import 'package:unit2/widgets/error_state.dart';
import '../../../bloc/profile/education/education_bloc.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/alerts.dart';
import '../../../utils/global.dart';
import '../../../widgets/Leadings/close_leading.dart'; import '../../../widgets/Leadings/close_leading.dart';
import '../shared/multiple_attachment.dart';
import '../shared/single_attachment.dart';
import 'education/edit_modal.dart'; import 'education/edit_modal.dart';
class EducationScreen extends StatelessWidget { class EducationScreen extends StatelessWidget {
const EducationScreen({super.key}); const EducationScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final parent = context;
FilePickerResult? result;
AttachmentCategory? selectedAttachmentCategory;
List<AttachmentCategory> attachmentCategories = [];
int profileId; int profileId;
String? token; String? token;
return Scaffold( return Scaffold(
@ -144,6 +160,11 @@ class EducationScreen extends StatelessWidget {
}, },
builder: (context, state) { builder: (context, state) {
if (state is EducationalBackgroundLoadedState) { if (state is EducationalBackgroundLoadedState) {
for (var cat in state.attachmentCategory) {
if (cat.subclass.id == 1) {
attachmentCategories.add(cat);
}
}
if (state.educationalBackground.isNotEmpty) { if (state.educationalBackground.isNotEmpty) {
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -182,12 +203,15 @@ class EducationScreen extends StatelessWidget {
decoration: box1(), decoration: box1(),
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8), horizontal: 12, vertical: 8),
child: Row( child: Column(
children: [
Row(
children: [ children: [
Expanded( Expanded(
child: Column( child: Column(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.start, MainAxisAlignment
.start,
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment CrossAxisAlignment
.start, .start,
@ -195,7 +219,8 @@ class EducationScreen extends StatelessWidget {
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Text(level, child: Text(
level,
style: Theme.of( style: Theme.of(
context) context)
.textTheme .textTheme
@ -214,11 +239,13 @@ class EducationScreen extends StatelessWidget {
), ),
Text( Text(
school, school,
style: Theme.of(context) style: Theme.of(
context)
.textTheme .textTheme
.titleMedium! .titleMedium!
.copyWith( .copyWith(
color: primary, color:
primary,
fontWeight: fontWeight:
FontWeight FontWeight
.w500), .w500),
@ -226,7 +253,8 @@ class EducationScreen extends StatelessWidget {
Container( Container(
padding: padding:
const EdgeInsets const EdgeInsets
.only(top: 8), .only(
top: 8),
child: honors child: honors
.isNotEmpty .isNotEmpty
? Column( ? Column(
@ -238,13 +266,13 @@ class EducationScreen extends StatelessWidget {
.start, .start,
children: [ children: [
const SizedBox( const SizedBox(
height: 8, height:
8,
), ),
const Text( const Text(
" honors: ", " honors: ",
style: TextStyle( style:
fontWeight: TextStyle(fontWeight: FontWeight.w600),
FontWeight.w600),
), ),
Column( Column(
children: honors children: honors
@ -270,43 +298,51 @@ class EducationScreen extends StatelessWidget {
const SizedBox( const SizedBox(
height: 5, height: 5,
), ),
Text(program), Text(
program),
], ],
), ),
]), ]),
), ),
AppPopupMenu<int>( AppPopupMenu<int>(
offset: const Offset(-10, -10), offset:
const Offset(-10, -10),
elevation: 3, elevation: 3,
onSelected: (value) { onSelected: (value) {
////delete -= = = = = = = = =>> ////delete -= = = = = = = = =>>
if (value == 2) { if (value == 2) {
confirmAlert(context, () { confirmAlert(context,
() {
final progress = final progress =
ProgressHUD.of( ProgressHUD.of(
context); context);
progress!.showWithText( progress!
.showWithText(
"Loading..."); "Loading...");
context context
.read<EducationBloc>() .read<
EducationBloc>()
.add(DeleteEducation( .add(DeleteEducation(
educationalBackground: educationalBackground:
state.educationalBackground[ state.educationalBackground[
index], index],
profileId: profileId:
profileId, profileId,
token: token!)); token:
token!));
}, "Delete?", }, "Delete?",
"Confirm Delete?"); "Confirm Delete?");
} }
if (value == 1) { if (value == 1) {
////edit -= = = = = = = = =>> ////edit -= = = = = = = = =>>
final progress = final progress =
ProgressHUD.of(context); ProgressHUD.of(
context);
progress!.showWithText( progress!.showWithText(
"Loading..."); "Loading...");
context context
.read<EducationBloc>() .read<
EducationBloc>()
.add(ShowEditEducationForm( .add(ShowEditEducationForm(
profileId: profileId:
profileId, profileId,
@ -315,6 +351,225 @@ class EducationScreen extends StatelessWidget {
state.educationalBackground[ state.educationalBackground[
index])); 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<Color>(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<String> paths = [];
if (selectedAttachmentCategory != null) {
for (var res in result!.files) {
paths.add(res.path!);
}
Navigator.pop(context);
parent.read<EducationBloc>().add(AddAttachment(attachmentModule: state.educationalBackground[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString()));
}
},
child: const Text("Submit")),
)
]),
);
}),
);
});
}
}, },
menuItems: [ menuItems: [
popMenuItem( popMenuItem(
@ -327,8 +582,9 @@ class EducationScreen extends StatelessWidget {
icon: Icons.delete), icon: Icons.delete),
popMenuItem( popMenuItem(
text: "Attach", text: "Attach",
value: 2, value: 3,
icon: Icons.attach_file), icon: Icons
.attach_file),
], ],
icon: const Icon( icon: const Icon(
Icons.more_vert, Icons.more_vert,
@ -338,6 +594,61 @@ class EducationScreen extends StatelessWidget {
) )
], ],
), ),
////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!,
))
],
),
), ),
const SizedBox( const SizedBox(
height: 5, height: 5,

View File

@ -1,16 +1,24 @@
import 'dart:io';
import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:app_popup_menu/app_popup_menu.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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_progress_hud/flutter_progress_hud.dart';
import 'package:flutter_spinkit/flutter_spinkit.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/profile/profile_bloc.dart';
import 'package:unit2/bloc/user/user_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/model/profile/eligibility.dart';
import 'package:unit2/screens/profile/components/eligibility/add_modal.dart'; import 'package:unit2/screens/profile/components/eligibility/add_modal.dart';
import 'package:unit2/screens/profile/components/eligibility/edit_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/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/colors.dart';
import 'package:unit2/theme-data.dart/form-style.dart';
import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/global.dart';
import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/text_container.dart';
import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart';
@ -19,14 +27,20 @@ import 'package:unit2/widgets/empty_data.dart';
import 'package:unit2/widgets/error_state.dart'; import 'package:unit2/widgets/error_state.dart';
import '../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../bloc/profile/eligibility/eligibility_bloc.dart';
import '../../../utils/alerts.dart'; import '../../../utils/alerts.dart';
import '../shared/multiple_attachment.dart';
import '../shared/single_attachment.dart';
class EligibiltyScreen extends StatelessWidget { class EligibiltyScreen extends StatelessWidget {
const EligibiltyScreen({super.key}); const EligibiltyScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
BuildContext parent = context;
String? token; String? token;
int? profileId; int? profileId;
FilePickerResult? result;
AttachmentCategory? selectedAttachmentCategory;
List<AttachmentCategory> attachmentCategories = [];
return WillPopScope( return WillPopScope(
onWillPop: () async { onWillPop: () async {
@ -42,8 +56,8 @@ class EligibiltyScreen extends StatelessWidget {
: const Text(elibilityScreenTitle), : const Text(elibilityScreenTitle),
centerTitle: true, centerTitle: true,
backgroundColor: primary, backgroundColor: primary,
actions: (context.watch<EligibilityBloc>().state is EligibilityLoaded) actions:
(context.watch<EligibilityBloc>().state is EligibilityLoaded)
? [ ? [
AddLeading(onPressed: () { AddLeading(onPressed: () {
context context
@ -51,18 +65,24 @@ class EligibiltyScreen extends StatelessWidget {
.add(ShowAddEligibilityForm()); .add(ShowAddEligibilityForm());
}) })
] ]
:(context.watch<EligibilityBloc>().state is AddEligibilityState || context.watch<EligibilityBloc>().state is EditEligibilityState)? [ : (context.watch<EligibilityBloc>().state
is AddEligibilityState ||
context.watch<EligibilityBloc>().state
is EditEligibilityState)
? [
CloseLeading(onPressed: () { CloseLeading(onPressed: () {
context.read<EligibilityBloc>().add(const LoadEligibility()); context
.read<EligibilityBloc>()
.add(const LoadEligibility());
}) })
]:[], ]
: [],
), ),
body: BlocBuilder<UserBloc, UserState>( body: BlocBuilder<UserBloc, UserState>(
builder: (context, state) { builder: (context, state) {
if (state is UserLoggedIn) { if (state is UserLoggedIn) {
token = state.userData!.user!.login!.token; token = state.userData!.user!.login!.token;
profileId = profileId = state.userData!.user!.login!.user!.profileId;
state.userData!.user!.login!.user!.profileId;
return BlocBuilder<ProfileBloc, ProfileState>( return BlocBuilder<ProfileBloc, ProfileState>(
builder: (context, state) { builder: (context, state) {
if (state is ProfileLoaded) { if (state is ProfileLoaded) {
@ -84,8 +104,7 @@ class EligibiltyScreen extends StatelessWidget {
state is DeletedState || state is DeletedState ||
state is EligibilityAddedState || state is EligibilityAddedState ||
state is EligibilityEditedState || state is EligibilityEditedState ||
state is EligibilityErrorState state is EligibilityErrorState) {
) {
final progress = ProgressHUD.of(context); final progress = ProgressHUD.of(context);
progress!.dismiss(); progress!.dismiss();
} }
@ -96,15 +115,17 @@ class EligibiltyScreen extends StatelessWidget {
"Eligibility has been deleted successfully", "Eligibility has been deleted successfully",
() { () {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<EligibilityBloc>().add(const LoadEligibility( context
)); .read<EligibilityBloc>()
.add(const LoadEligibility());
}); });
} else { } else {
errorAlert(context, "Deletion Failed", errorAlert(context, "Deletion Failed",
"Error deleting eligibility", () { "Error deleting eligibility", () {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<EligibilityBloc>().add(const LoadEligibility( context
)); .read<EligibilityBloc>()
.add(const LoadEligibility());
}); });
} }
} }
@ -114,16 +135,18 @@ class EligibiltyScreen extends StatelessWidget {
successAlert(context, "Adding Successfull!", successAlert(context, "Adding Successfull!",
state.response['message'], () { state.response['message'], () {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<EligibilityBloc>().add(const LoadEligibility( context
)); .read<EligibilityBloc>()
.add(const LoadEligibility());
}); });
} else { } else {
errorAlert(context, "Adding Failed", errorAlert(context, "Adding Failed",
"Something went wrong. Please try again.", "Something went wrong. Please try again.",
() { () {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<EligibilityBloc>().add(const LoadEligibility( context
)); .read<EligibilityBloc>()
.add(const LoadEligibility());
}); });
} }
} }
@ -133,16 +156,18 @@ class EligibiltyScreen extends StatelessWidget {
successAlert(context, "Update Successfull!", successAlert(context, "Update Successfull!",
state.response['message'], () { state.response['message'], () {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<EligibilityBloc>().add(const LoadEligibility( context
)); .read<EligibilityBloc>()
.add(const LoadEligibility());
}); });
} else { } else {
errorAlert(context, "Update Failed", errorAlert(context, "Update Failed",
"Something went wrong. Please try again.", "Something went wrong. Please try again.",
() { () {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<EligibilityBloc>().add(const LoadEligibility( context
)); .read<EligibilityBloc>()
.add(const LoadEligibility());
}); });
} }
} }
@ -151,7 +176,11 @@ class EligibiltyScreen extends StatelessWidget {
return BlocBuilder<EligibilityBloc, EligibilityState>( return BlocBuilder<EligibilityBloc, EligibilityState>(
builder: (context, state) { builder: (context, state) {
if (state is EligibilityLoaded) { if (state is EligibilityLoaded) {
for (var cat in state.attachmentCategory) {
if (cat.subclass.id == 3) {
attachmentCategories.add(cat);
}
}
if (state.eligibilities.isNotEmpty) { if (state.eligibilities.isNotEmpty) {
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -164,6 +193,7 @@ class EligibiltyScreen extends StatelessWidget {
.eligibility! .eligibility!
.title; .title;
return Column( return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.start, MainAxisAlignment.start,
crossAxisAlignment: crossAxisAlignment:
@ -180,6 +210,8 @@ class EligibiltyScreen extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: Column( child: Column(
mainAxisSize:
MainAxisSize.min,
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment MainAxisAlignment
.start, .start,
@ -196,9 +228,10 @@ class EligibiltyScreen extends StatelessWidget {
.copyWith( .copyWith(
fontWeight: fontWeight:
FontWeight FontWeight
.w500,color: primary), .w500,
color:
primary),
), ),
const SizedBox( const SizedBox(
height: 5, height: 5,
), ),
@ -212,11 +245,52 @@ class EligibiltyScreen extends StatelessWidget {
height: 3, height: 3,
), ),
Text( Text(
"Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", "Rating : ${state.eligibilities[index].rating ?? 'N/A'}",
style: Theme.of( style: Theme.of(
context) context)
.textTheme .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<EligibilityBloc>().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<int>( AppPopupMenu<int>(
@ -224,29 +298,27 @@ class EligibiltyScreen extends StatelessWidget {
const Offset(-10, -10), const Offset(-10, -10),
elevation: 3, elevation: 3,
onSelected: (value) { onSelected: (value) {
////delete eligibilty-= = = = = = = = =>> ////delete eligibilty-= = = = = = = = =>>
if (value == 2) { if (value == 2) {
confirmAlert(context, confirmAlert(context,
() { () {
final progress = final progress =
ProgressHUD.of( ProgressHUD.of(
context); context);
progress!.showWithText( progress!
.showWithText(
"Loading..."); "Loading...");
BlocProvider.of< BlocProvider.of<
EligibilityBloc>( EligibilityBloc>(
context) context)
.add(DeleteEligibility( .add(DeleteEligibility(
eligibilityId: state eligibilityId: state
.eligibilities[ .eligibilities[
index] index]
.id!, .id!,
profileId: profileId:
profileId.toString(), profileId
.toString(),
token: token:
token!)); token!));
}, "Delete?", }, "Delete?",
@ -278,11 +350,231 @@ class EligibiltyScreen extends StatelessWidget {
overseas; overseas;
context context
.read<EligibilityBloc>() .read<
EligibilityBloc>()
.add(ShowEditEligibilityForm( .add(ShowEditEligibilityForm(
eligibityCert: eligibityCert:
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<Color>(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<String> paths = [];
if (selectedAttachmentCategory != null) {
for (var res in result!.files) {
paths.add(res.path!);
}
Navigator.pop(context);
parent.read<EligibilityBloc>().add(AddAttachment(attachmentModule: state.eligibilities[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString()));
}
},
child: const Text("Submit")),
)
]),
);
}),
);
});
}
}, },
menuItems: [ menuItems: [
popMenuItem( popMenuItem(
@ -295,9 +587,9 @@ class EligibiltyScreen extends StatelessWidget {
icon: Icons.delete), icon: Icons.delete),
popMenuItem( popMenuItem(
text: "Attach", text: "Attach",
value: 2, value: 3,
icon: Icons.attach_file), icon: Icons
.attach_file),
], ],
icon: const Icon( icon: const Icon(
Icons.more_vert, Icons.more_vert,
@ -310,7 +602,7 @@ class EligibiltyScreen extends StatelessWidget {
), ),
const SizedBox( const SizedBox(
height: 5, height: 5,
) ),
], ],
); );
}); });
@ -327,11 +619,19 @@ class EligibiltyScreen extends StatelessWidget {
eligibityCert: state.eligibityCert); eligibityCert: state.eligibityCert);
} }
if (state is AddEligibilityState) { if (state is AddEligibilityState) {
return AddEligibilityScreen(token: token!,profileId: profileId!,); return AddEligibilityScreen(
token: token!,
profileId: profileId!,
);
} }
if (state is EligibilityErrorState) { if (state is EligibilityErrorState) {
return SomethingWentWrong(message: state.message, onpressed: (){ return SomethingWentWrong(
context.read<EligibilityBloc>().add(GetEligibilities(token: token!,profileId: profileId!)); message: state.message,
onpressed: () {
context.read<EligibilityBloc>().add(
GetEligibilities(
token: token!,
profileId: profileId!));
}); });
} }
return Container( return Container(
@ -342,11 +642,9 @@ class EligibiltyScreen extends StatelessWidget {
}, },
), ),
); );
} }
return Container(); return Container();
} });
);
} }
return Container(); return Container();
}, },

View File

@ -1,11 +1,15 @@
import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:app_popup_menu/app_popup_menu.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter/src/widgets/placeholder.dart';
import 'package:flutter_bloc/flutter_bloc.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_progress_hud/flutter_progress_hud.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_svg/svg.dart';
import 'package:fluttericon/font_awesome_icons.dart'; import 'package:fluttericon/font_awesome_icons.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart';
import 'package:unit2/bloc/user/user_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/empty_data.dart';
import 'package:unit2/widgets/error_state.dart'; import 'package:unit2/widgets/error_state.dart';
import '../../../bloc/profile/learningDevelopment/learning_development_bloc.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 '../../../utils/alerts.dart';
import '../../../widgets/Leadings/close_leading.dart'; import '../../../widgets/Leadings/close_leading.dart';
import '../shared/multiple_attachment.dart';
import '../shared/single_attachment.dart';
import 'learning_development/add_modal.dart'; import 'learning_development/add_modal.dart';
class LearningAndDevelopmentScreen extends StatelessWidget { class LearningAndDevelopmentScreen extends StatelessWidget {
@ -32,7 +41,11 @@ class LearningAndDevelopmentScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
String token; String token;
int profileId; int profileId;
BuildContext parent = context;
DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); DateFormat dteFormat2 = DateFormat.yMMMMd('en_US');
FilePickerResult? result;
AttachmentCategory? selectedAttachmentCategory;
List<AttachmentCategory> attachmentCategories = [];
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: context.watch<LearningDevelopmentBloc>().state title: context.watch<LearningDevelopmentBloc>().state
@ -167,9 +180,13 @@ class LearningAndDevelopmentScreen extends StatelessWidget {
// TODO: implement listener // TODO: implement listener
}, },
builder: (context, state) { builder: (context, state) {
print(state);
if (state is LearningDevelopmentLoadedState) {
if (state is LearningDevelopmentLoadedState) {
for (var cat in state.attachmentCategory) {
if (cat.subclass.id == 2) {
attachmentCategories.add(cat);
}
}
if (state.learningsAndDevelopment.isNotEmpty) { if (state.learningsAndDevelopment.isNotEmpty) {
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -208,7 +225,9 @@ class LearningAndDevelopmentScreen extends StatelessWidget {
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8), horizontal: 12, vertical: 8),
width: screenWidth, width: screenWidth,
child: Row( child: Column(
children: [
Row(
children: [ children: [
Expanded( Expanded(
child: Column( child: Column(
@ -321,6 +340,226 @@ class LearningAndDevelopmentScreen extends StatelessWidget {
isOverseas: isOverseas:
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<Color>(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<String> paths = [];
if (selectedAttachmentCategory != null) {
for (var res in result!.files) {
paths.add(res.path!);
}
Navigator.pop(context);
parent.read<LearningDevelopmentBloc>().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: [ menuItems: [
popMenuItem( popMenuItem(
@ -333,7 +572,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget {
icon: Icons.delete), icon: Icons.delete),
popMenuItem( popMenuItem(
text: "Attach", text: "Attach",
value: 2, value: 3,
icon: Icons.attach_file), icon: Icons.attach_file),
], ],
icon: const Icon( icon: const Icon(
@ -344,6 +583,47 @@ class LearningAndDevelopmentScreen extends StatelessWidget {
) )
], ],
), ),
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!,
))
],
),
), ),
const SizedBox( const SizedBox(
height: 8, height: 8,

View File

@ -1,10 +1,14 @@
import 'dart:io'; import 'dart:io';
import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:app_popup_menu/app_popup_menu.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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_progress_hud/flutter_progress_hud.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_svg/svg.dart';
import 'package:fluttericon/font_awesome_icons.dart'; import 'package:fluttericon/font_awesome_icons.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart';
import 'package:unit2/bloc/user/user_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 'package:unit2/widgets/error_state.dart';
import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; import '../../../bloc/profile/workHistory/workHistory_bloc.dart';
import '../../../model/profile/attachment.dart';
import '../../../model/profile/work_history.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/alerts.dart';
import '../../../utils/global.dart'; import '../../../utils/global.dart';
import '../shared/multiple_attachment.dart';
import '../shared/single_attachment.dart';
class WorkHistoryScreen extends StatelessWidget { class WorkHistoryScreen extends StatelessWidget {
const WorkHistoryScreen({super.key}); const WorkHistoryScreen({super.key});
@ -29,8 +38,12 @@ class WorkHistoryScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); DateFormat dteFormat2 = DateFormat.yMMMMd('en_US');
BuildContext parent = context;
String? token; String? token;
int profileId; int? profileId;
FilePickerResult? result;
AttachmentCategory? selectedAttachmentCategory;
List<AttachmentCategory> attachmentCategories = [];
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: context.watch<WorkHistoryBloc>().state is AddWorkHistoryState title: context.watch<WorkHistoryBloc>().state is AddWorkHistoryState
@ -160,6 +173,11 @@ class WorkHistoryScreen extends StatelessWidget {
}, },
builder: (context, state) { builder: (context, state) {
if (state is WorkHistoryLoaded) { if (state is WorkHistoryLoaded) {
for (var cat in state.attachmentCategory) {
if (cat.subclass.id == 4) {
attachmentCategories.add(cat);
}
}
if (state.workExperiences.isNotEmpty) { if (state.workExperiences.isNotEmpty) {
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
@ -183,13 +201,20 @@ class WorkHistoryScreen extends StatelessWidget {
.workExperiences[index] .workExperiences[index]
.toDate!); .toDate!);
return Column( return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Container( Container(
width: screenWidth, width: screenWidth,
decoration: box1(), decoration: box1(),
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8), horizontal: 12, vertical: 8),
child: Row(children: [ child: Column(
children: [
Row(children: [
Expanded( Expanded(
child: Column( child: Column(
mainAxisAlignment: mainAxisAlignment:
@ -204,7 +229,8 @@ class WorkHistoryScreen extends StatelessWidget {
.titleMedium! .titleMedium!
.copyWith( .copyWith(
fontWeight: fontWeight:
FontWeight.w600, FontWeight
.w600,
color: primary), color: primary),
), ),
const SizedBox( const SizedBox(
@ -217,7 +243,8 @@ class WorkHistoryScreen extends StatelessWidget {
.titleSmall! .titleSmall!
.copyWith( .copyWith(
fontWeight: fontWeight:
FontWeight.w500), FontWeight
.w500),
), ),
const SizedBox( const SizedBox(
height: 5, height: 5,
@ -231,24 +258,27 @@ class WorkHistoryScreen extends StatelessWidget {
], ],
)), )),
AppPopupMenu<int>( AppPopupMenu<int>(
offset: const Offset(-10, -10), offset:
const Offset(-10, -10),
elevation: 3, elevation: 3,
onSelected: (value) { onSelected: (value) {
////delete workhistory-= = = = = = = = =>> ////delete workhistory-= = = = = = = = =>>
if (value == 2) { if (value == 2) {
confirmAlert(context, () { confirmAlert(context, () {
final progress = final progress =
ProgressHUD.of(context); ProgressHUD.of(
context);
progress!.showWithText( progress!.showWithText(
"Loading..."); "Loading...");
BlocProvider.of< BlocProvider.of<
WorkHistoryBloc>( WorkHistoryBloc>(
context) context)
.add(DeleteWorkHistory( .add(
profileId: profileId, DeleteWorkHistory(
profileId: profileId!,
token: token!, token: token!,
workHistory: workHistory: state
state.workExperiences[ .workExperiences[
index], index],
)); ));
}, "Delete?", }, "Delete?",
@ -257,19 +287,257 @@ class WorkHistoryScreen extends StatelessWidget {
if (value == 1) { if (value == 1) {
////edit eligibilty-= = = = = = = = =>> ////edit eligibilty-= = = = = = = = =>>
final progress = final progress =
ProgressHUD.of(context); ProgressHUD.of(
context);
progress!.showWithText( progress!.showWithText(
"Loading..."); "Loading...");
WorkHistory workHistory = WorkHistory workHistory =
state.workExperiences[ state.workExperiences[
index]; index];
context context
.read<WorkHistoryBloc>() .read<
.add( WorkHistoryBloc>()
ShowEditWorkHistoryForm( .add(ShowEditWorkHistoryForm(
workHistory: workHistory:
workHistory)); workHistory));
} }
////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<Color>(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<String> paths = [];
if (selectedAttachmentCategory != null) {
for (var res in result!.files) {
paths.add(res.path!);
}
Navigator.pop(context);
parent.read<WorkHistoryBloc>().add(AddAttachment(attachmentModule: state.workExperiences[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString()));
}
},
child: const Text("Submit")),
)
]),
);
}),
);
});
}
}, },
menuItems: [ menuItems: [
popMenuItem( popMenuItem(
@ -282,9 +550,9 @@ class WorkHistoryScreen extends StatelessWidget {
icon: Icons.delete), icon: Icons.delete),
popMenuItem( popMenuItem(
text: "Attach", text: "Attach",
value: 2, value: 3,
icon: Icons.attach_file), icon:
Icons.attach_file),
], ],
icon: const Icon( icon: const Icon(
Icons.more_vert, Icons.more_vert,
@ -293,6 +561,61 @@ class WorkHistoryScreen extends StatelessWidget {
tooltip: "Options", 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( const SizedBox(
height: 5, height: 5,
@ -308,13 +631,13 @@ class WorkHistoryScreen extends StatelessWidget {
} }
if (state is AddWorkHistoryState) { if (state is AddWorkHistoryState) {
return AddWorkHistoryScreen( return AddWorkHistoryScreen(
profileId: profileId, profileId: profileId!,
token: token!, token: token!,
); );
} }
if (state is EditWorkHistoryState) { if (state is EditWorkHistoryState) {
return EditWorkHistoryScreen( return EditWorkHistoryScreen(
profileId: profileId, profileId: profileId!,
token: token!, token: token!,
); );
} }
@ -324,7 +647,8 @@ class WorkHistoryScreen extends StatelessWidget {
onpressed: () { onpressed: () {
context.read<WorkHistoryBloc>().add( context.read<WorkHistoryBloc>().add(
GetWorkHistories( GetWorkHistories(
profileId: profileId, token: token!)); profileId: profileId!,
token: token!));
}); });
} }
return Container(); return Container();

View File

@ -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<Attachment> 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,
),
],
),
),
),
)),
],
);
}
}

View File

@ -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,))
],
));
}
}

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:unit2/model/profile/attachment.dart';
import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/eligibility.dart';
import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/request.dart';
import 'package:unit2/utils/urls.dart'; import 'package:unit2/utils/urls.dart';
@ -9,19 +10,19 @@ class EligibilityService {
static final EligibilityService _instance = EligibilityService(); static final EligibilityService _instance = EligibilityService();
static EligibilityService get instance => _instance; static EligibilityService get instance => _instance;
Future<List<EligibityCert>> getEligibilities(
Future<List<EligibityCert>> getEligibilities(int profileId, String token)async{ int profileId, String token) async {
List<EligibityCert> eligibilities = []; List<EligibityCert> eligibilities = [];
String authToken = "Token $token"; String authToken = "Token $token";
String path = "${Url.instance.getEligibilities()}$profileId/"; String path = "${Url.instance.getEligibilities()}$profileId/";
Map<String, String> headers = { Map<String, String> headers = {
'Content-Type': 'application/json; charset=UTF-8', 'Content-Type': 'application/json; charset=UTF-8',
'Authorization': authToken 'Authorization': authToken
}; };
try { try {
http.Response response = await Request.instance.getRequest(path: path,headers: headers,param: {}); http.Response response = await Request.instance
.getRequest(path: path, headers: headers, param: {});
if (response.statusCode == 200) { if (response.statusCode == 200) {
Map data = jsonDecode(response.body); Map data = jsonDecode(response.body);
if (data['data'] != null) { if (data['data'] != null) {
@ -51,15 +52,14 @@ class EligibilityService {
'Authorization': authtoken 'Authorization': authtoken
}; };
try { try {
http.Response response = await Request.instance http.Response response = await Request.instance.deleteRequest(
.deleteRequest(path: path, headers: headers, body: body, param: params); path: path, headers: headers, body: body, param: params);
if (response.statusCode == 200) { if (response.statusCode == 200) {
Map data = jsonDecode(response.body); Map data = jsonDecode(response.body);
success = data['success']; success = data['success'];
} else { } else {
success = false; success = false;
} }
} catch (e) { } catch (e) {
throw (e.toString()); throw (e.toString());
} }
@ -106,7 +106,8 @@ class EligibilityService {
Future<Map<dynamic, dynamic>> update( Future<Map<dynamic, dynamic>> update(
{required EligibityCert eligibityCert, {required EligibityCert eligibityCert,
required String token, required String token,
required int profileId, required int oldEligibility}) async { required int profileId,
required int oldEligibility}) async {
Map<dynamic, dynamic>? response = {}; Map<dynamic, dynamic>? response = {};
String authtoken = "Token $token"; String authtoken = "Token $token";
String path = '${Url.instance.addEligibility()}$profileId/'; String path = '${Url.instance.addEligibility()}$profileId/';
@ -140,4 +141,63 @@ class EligibilityService {
throw e.toString(); throw e.toString();
} }
} }
Future<bool> 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<String, dynamic> params = {"force_mode": "true"};
Map<String, String> 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!;
}
} }

View File

@ -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<List<AttachmentCategory>> getCategories() async {
List<AttachmentCategory> attachmentCategories = [];
String path = Url.instance.attachmentCategories();
Map<String, String> 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<Map<dynamic, dynamic>> attachment(
{required String categoryId,
required String module,
required List<String> paths,
required String token,
required String profileId}) async {
String authtoken = "Token $token";
Map<String, String> headers = {'Authorization': authtoken};
String path = Url.instance.attachments();
Map<dynamic, dynamic>? response = {};
Map<String, String> 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;
}
}

View File

@ -167,6 +167,9 @@ class Url {
String getServiceTypes() { String getServiceTypes() {
return "/api/jobnet_app/comm_service_type/"; return "/api/jobnet_app/comm_service_type/";
} }
String attachments(){
return "/api/jobnet_app/profile/attachment/";
}
//// address path //// address path
String addressPath() { String addressPath() {
@ -319,4 +322,7 @@ class Url {
String getAddressCategory() { String getAddressCategory() {
return "/api/jobnet_app/address_categories/"; return "/api/jobnet_app/address_categories/";
} }
String attachmentCategories(){
return "/api/jobnet_app/attachment_categories/";
}
} }

View File

@ -449,6 +449,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.4" 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: file_utils:
dependency: transitive dependency: transitive
description: description:

View File

@ -86,6 +86,7 @@ dependencies:
flutter_staggered_animations: ^1.1.1 flutter_staggered_animations: ^1.1.1
group_list_view: ^1.1.1 group_list_view: ^1.1.1
search_page: ^2.3.0 search_page: ^2.3.0
file_picker: ^5.3.1
dev_dependencies: dev_dependencies: