Compare commits
65 Commits
mohemm_HMG
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
3bafd74857 | 11 hours ago |
|
|
352c85dcd8 | 11 hours ago |
|
|
152cd5c019 | 11 hours ago |
|
|
057e71e3c0 | 12 hours ago |
|
|
e4d182912c | 21 hours ago |
|
|
480d9d466f | 21 hours ago |
|
|
bdaccf133c | 21 hours ago |
|
|
16e2cc604f | 21 hours ago |
|
|
d5c3ce4c62 | 21 hours ago |
|
|
5da81968d7 | 21 hours ago |
|
|
6a80e21c3f | 1 day ago |
|
|
b240f55aff | 1 day ago |
|
|
3343e4fe10 | 1 day ago |
|
|
2c9db30d6a | 1 day ago |
|
|
eb3eb1d102 | 1 day ago |
|
|
257b803cce | 2 weeks ago |
|
|
a958257ce9 | 2 weeks ago |
|
|
dae746587f | 2 weeks ago |
|
|
facb3bf4e6 | 2 weeks ago |
|
|
71d03ba664 | 2 weeks ago |
|
|
9f663df3cc | 2 weeks ago |
|
|
ba8908d4eb | 2 weeks ago |
|
|
a8f390d49f | 2 weeks ago |
|
|
2c7d3bf963 | 3 weeks ago |
|
|
31d1804f7a | 4 weeks ago |
|
|
82c9aa8a2f | 4 weeks ago |
|
|
048c9b395f | 4 weeks ago |
|
|
eea35e4d77 | 4 weeks ago |
|
|
19ccee6a20 | 2 months ago |
|
|
5705619ce4 | 2 months ago |
|
|
fe583c234b | 2 months ago |
|
|
a426bc332e | 2 months ago |
|
|
12d8fd95ba | 2 months ago |
|
|
eebaafcf7c | 2 months ago |
|
|
a28eb4426b | 2 months ago |
|
|
dfa7d9a713 | 2 months ago |
|
|
9112b26987 | 2 months ago |
|
|
e3fcc992df | 2 months ago |
|
|
d021c95def | 2 months ago |
|
|
74bcf3e1b7 | 2 months ago |
|
|
912f7b23f1 | 2 months ago |
|
|
99414bf787 | 2 months ago |
|
|
90bf529865 | 2 months ago |
|
|
89fa395183 | 3 months ago |
|
|
1ee51a7597 | 3 months ago |
|
|
4d0918cd25 | 3 months ago |
|
|
9b3601f06a | 3 months ago |
|
|
89ea554c7a | 4 months ago |
|
|
3d91a1444e | 4 months ago |
|
|
bc697df13d | 4 months ago |
|
|
fb35e256e7 | 4 months ago |
|
|
c9d5ab98a3 | 4 months ago |
|
|
b96c460d6b | 5 months ago |
|
|
bf3f9b55fc | 5 months ago |
|
|
c191d4c0c0 | 5 months ago |
|
|
0b08606813 | 5 months ago |
|
|
107846dd82 | 5 months ago |
|
|
c7e44965d6 | 5 months ago |
|
|
04a2561f7f | 5 months ago |
|
|
413db57206 | 5 months ago |
|
|
c06c020fb3 | 5 months ago |
|
|
cca95cee6a | 6 months ago |
|
|
1bcecde59b | 7 months ago |
|
|
c8fa64171e | 7 months ago |
|
|
1bb44039d6 | 8 months ago |
@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.3339 15.9992H13.3339V11.1992C13.3339 10.9365 13.3343 10.7929 13.34 10.6886L13.341 10.673L13.3565 10.6721C13.4609 10.6664 13.6045 10.6659 13.8672 10.6659H17.3339C18.8066 10.6659 20.0005 11.8598 20.0005 13.3326C20.0005 14.8053 18.8066 15.9992 17.3339 15.9992Z" fill="#2E3039"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0684 2.33203H15.9316C13.1698 2.33202 11.005 2.33201 9.30102 2.53899C7.55973 2.75051 6.16606 3.19084 5.02088 4.16892C4.71548 4.42976 4.43104 4.71419 4.1702 5.0196C3.19212 6.16478 2.75179 7.55845 2.54027 9.29974C2.33329 11.0037 2.3333 13.1684 2.33331 15.9304V16.067C2.3333 18.829 2.33329 20.9937 2.54027 22.6977C2.75179 24.4389 3.19212 25.8326 4.1702 26.9778C4.43104 27.2832 4.71547 27.5676 5.02088 27.8285C6.16606 28.8066 7.55973 29.2469 9.30102 29.4584C11.005 29.6654 13.1697 29.6654 15.9316 29.6654H16.0683C18.8302 29.6654 20.995 29.6654 22.6989 29.4584C24.4402 29.2469 25.8339 28.8066 26.9791 27.8285C27.2845 27.5676 27.5689 27.2832 27.8298 26.9778C28.8078 25.8326 29.2482 24.4389 29.4597 22.6977C29.6667 20.9937 29.6667 18.829 29.6666 16.067V15.9304C29.6667 13.1684 29.6667 11.0037 29.4597 9.29974C29.2482 7.55845 28.8078 6.16478 27.8298 5.0196C27.5689 4.71419 27.2845 4.42976 26.9791 4.16892C25.8339 3.19084 24.4402 2.75051 22.6989 2.53899C20.995 2.33201 18.8303 2.33202 16.0684 2.33203ZM13.8672 7.99925L13.7592 7.99898C13.3835 7.99761 12.9217 7.99593 12.5098 8.12976C11.698 8.39355 11.0615 9.03003 10.7977 9.84187C10.6639 10.2538 10.6656 10.7156 10.6669 11.0913L10.6672 11.1992V23.9993C10.6672 24.7356 11.2642 25.3326 12.0005 25.3326C12.7369 25.3326 13.3339 24.7356 13.3339 23.9993V18.6659H17.3339C20.2794 18.6659 22.6672 16.2781 22.6672 13.3326C22.6672 10.3871 20.2794 7.99925 17.3339 7.99925H13.8672Z" fill="#2E3039"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="19.5" height="19.5" viewBox="0 0 19.5 19.5">
|
||||
<g id="graduation-cap" transform="translate(0)">
|
||||
<path id="cap-top" d="M9.75,4.5L3.5,7.5L9.75,10.5L16,7.5L9.75,4.5Z" fill="#2e303a"/>
|
||||
<path id="cap-body" d="M4.5,8.5V13.5C4.5,14.5,6.5,16.5,9.75,16.5C13,16.5,15,14.5,15,13.5V8.5L9.75,11.5L4.5,8.5Z" fill="#2e303a"/>
|
||||
<path id="tassel" d="M16,8V12.5L16.5,13L17,12.5V8H16Z" fill="#2e303a"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 457 B |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32.774" height="32.788" viewBox="0 0 32.774 32.788">
|
||||
<g id="park-tickets-couple" transform="translate(-0.205 0.001)">
|
||||
<path id="Path_4724" data-name="Path 4724" d="M68.36,485.746a1.657,1.657,0,0,1-1.581,1.3.539.539,0,0,0-.206,1.025,1.656,1.656,0,0,1,.955,1.814l-.14.7a1.744,1.744,0,0,1-2.093,1.278c-.25-.06-.506-.106-.759-.156-.373-.073-.745-.15-1.118-.223-.493-.1-.985-.2-1.477-.3l-1.76-.353c-.655-.133-1.311-.263-1.967-.4-.7-.14-1.4-.279-2.1-.423-.719-.143-1.438-.289-2.153-.433l-2.133-.429-2.036-.409c-.622-.123-1.241-.25-1.863-.373-.539-.107-1.078-.216-1.614-.323l-1.291-.26-.888-.18c-.136-.027-.273-.057-.409-.083-.007,0-.013,0-.02,0a1.686,1.686,0,0,1-.12,1.062,1.705,1.705,0,0,1-1.87.958.5.5,0,0,0-.562.246l-.3.649a.5.5,0,0,0,.176.589c.033.027,24.018,11.084,24.058,11.094a.5.5,0,0,0,.562-.246l.3-.649a.5.5,0,0,0-.176-.589,1.7,1.7,0,0,1,1.388-3,.5.5,0,0,0,.562-.246l.3-.649a.5.5,0,0,0-.176-.589,1.7,1.7,0,0,1,1.388-3,.5.5,0,0,0,.562-.246l.3-.649a.5.5,0,0,0-.176-.589,1.7,1.7,0,0,1,1.388-3,.5.5,0,0,0,.562-.246l.329-.719a.41.41,0,0,0-.2-.546L68.416,485.5Z" transform="translate(-39.26 -469.349)" fill="#2bb8a6"/>
|
||||
<path id="Path_4725" data-name="Path 4725" d="M.519,16.168c.04.02,25.932,5.214,25.975,5.214a.5.5,0,0,0,.489-.369l.14-.7a.5.5,0,0,0-.309-.532,1.7,1.7,0,0,1,.652-3.244.5.5,0,0,0,.489-.369l.1-.512.037-.19a.5.5,0,0,0-.309-.532,1.7,1.7,0,0,1,.652-3.244.5.5,0,0,0,.489-.369l.14-.7a.5.5,0,0,0-.309-.532,1.7,1.7,0,0,1,.652-3.244.5.5,0,0,0,.489-.369l.156-.775a.41.41,0,0,0-.323-.482S3.714,0,3.687,0a.412.412,0,0,0-.4.329L3.128,1.1a.5.5,0,0,0,.309.532A1.7,1.7,0,0,1,2.786,4.88.5.5,0,0,0,2.3,5.25l-.14.7a.5.5,0,0,0,.309.532,1.7,1.7,0,0,1-.652,3.244.5.5,0,0,0-.489.369l-.14.7a.5.5,0,0,0,.309.532A1.7,1.7,0,0,1,.842,14.57a.5.5,0,0,0-.489.369l-.14.7A.494.494,0,0,0,.519,16.168ZM22.7,7.639a.831.831,0,1,1,1.631.326l-1.1,5.464L22.555,16.8a.832.832,0,0,1-.815.669.9.9,0,0,1-.163-.017.833.833,0,0,1-.652-.978l.755-3.757ZM7.7,4.628a.831.831,0,0,1,1.631.326L8.948,6.84,8.519,8.98,8.173,10.7l-.616,3.081a.832.832,0,0,1-.815.669.9.9,0,0,1-.163-.017.4.4,0,0,1-.067-.017.829.829,0,0,1-.582-.962l.123-.622.323-1.611Z" fill="#125765"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="23.959" height="37.006" viewBox="0 0 23.959 37.006">
|
||||
<g id="Layer_x0020_1" transform="translate(-300.998)">
|
||||
<g id="_411123120" transform="translate(301)">
|
||||
<g id="Group_8657" data-name="Group 8657" transform="translate(3.252)">
|
||||
<path id="Path_13674" data-name="Path 13674" d="M612.691,6.612H619.8V1.691h-7.111ZM620.669,8.3h-8.823A.833.833,0,0,1,611,7.458V.845A.847.847,0,0,1,611.846,0h8.823a.861.861,0,0,1,.845.845V7.458A.847.847,0,0,1,620.669,8.3Z" transform="translate(-607.531)" fill="#2bb8a6"/>
|
||||
<path id="Path_13675" data-name="Path 13675" d="M466.845,305h15.089a.833.833,0,0,1,.845.845,5.44,5.44,0,0,1-5.42,5.441h-5.918A5.478,5.478,0,0,1,466,305.845.847.847,0,0,1,466.845,305Z" transform="translate(-465.675 -298.388)" fill="#2bb8a6"/>
|
||||
<path id="Path_13676" data-name="Path 13676" d="M527.845,0A.861.861,0,0,0,527,.845a.847.847,0,0,0,.846.845h12.444a.851.851,0,0,0,.867-.845A.865.865,0,0,0,540.289,0Z" transform="translate(-525.352)" fill="#2bb8a6"/>
|
||||
<g id="Group_8656" data-name="Group 8656" transform="translate(0 33.863)">
|
||||
<path id="Path_13677" data-name="Path 13677" d="M455.553,1562v.867a2.276,2.276,0,1,1-4.553,0V1562h4.553Z" transform="translate(-451 -1562)" fill="#2bb8a6"/>
|
||||
<path id="Path_13678" data-name="Path 13678" d="M1050.553,1562v.867a2.276,2.276,0,0,1-4.553,0V1562h4.553Z" transform="translate(-1033.101 -1562)" fill="#2bb8a6"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="Subtraction_59" data-name="Subtraction 59" d="M20570.063,22681.4h-16.975a.817.817,0,0,1-.238-.023,3.388,3.388,0,0,1-2.236-1.018,3.476,3.476,0,0,1-1.02-2.471v-19.4a3.557,3.557,0,0,1,1.02-2.475,3.457,3.457,0,0,1,1.906-.975,1.745,1.745,0,0,0-.193.8,6.288,6.288,0,0,0,6.289,6.285h5.92a6.288,6.288,0,0,0,6.285-6.285,1.692,1.692,0,0,0-.217-.8,3.472,3.472,0,0,1,2.949,3.449v19.4a3.476,3.476,0,0,1-1.02,2.471,3.383,3.383,0,0,1-2.236,1.018A.879.879,0,0,1,20570.063,22681.4Zm-10.756-7.187a2.114,2.114,0,1,0,1.494.619A2.1,2.1,0,0,0,20559.307,22674.211Zm4.027.732-.613.621.961.939-.961.943.613.621.963-.949v0l.973.949.609-.621-.961-.943.961-.939-.609-.621-.973.951Zm-4.025-6.031a2.112,2.112,0,0,0-.807,4.063,2.071,2.071,0,0,0,.809.164,2.113,2.113,0,0,0,0-4.227Zm3.543,1.406-.645.584,1.531,1.7,2.637-2.6-.609-.619-1.992,1.963Zm-3.539-6.709a2.158,2.158,0,0,0-.486.059,2.111,2.111,0,1,0,.969,4.109,2.123,2.123,0,0,0,1.57-2.545A2.1,2.1,0,0,0,20559.313,22663.609Zm3.539,1.266-.645.58,1.531,1.7,2.637-2.6-.609-.619-1.992,1.963Z" transform="translate(-20549.596 -22648.381)" fill="#125765"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg id="Outline" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#ffffff;}</style></defs><path style="fill: #FFF" class="cls-1" d="M376,170a11.34,11.34,0,0,0-16,.15L215.28,317.64l-64-65.22a11.34,11.34,0,0,0-16.18,15.88l72.16,73.58a11.35,11.35,0,0,0,16,.15,12,12,0,0,0,1.43-1.7l.4-.37,151-154A11.33,11.33,0,0,0,376,170Z"/><path style="fill: #FFF" class="cls-1" d="M256,58.65c-108.38,0-196.56,88.18-196.56,196.56S147.62,451.77,256,451.77,452.56,363.6,452.56,255.21,364.38,58.65,256,58.65Zm0,370.45c-95.88,0-173.88-78-173.88-173.89S160.12,81.33,256,81.33s173.88,78,173.88,173.88S351.88,429.1,256,429.1Z"/></svg>
|
||||
|
After Width: | Height: | Size: 665 B |
@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg data-name="Livello 1" id="Livello_1" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><title/><path style="fill: #fff;" d="M64,0a64,64,0,1,0,64,64A64.07,64.07,0,0,0,64,0Zm0,122a58,58,0,1,1,58-58A58.07,58.07,0,0,1,64,122Z"/><path style="fill: #fff;" d="M92.12,35.79a3,3,0,0,0-4.24,0L64,59.75l-23.87-24A3,3,0,0,0,35.88,40L59.76,64,35.88,88a3,3,0,0,0,4.25,4.24L64,68.25l23.88,24A3,3,0,0,0,92.13,88L68.24,64,92.13,40A3,3,0,0,0,92.12,35.79Z"/></svg>
|
||||
|
After Width: | Height: | Size: 479 B |
@ -0,0 +1,140 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mohem_flutter_app/api/api_client.dart';
|
||||
import 'package:mohem_flutter_app/app_state/app_state.dart';
|
||||
import 'package:mohem_flutter_app/classes/consts.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart';
|
||||
import 'package:mohem_flutter_app/models/generic_response_model.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_department_sections.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_project_departments.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_projects.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_section_topics.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_details.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_transactions.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_types.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_tickets_list.dart';
|
||||
import 'package:mohem_flutter_app/models/mowadhafhi/get_transaction_attachment_model.dart';
|
||||
|
||||
class EtqanApiClient {
|
||||
static final EtqanApiClient _instance = EtqanApiClient._internal();
|
||||
|
||||
EtqanApiClient._internal();
|
||||
|
||||
factory EtqanApiClient() => _instance;
|
||||
|
||||
Future<List<EtqanGetProjectsResponse>> getEtqanProjects() async {
|
||||
String url = "${ApiConsts.cocRest}ETQAN_GetProjects";
|
||||
Map<String, dynamic> postParams = <String, dynamic>{"P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER};
|
||||
postParams.addAll(AppState().postParamsJson);
|
||||
return await ApiClient().postJsonForObject(
|
||||
(json) {
|
||||
List<EtqanGetProjectsResponse>? responseData = <EtqanGetProjectsResponse>[];
|
||||
if (json["ETQAN_GetProjects_Response"] != null && json["ETQAN_GetProjects_Response"].isNotEmpty) {
|
||||
json["ETQAN_GetProjects_Response"].forEach((element) {
|
||||
responseData.add(EtqanGetProjectsResponse.fromJson(element));
|
||||
});
|
||||
}
|
||||
return responseData;
|
||||
},
|
||||
url,
|
||||
postParams,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<EtqanGetEmployeeOvrRequestsResponse>> getEmployeeEtqanRequests() async {
|
||||
String url = "${ApiConsts.cocRest}ETQAN_GetEmployeeOVRRequests";
|
||||
Map<String, dynamic> postParams = <String, dynamic>{
|
||||
"ETQAN_EmployeeID": AppState().memberInformationList?.eMPLOYEENUMBER,
|
||||
"P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER,
|
||||
};
|
||||
postParams.addAll(AppState().postParamsJson);
|
||||
return await ApiClient().postJsonForObject(
|
||||
(json) {
|
||||
List<EtqanGetEmployeeOvrRequestsResponse> responseData = <EtqanGetEmployeeOvrRequestsResponse>[];
|
||||
if (json["ETQAN_GetEmployeeOVRRequests_Response"] != null && json["ETQAN_GetEmployeeOVRRequests_Response"].isNotEmpty) {
|
||||
json["ETQAN_GetEmployeeOVRRequests_Response"].forEach((element) {
|
||||
responseData.add(EtqanGetEmployeeOvrRequestsResponse.fromJson(element));
|
||||
});
|
||||
}
|
||||
|
||||
if (responseData.isNotEmpty) {
|
||||
responseData.sort((EtqanGetEmployeeOvrRequestsResponse a, EtqanGetEmployeeOvrRequestsResponse b) {
|
||||
if (a.createdDate == null && b.createdDate == null) return 0;
|
||||
if (a.createdDate == null) return 1;
|
||||
if (b.createdDate == null) return -1;
|
||||
|
||||
try {
|
||||
// Parse dates in format "26-Feb-2026 12:54 PM" or "01-Mar-2026 02:45 PM"
|
||||
DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a", "en_US");
|
||||
DateTime dateA = dateFormat.parse(a.createdDate!);
|
||||
DateTime dateB = dateFormat.parse(b.createdDate!);
|
||||
return dateB.compareTo(dateA); // Descending order (newest first)
|
||||
} catch (e) {
|
||||
return (b.createdDate ?? '').compareTo(a.createdDate ?? '');
|
||||
}
|
||||
});
|
||||
}
|
||||
return responseData;
|
||||
},
|
||||
url,
|
||||
postParams,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<EtqanGetIncidentRequestResponse>> getEtqanIncidentRequests(String ticketNo) async {
|
||||
String url = "${ApiConsts.cocRest}ETQAN_GetIncidentRequest";
|
||||
Map<String, dynamic> postParams = <String, dynamic>{"ETQAN_TicketNumber": ticketNo, "P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER};
|
||||
postParams.addAll(AppState().postParamsJson);
|
||||
return await ApiClient().postJsonForObject(
|
||||
(json) {
|
||||
List<EtqanGetIncidentRequestResponse> responseData = <EtqanGetIncidentRequestResponse>[];
|
||||
if (json["ETQAN_GetIncidentRequest_Response"] != null && json["ETQAN_GetIncidentRequest_Response"].isNotEmpty) {
|
||||
json["ETQAN_GetIncidentRequest_Response"].forEach((element) {
|
||||
responseData.add(EtqanGetIncidentRequestResponse.fromJson(element));
|
||||
});
|
||||
}
|
||||
return responseData;
|
||||
},
|
||||
url,
|
||||
postParams,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> createRequest(String description, int? projectID, String? attachmentBase64, bool? isAnonymous) async {
|
||||
String url = "${ApiConsts.cocRest}ETQAN_CreateIncident";
|
||||
Map<String, dynamic> postParams = <String, dynamic>{
|
||||
"P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER,
|
||||
"P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER,
|
||||
"ETQAN_Emp_EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER,
|
||||
"ETQAN_Description": description,
|
||||
"ETQAN_ProjectId": projectID,
|
||||
"ETQAN_FileInfoBase64": attachmentBase64,
|
||||
"ETQAN_IsAnonymous": isAnonymous ?? false,
|
||||
};
|
||||
|
||||
postParams.addAll(AppState().postParamsJson);
|
||||
return await ApiClient().postJsonForObject(
|
||||
(json) {
|
||||
if (json["ETQAN_CreateIncident_Response"] != null) {
|
||||
var response = json["ETQAN_CreateIncident_Response"];
|
||||
if (response["id"] != null && response["ticketNumber"] != null) {
|
||||
return {"id": response["id"], "ticketNumber": response["ticketNumber"]};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
|
||||
// "ETQAN_CreateIncident_Response": {
|
||||
// "id": "00adabe4-4a32-4b35-1f00-08de4d152e7c",
|
||||
// "ticketNumber": "OVR/OLY/2026/224"
|
||||
// },
|
||||
},
|
||||
url,
|
||||
postParams,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
class CoursesResponseModel {
|
||||
bool? success;
|
||||
int? status;
|
||||
String? username;
|
||||
List<CourseStatus>? coursesStatus;
|
||||
int? totalCourses;
|
||||
|
||||
CoursesResponseModel({
|
||||
this.success,
|
||||
this.status,
|
||||
this.username,
|
||||
this.coursesStatus,
|
||||
this.totalCourses,
|
||||
});
|
||||
|
||||
CoursesResponseModel.fromJson(Map<String, dynamic> json) {
|
||||
success = json['success'];
|
||||
status = json['status'];
|
||||
username = json['username'];
|
||||
if (json['courses_status'] != null) {
|
||||
coursesStatus = <CourseStatus>[];
|
||||
json['courses_status'].forEach((v) {
|
||||
coursesStatus!.add(CourseStatus.fromJson(v));
|
||||
});
|
||||
}
|
||||
totalCourses = json['total_courses'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['success'] = success;
|
||||
data['status'] = status;
|
||||
data['username'] = username;
|
||||
if (coursesStatus != null) {
|
||||
data['courses_status'] = coursesStatus!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
data['total_courses'] = totalCourses;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class CourseStatus {
|
||||
String? cId;
|
||||
String? cFullname;
|
||||
String? cNameAr;
|
||||
String? cShortname;
|
||||
String? cStatus;
|
||||
String? cIsMohemmAffective;
|
||||
String? cStartdate;
|
||||
String? cEnddate;
|
||||
String? cUrl;
|
||||
String? token;
|
||||
CourseStatus({
|
||||
this.cId,
|
||||
this.cFullname,
|
||||
this.cNameAr,
|
||||
this.cShortname,
|
||||
this.cStatus,
|
||||
this.cIsMohemmAffective,
|
||||
this.cStartdate,
|
||||
this.cEnddate,
|
||||
this.cUrl,
|
||||
this.token
|
||||
});
|
||||
|
||||
CourseStatus.fromJson(Map<String, dynamic> json) {
|
||||
cId = json['c_id'];
|
||||
cFullname = json['c_fullname'];
|
||||
cNameAr = json['c_name_ar'];
|
||||
cShortname = json['c_shortname'];
|
||||
cStatus = json['c_status'];
|
||||
cIsMohemmAffective = json['c_is_mohemm_affective'];
|
||||
cStartdate = json['c_startdate'];
|
||||
cEnddate = json['c_enddate'];
|
||||
cUrl = json['c_url'];
|
||||
token =json['c_token'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['c_id'] = cId;
|
||||
data['c_fullname'] = cFullname;
|
||||
data['c_name_ar'] = cNameAr;
|
||||
data['c_shortname'] = cShortname;
|
||||
data['c_status'] = cStatus;
|
||||
data['c_is_mohemm_affective'] = cIsMohemmAffective;
|
||||
data['c_startdate'] = cStartdate;
|
||||
data['c_enddate'] = cEnddate;
|
||||
data['c_url'] = cUrl;
|
||||
data['c_token'] = token;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class GetTicketAccuralBalanceModel {
|
||||
int? accrualNetEntitlement;
|
||||
String? accuralPlanCode;
|
||||
|
||||
GetTicketAccuralBalanceModel({
|
||||
this.accrualNetEntitlement,
|
||||
this.accuralPlanCode,
|
||||
});
|
||||
|
||||
factory GetTicketAccuralBalanceModel.fromRawJson(String str) => GetTicketAccuralBalanceModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory GetTicketAccuralBalanceModel.fromJson(Map<String, dynamic> json) => GetTicketAccuralBalanceModel(
|
||||
accrualNetEntitlement: json["ACCRUAL_NET_ENTITLEMENT"],
|
||||
accuralPlanCode: json["ACCURAL_PLAN_CODE"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"ACCRUAL_NET_ENTITLEMENT": accrualNetEntitlement,
|
||||
"ACCURAL_PLAN_CODE": accuralPlanCode,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class EtqanGetEmployeeIncidentRequestModel {
|
||||
List<EtqanGetIncidentRequestResponse>? etqanGetIncidentRequestResponse;
|
||||
|
||||
EtqanGetEmployeeIncidentRequestModel({
|
||||
this.etqanGetIncidentRequestResponse,
|
||||
});
|
||||
|
||||
factory EtqanGetEmployeeIncidentRequestModel.fromRawJson(String str) => EtqanGetEmployeeIncidentRequestModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory EtqanGetEmployeeIncidentRequestModel.fromJson(Map<String, dynamic> json) => EtqanGetEmployeeIncidentRequestModel(
|
||||
etqanGetIncidentRequestResponse: json["ETQAN_GetIncidentRequest_Response"] == null ? [] : List<EtqanGetIncidentRequestResponse>.from(json["ETQAN_GetIncidentRequest_Response"]!.map((x) => EtqanGetIncidentRequestResponse.fromJson(x))),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"ETQAN_GetIncidentRequest_Response": etqanGetIncidentRequestResponse == null ? [] : List<dynamic>.from(etqanGetIncidentRequestResponse!.map((x) => x.toJson())),
|
||||
};
|
||||
}
|
||||
|
||||
class EtqanGetIncidentRequestResponse {
|
||||
String? key;
|
||||
String? keyAr;
|
||||
String? value;
|
||||
String? valueAr;
|
||||
List<String>? subValues;
|
||||
|
||||
EtqanGetIncidentRequestResponse({
|
||||
this.key,
|
||||
this.keyAr,
|
||||
this.value,
|
||||
this.valueAr,
|
||||
this.subValues,
|
||||
});
|
||||
|
||||
factory EtqanGetIncidentRequestResponse.fromRawJson(String str) => EtqanGetIncidentRequestResponse.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory EtqanGetIncidentRequestResponse.fromJson(Map<String, dynamic> json) => EtqanGetIncidentRequestResponse(
|
||||
key: json["key"],
|
||||
keyAr: json["keyAr"],
|
||||
value: json["value"],
|
||||
valueAr: json["valueAr"],
|
||||
subValues: json["subValues"] == null ? [] : List<String>.from(json["subValues"]!.map((x) => x)),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"key": key,
|
||||
"keyAr": keyAr,
|
||||
"value": value,
|
||||
"valueAr": valueAr,
|
||||
"subValues": subValues == null ? [] : List<dynamic>.from(subValues!.map((x) => x)),
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class EtqanGetEmployeeRequestsModel {
|
||||
List<EtqanGetEmployeeOvrRequestsResponse>? etqanGetEmployeeOvrRequestsResponse;
|
||||
|
||||
EtqanGetEmployeeRequestsModel({
|
||||
this.etqanGetEmployeeOvrRequestsResponse,
|
||||
});
|
||||
|
||||
factory EtqanGetEmployeeRequestsModel.fromRawJson(String str) => EtqanGetEmployeeRequestsModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory EtqanGetEmployeeRequestsModel.fromJson(Map<String, dynamic> json) => EtqanGetEmployeeRequestsModel(
|
||||
etqanGetEmployeeOvrRequestsResponse: json["ETQAN_GetEmployeeOVRRequests_Response"] == null ? [] : List<EtqanGetEmployeeOvrRequestsResponse>.from(json["ETQAN_GetEmployeeOVRRequests_Response"]!.map((x) => EtqanGetEmployeeOvrRequestsResponse.fromJson(x))),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"ETQAN_GetEmployeeOVRRequests_Response": etqanGetEmployeeOvrRequestsResponse == null ? [] : List<dynamic>.from(etqanGetEmployeeOvrRequestsResponse!.map((x) => x.toJson())),
|
||||
};
|
||||
}
|
||||
|
||||
class EtqanGetEmployeeOvrRequestsResponse {
|
||||
String? createdDate;
|
||||
String? description;
|
||||
String? id;
|
||||
bool? isAnonymous;
|
||||
String? status;
|
||||
String? statusAr;
|
||||
String? ticketNumber;
|
||||
|
||||
EtqanGetEmployeeOvrRequestsResponse({
|
||||
this.createdDate,
|
||||
this.description,
|
||||
this.id,
|
||||
this.isAnonymous,
|
||||
this.status,
|
||||
this.statusAr,
|
||||
this.ticketNumber,
|
||||
});
|
||||
|
||||
factory EtqanGetEmployeeOvrRequestsResponse.fromRawJson(String str) => EtqanGetEmployeeOvrRequestsResponse.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory EtqanGetEmployeeOvrRequestsResponse.fromJson(Map<String, dynamic> json) => EtqanGetEmployeeOvrRequestsResponse(
|
||||
createdDate: json["createdDate"],
|
||||
description: json["description"],
|
||||
id: json["id"],
|
||||
isAnonymous: json["isAnonymous"],
|
||||
status: json["status"],
|
||||
statusAr: json["statusAr"],
|
||||
ticketNumber: json["ticketNumber"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"createdDate": createdDate,
|
||||
"description": description,
|
||||
"id": id,
|
||||
"isAnonymous": isAnonymous,
|
||||
"status": status,
|
||||
"statusAr": statusAr,
|
||||
"ticketNumber": ticketNumber,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class EtqanGetProjectsModel {
|
||||
List<EtqanGetProjectsResponse>? etqanGetProjectsResponse;
|
||||
|
||||
EtqanGetProjectsModel({
|
||||
this.etqanGetProjectsResponse,
|
||||
});
|
||||
|
||||
factory EtqanGetProjectsModel.fromRawJson(String str) => EtqanGetProjectsModel.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory EtqanGetProjectsModel.fromJson(Map<String, dynamic> json) => EtqanGetProjectsModel(
|
||||
etqanGetProjectsResponse: json["ETQAN_GetProjects_Response"] == null ? [] : List<EtqanGetProjectsResponse>.from(json["ETQAN_GetProjects_Response"]!.map((x) => EtqanGetProjectsResponse.fromJson(x))),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"ETQAN_GetProjects_Response": etqanGetProjectsResponse == null ? [] : List<dynamic>.from(etqanGetProjectsResponse!.map((x) => x.toJson())),
|
||||
};
|
||||
}
|
||||
|
||||
class EtqanGetProjectsResponse {
|
||||
String? projectAr;
|
||||
String? projectCode;
|
||||
String? projectEn;
|
||||
int? projectId;
|
||||
|
||||
EtqanGetProjectsResponse({
|
||||
this.projectAr,
|
||||
this.projectCode,
|
||||
this.projectEn,
|
||||
this.projectId,
|
||||
});
|
||||
|
||||
factory EtqanGetProjectsResponse.fromRawJson(String str) => EtqanGetProjectsResponse.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory EtqanGetProjectsResponse.fromJson(Map<String, dynamic> json) => EtqanGetProjectsResponse(
|
||||
projectAr: json["projectAr"],
|
||||
projectCode: json["projectCode"],
|
||||
projectEn: json["projectEn"],
|
||||
projectId: json["projectId"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"projectAr": projectAr,
|
||||
"projectCode": projectCode,
|
||||
"projectEn": projectEn,
|
||||
"projectId": projectId,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class GetEmployeeParking {
|
||||
GetEmployeeParkingClass? getEmployeeParking;
|
||||
|
||||
GetEmployeeParking({
|
||||
this.getEmployeeParking,
|
||||
});
|
||||
|
||||
factory GetEmployeeParking.fromRawJson(String str) => GetEmployeeParking.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory GetEmployeeParking.fromJson(Map<String, dynamic> json) => GetEmployeeParking(
|
||||
getEmployeeParking: json["GET_Employee_Parking"] == null ? null : GetEmployeeParkingClass.fromJson(json["GET_Employee_Parking"]),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"GET_Employee_Parking": getEmployeeParking?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
class GetEmployeeParkingClass {
|
||||
DateTime? createdAt;
|
||||
String? employeeCode;
|
||||
String? isNewQrCode;
|
||||
String? qrCode;
|
||||
String? qrCodeImageBase64;
|
||||
bool? success;
|
||||
|
||||
GetEmployeeParkingClass({
|
||||
this.createdAt,
|
||||
this.employeeCode,
|
||||
this.isNewQrCode,
|
||||
this.qrCode,
|
||||
this.qrCodeImageBase64,
|
||||
this.success,
|
||||
});
|
||||
|
||||
factory GetEmployeeParkingClass.fromRawJson(String str) => GetEmployeeParkingClass.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory GetEmployeeParkingClass.fromJson(Map<String, dynamic> json) => GetEmployeeParkingClass(
|
||||
createdAt: json["createdAt"] == null ? null : DateTime.parse(json["createdAt"]),
|
||||
employeeCode: json["employeeCode"],
|
||||
isNewQrCode: json["isNewQRCode"],
|
||||
qrCode: json["qrCode"],
|
||||
qrCodeImageBase64: json["qrCodeImageBase64"],
|
||||
success: json["success"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"createdAt": createdAt?.toIso8601String(),
|
||||
"employeeCode": employeeCode,
|
||||
"isNewQRCode": isNewQrCode,
|
||||
"qrCode": qrCode,
|
||||
"qrCodeImageBase64": qrCodeImageBase64,
|
||||
"success": success,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class GetGreetingCardsModelResponse {
|
||||
List<GetGreetingCardsModelResponseElement>? getGreetingCardsModelResponse;
|
||||
|
||||
GetGreetingCardsModelResponse({
|
||||
this.getGreetingCardsModelResponse,
|
||||
});
|
||||
|
||||
factory GetGreetingCardsModelResponse.fromRawJson(String str) => GetGreetingCardsModelResponse.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory GetGreetingCardsModelResponse.fromJson(Map<String, dynamic> json) => GetGreetingCardsModelResponse(
|
||||
getGreetingCardsModelResponse: json["Get_GreetingCardsModel_Response"] == null ? [] : List<GetGreetingCardsModelResponseElement>.from(json["Get_GreetingCardsModel_Response"]!.map((x) => GetGreetingCardsModelResponseElement.fromJson(x))),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"Get_GreetingCardsModel_Response": getGreetingCardsModelResponse == null ? [] : List<dynamic>.from(getGreetingCardsModelResponse!.map((x) => x.toJson())),
|
||||
};
|
||||
}
|
||||
|
||||
class GetGreetingCardsModelResponseElement {
|
||||
String? backgroundImageUrlAr;
|
||||
String? backgroundImageUrlEn;
|
||||
String? buttonBgColor;
|
||||
String? buttonText;
|
||||
String? buttonTextN;
|
||||
int? cardType;
|
||||
int? categoryId;
|
||||
String? categoryNameAr;
|
||||
String? categoryNameEn;
|
||||
int? channel;
|
||||
String? createdOn;
|
||||
String? descriptionAr;
|
||||
String? descriptionEn;
|
||||
String? endDate;
|
||||
bool? hasButton;
|
||||
int? id;
|
||||
bool? isActive;
|
||||
int? moduleId;
|
||||
String? moduleName;
|
||||
String? moduleNameN;
|
||||
String? startDate;
|
||||
String? textColor;
|
||||
String? titleAr;
|
||||
String? titleEn;
|
||||
String? urlAr;
|
||||
String? urlEn;
|
||||
|
||||
GetGreetingCardsModelResponseElement({
|
||||
this.backgroundImageUrlAr,
|
||||
this.backgroundImageUrlEn,
|
||||
this.buttonBgColor,
|
||||
this.buttonText,
|
||||
this.buttonTextN,
|
||||
this.cardType,
|
||||
this.categoryId,
|
||||
this.categoryNameAr,
|
||||
this.categoryNameEn,
|
||||
this.channel,
|
||||
this.createdOn,
|
||||
this.descriptionAr,
|
||||
this.descriptionEn,
|
||||
this.endDate,
|
||||
this.hasButton,
|
||||
this.id,
|
||||
this.isActive,
|
||||
this.moduleId,
|
||||
this.moduleName,
|
||||
this.moduleNameN,
|
||||
this.startDate,
|
||||
this.textColor,
|
||||
this.titleAr,
|
||||
this.titleEn,
|
||||
this.urlAr,
|
||||
this.urlEn,
|
||||
});
|
||||
|
||||
factory GetGreetingCardsModelResponseElement.fromRawJson(String str) => GetGreetingCardsModelResponseElement.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory GetGreetingCardsModelResponseElement.fromJson(Map<String, dynamic> json) => GetGreetingCardsModelResponseElement(
|
||||
backgroundImageUrlAr: json["BackgroundImageUrlAr"],
|
||||
backgroundImageUrlEn: json["BackgroundImageUrlEn"],
|
||||
buttonBgColor: json["ButtonBgColor"],
|
||||
buttonText: json["ButtonText"],
|
||||
buttonTextN: json["ButtonTextN"],
|
||||
cardType: json["CardType"],
|
||||
categoryId: json["CategoryID"],
|
||||
categoryNameAr: json["CategoryNameAr"],
|
||||
categoryNameEn: json["CategoryNameEn"],
|
||||
channel: json["Channel"],
|
||||
createdOn: json["CreatedOn"],
|
||||
descriptionAr: json["DescriptionAr"],
|
||||
descriptionEn: json["DescriptionEn"],
|
||||
endDate: json["EndDate"],
|
||||
hasButton: json["HasButton"],
|
||||
id: json["ID"],
|
||||
isActive: json["IsActive"],
|
||||
moduleId: json["ModuleID"],
|
||||
moduleName: json["ModuleName"],
|
||||
moduleNameN: json["ModuleNameN"],
|
||||
startDate: json["StartDate"],
|
||||
textColor: json["TextColor"],
|
||||
titleAr: json["TitleAr"],
|
||||
titleEn: json["TitleEn"],
|
||||
urlAr: json["UrlAr"],
|
||||
urlEn: json["UrlEn"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"BackgroundImageUrlAr": backgroundImageUrlAr,
|
||||
"BackgroundImageUrlEn": backgroundImageUrlEn,
|
||||
"ButtonBgColor": buttonBgColor,
|
||||
"ButtonText": buttonText,
|
||||
"ButtonTextN": buttonTextN,
|
||||
"CardType": cardType,
|
||||
"CategoryID": categoryId,
|
||||
"CategoryNameAr": categoryNameAr,
|
||||
"CategoryNameEn": categoryNameEn,
|
||||
"Channel": channel,
|
||||
"CreatedOn": createdOn,
|
||||
"DescriptionAr": descriptionAr,
|
||||
"DescriptionEn": descriptionEn,
|
||||
"EndDate": endDate,
|
||||
"HasButton": hasButton,
|
||||
"ID": id,
|
||||
"IsActive": isActive,
|
||||
"ModuleID": moduleId,
|
||||
"ModuleName": moduleName,
|
||||
"ModuleNameN": moduleNameN,
|
||||
"StartDate": startDate,
|
||||
"TextColor": textColor,
|
||||
"TitleAr": titleAr,
|
||||
"TitleEn": titleEn,
|
||||
"UrlAr": urlAr,
|
||||
"UrlEn": urlEn,
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,233 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class GetFaDisposalNtfDetails {
|
||||
List<PFaBuyer>? pFaBuyers;
|
||||
List<PFaHeader>? pFaHeader;
|
||||
List<PFaLine>? pFaLines;
|
||||
String? pInformation;
|
||||
dynamic pQuestion;
|
||||
|
||||
GetFaDisposalNtfDetails({
|
||||
this.pFaBuyers,
|
||||
this.pFaHeader,
|
||||
this.pFaLines,
|
||||
this.pInformation,
|
||||
this.pQuestion,
|
||||
});
|
||||
|
||||
factory GetFaDisposalNtfDetails.fromRawJson(String str) => GetFaDisposalNtfDetails.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory GetFaDisposalNtfDetails.fromJson(Map<String, dynamic> json) => GetFaDisposalNtfDetails(
|
||||
pFaBuyers: json["P_FA_BUYERS"] == null ? [] : List<PFaBuyer>.from(json["P_FA_BUYERS"]!.map((x) => PFaBuyer.fromJson(x))),
|
||||
pFaHeader: json["P_FA_HEADER"] == null ? [] : List<PFaHeader>.from(json["P_FA_HEADER"]!.map((x) => PFaHeader.fromJson(x))),
|
||||
pFaLines: json["P_FA_LINES"] == null ? [] : List<PFaLine>.from(json["P_FA_LINES"]!.map((x) => PFaLine.fromJson(x))),
|
||||
pInformation: json["P_INFORMATION"],
|
||||
pQuestion: json["P_QUESTION"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"P_FA_BUYERS": pFaBuyers == null ? [] : List<dynamic>.from(pFaBuyers!.map((x) => x.toJson())),
|
||||
"P_FA_HEADER": pFaHeader == null ? [] : List<dynamic>.from(pFaHeader!.map((x) => x.toJson())),
|
||||
"P_FA_LINES": pFaLines == null ? [] : List<dynamic>.from(pFaLines!.map((x) => x.toJson())),
|
||||
"P_INFORMATION": pInformation,
|
||||
"P_QUESTION": pQuestion,
|
||||
};
|
||||
}
|
||||
|
||||
class PFaBuyer {
|
||||
int? amount;
|
||||
String? buyerName;
|
||||
String? buyerNumber;
|
||||
int? fromRowNum;
|
||||
String? highestBidder;
|
||||
int? noOfRows;
|
||||
String? receiptNumber;
|
||||
String? remarks;
|
||||
int? requestNo;
|
||||
int? rowNum;
|
||||
int? toRowNum;
|
||||
|
||||
PFaBuyer({
|
||||
this.amount,
|
||||
this.buyerName,
|
||||
this.buyerNumber,
|
||||
this.fromRowNum,
|
||||
this.highestBidder,
|
||||
this.noOfRows,
|
||||
this.receiptNumber,
|
||||
this.remarks,
|
||||
this.requestNo,
|
||||
this.rowNum,
|
||||
this.toRowNum,
|
||||
});
|
||||
|
||||
factory PFaBuyer.fromRawJson(String str) => PFaBuyer.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PFaBuyer.fromJson(Map<String, dynamic> json) => PFaBuyer(
|
||||
amount: json["AMOUNT"],
|
||||
buyerName: json["BUYER_NAME"],
|
||||
buyerNumber: json["BUYER_NUMBER"],
|
||||
fromRowNum: json["FROM_ROW_NUM"],
|
||||
highestBidder: json["HIGHEST_BIDDER"],
|
||||
noOfRows: json["NO_OF_ROWS"],
|
||||
receiptNumber: json["RECEIPT_NUMBER"],
|
||||
remarks: json["REMARKS"],
|
||||
requestNo: json["REQUEST_NO"],
|
||||
rowNum: json["ROW_NUM"],
|
||||
toRowNum: json["TO_ROW_NUM"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"AMOUNT": amount,
|
||||
"BUYER_NAME": buyerName,
|
||||
"BUYER_NUMBER": buyerNumber,
|
||||
"FROM_ROW_NUM": fromRowNum,
|
||||
"HIGHEST_BIDDER": highestBidder,
|
||||
"NO_OF_ROWS": noOfRows,
|
||||
"RECEIPT_NUMBER": receiptNumber,
|
||||
"REMARKS": remarks,
|
||||
"REQUEST_NO": requestNo,
|
||||
"ROW_NUM": rowNum,
|
||||
"TO_ROW_NUM": toRowNum,
|
||||
};
|
||||
}
|
||||
|
||||
class PFaHeader {
|
||||
String? bookTypeCode;
|
||||
String? categoryCode;
|
||||
String? categoryGroup;
|
||||
String? comments;
|
||||
int? requestNo;
|
||||
String? status;
|
||||
|
||||
PFaHeader({
|
||||
this.bookTypeCode,
|
||||
this.categoryCode,
|
||||
this.categoryGroup,
|
||||
this.comments,
|
||||
this.requestNo,
|
||||
this.status,
|
||||
});
|
||||
|
||||
factory PFaHeader.fromRawJson(String str) => PFaHeader.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PFaHeader.fromJson(Map<String, dynamic> json) => PFaHeader(
|
||||
bookTypeCode: json["BOOK_TYPE_CODE"],
|
||||
categoryCode: json["CATEGORY_CODE"],
|
||||
categoryGroup: json["CATEGORY_GROUP"],
|
||||
comments: json["COMMENTS"],
|
||||
requestNo: json["REQUEST_NO"],
|
||||
status: json["STATUS"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"BOOK_TYPE_CODE": bookTypeCode,
|
||||
"CATEGORY_CODE": categoryCode,
|
||||
"CATEGORY_GROUP": categoryGroup,
|
||||
"COMMENTS": comments,
|
||||
"REQUEST_NO": requestNo,
|
||||
"STATUS": status,
|
||||
};
|
||||
}
|
||||
|
||||
class PFaLine {
|
||||
String? action;
|
||||
String? assetDescription;
|
||||
int? assetNumber;
|
||||
String? barcodeNumber;
|
||||
String? bme;
|
||||
String? datePlacedInService;
|
||||
String? department;
|
||||
String? disposedDate;
|
||||
int? fromRowNum;
|
||||
double? netBookValue;
|
||||
int? noOfRows;
|
||||
String? poNumber;
|
||||
double? purchasePrice;
|
||||
int? quantity;
|
||||
int? requestNo;
|
||||
int? rowNum;
|
||||
String? serialNumber;
|
||||
int? toRowNum;
|
||||
int? usefulLife;
|
||||
String? yearsUsed;
|
||||
|
||||
PFaLine({
|
||||
this.action,
|
||||
this.assetDescription,
|
||||
this.assetNumber,
|
||||
this.barcodeNumber,
|
||||
this.bme,
|
||||
this.datePlacedInService,
|
||||
this.department,
|
||||
this.disposedDate,
|
||||
this.fromRowNum,
|
||||
this.netBookValue,
|
||||
this.noOfRows,
|
||||
this.poNumber,
|
||||
this.purchasePrice,
|
||||
this.quantity,
|
||||
this.requestNo,
|
||||
this.rowNum,
|
||||
this.serialNumber,
|
||||
this.toRowNum,
|
||||
this.usefulLife,
|
||||
this.yearsUsed,
|
||||
});
|
||||
|
||||
factory PFaLine.fromRawJson(String str) => PFaLine.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PFaLine.fromJson(Map<String, dynamic> json) => PFaLine(
|
||||
action: json["ACTION"],
|
||||
assetDescription: json["ASSET_DESCRIPTION"],
|
||||
assetNumber: json["ASSET_NUMBER"],
|
||||
barcodeNumber: json["BARCODE_NUMBER"],
|
||||
bme: json["BME"],
|
||||
datePlacedInService: json["DATE_PLACED_IN_SERVICE"],
|
||||
department: json["DEPARTMENT"],
|
||||
disposedDate: json["DISPOSED_DATE"],
|
||||
fromRowNum: json["FROM_ROW_NUM"],
|
||||
netBookValue: json["NET_BOOK_VALUE"]?.toDouble(),
|
||||
noOfRows: json["NO_OF_ROWS"],
|
||||
poNumber: json["PO_NUMBER"],
|
||||
purchasePrice: json["PURCHASE_PRICE"]?.toDouble(),
|
||||
quantity: json["QUANTITY"],
|
||||
requestNo: json["REQUEST_NO"],
|
||||
rowNum: json["ROW_NUM"],
|
||||
serialNumber: json["SERIAL_NUMBER"],
|
||||
toRowNum: json["TO_ROW_NUM"],
|
||||
usefulLife: json["USEFUL_LIFE"],
|
||||
yearsUsed: json["YEARS_USED"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"ACTION": action,
|
||||
"ASSET_DESCRIPTION": assetDescription,
|
||||
"ASSET_NUMBER": assetNumber,
|
||||
"BARCODE_NUMBER": barcodeNumber,
|
||||
"BME": bme,
|
||||
"DATE_PLACED_IN_SERVICE": datePlacedInService,
|
||||
"DEPARTMENT": department,
|
||||
"DISPOSED_DATE": disposedDate,
|
||||
"FROM_ROW_NUM": fromRowNum,
|
||||
"NET_BOOK_VALUE": netBookValue,
|
||||
"NO_OF_ROWS": noOfRows,
|
||||
"PO_NUMBER": poNumber,
|
||||
"PURCHASE_PRICE": purchasePrice,
|
||||
"QUANTITY": quantity,
|
||||
"REQUEST_NO": requestNo,
|
||||
"ROW_NUM": rowNum,
|
||||
"SERIAL_NUMBER": serialNumber,
|
||||
"TO_ROW_NUM": toRowNum,
|
||||
"USEFUL_LIFE": usefulLife,
|
||||
"YEARS_USED": yearsUsed,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,262 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mohem_flutter_app/api/etqan_ovr_api_client.dart';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart';
|
||||
import 'package:speech_to_text/speech_recognition_result.dart';
|
||||
import 'package:speech_to_text/speech_to_text.dart';
|
||||
|
||||
class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
|
||||
// Home screen state
|
||||
List<EtqanGetProjectsResponse>? getEtqanProjectsList;
|
||||
List<EtqanGetEmployeeOvrRequestsResponse>? getEtqanEmployeeRequestsList;
|
||||
List<EtqanGetIncidentRequestResponse>? getEtqanEmployeeIncidnetRequest;
|
||||
bool isLoading = false;
|
||||
String? _ticketId;
|
||||
String? _currentStatus;
|
||||
String? _currentStatusAr;
|
||||
bool? _currentIsAnonymous;
|
||||
|
||||
// Speech to text state
|
||||
final SpeechToText _speechToText = SpeechToText();
|
||||
bool _speechEnabled = false;
|
||||
bool _isListening = false;
|
||||
String _currentLocaleId = '';
|
||||
TextEditingController? _currentController;
|
||||
String _baseText = ''; // Text that was there when we started listening
|
||||
String _currentSessionText = ''; // Text captured in current listening session
|
||||
|
||||
// Getters for speech state
|
||||
bool get speechEnabled => _speechEnabled;
|
||||
|
||||
bool get isListening => _isListening;
|
||||
|
||||
// Getter for ticketId
|
||||
String get getTicketId => _ticketId ?? '';
|
||||
String get getCurrentStatus => _currentStatus ?? '';
|
||||
String get getCurrentStatusAr => _currentStatusAr ?? '';
|
||||
bool get getCurrentIsAnonymous => _currentIsAnonymous ?? false;
|
||||
|
||||
// Setter for ticketId with status and isAnonymous
|
||||
void setTicketId(String ticketId, {String? status, String? statusAr, bool? isAnonymous}) {
|
||||
_ticketId = ticketId;
|
||||
_currentStatus = status;
|
||||
_currentStatusAr = statusAr;
|
||||
_currentIsAnonymous = isAnonymous;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Create request screen state
|
||||
EtqanGetProjectsResponse? selectedProject;
|
||||
String? selectedEmployeeNumber;
|
||||
File? attachment;
|
||||
String? attachmentBase64;
|
||||
String description = '';
|
||||
bool isAnonymous = false;
|
||||
|
||||
/// Initialize speech recognition - call this once when screen loads
|
||||
Future<void> initSpeech() async {
|
||||
_speechEnabled = await _speechToText.initialize(
|
||||
onStatus: (String status) {
|
||||
print('Speech status: $status, isListening: $_isListening');
|
||||
// If user wants to keep listening but speech stopped, restart it
|
||||
if ((status == 'done' || status == 'notListening') && _isListening && _currentController != null) {
|
||||
// Save the finalized text before restarting
|
||||
if (_currentSessionText.isNotEmpty) {
|
||||
_baseText = _currentController!.text.trim();
|
||||
_currentSessionText = '';
|
||||
}
|
||||
|
||||
// Automatically restart listening after a brief pause
|
||||
Future.delayed(const Duration(milliseconds: 300), () {
|
||||
if (_isListening && _currentController != null) {
|
||||
_startListeningInternal(_currentLocaleId, _currentController!);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Internal method to start listening
|
||||
Future<void> _startListeningInternal(String localeId, TextEditingController controller) async {
|
||||
if (!_speechEnabled) return;
|
||||
|
||||
await _speechToText.listen(
|
||||
onResult: (SpeechRecognitionResult result) {
|
||||
_currentSessionText = result.recognizedWords;
|
||||
|
||||
// Combine base text with current session text
|
||||
String combinedText = _baseText.isEmpty
|
||||
? _currentSessionText
|
||||
: _baseText + (_baseText.endsWith(' ') ? '' : ' ') + _currentSessionText;
|
||||
|
||||
description = combinedText;
|
||||
controller.text = combinedText;
|
||||
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
|
||||
notifyListeners();
|
||||
},
|
||||
localeId: localeId,
|
||||
listenFor: const Duration(seconds: 60),
|
||||
pauseFor: const Duration(seconds: 10),
|
||||
listenOptions: SpeechListenOptions(
|
||||
partialResults: true,
|
||||
listenMode: ListenMode.confirmation,
|
||||
cancelOnError: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Start listening for speech input
|
||||
Future<void> startListening(String localeId, TextEditingController controller) async {
|
||||
_isListening = true;
|
||||
_currentLocaleId = localeId;
|
||||
_currentController = controller;
|
||||
_baseText = controller.text.trim(); // Save existing text
|
||||
_currentSessionText = '';
|
||||
notifyListeners();
|
||||
await _startListeningInternal(localeId, controller);
|
||||
}
|
||||
|
||||
/// Stop listening for speech input
|
||||
Future<void> stopListening() async {
|
||||
_isListening = false;
|
||||
_currentLocaleId = '';
|
||||
_currentController = null;
|
||||
_baseText = '';
|
||||
_currentSessionText = '';
|
||||
await _speechToText.stop();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Toggle listening state
|
||||
Future<void> toggleListening(String localeId, TextEditingController controller) async {
|
||||
if (_isListening) {
|
||||
await stopListening();
|
||||
} else {
|
||||
await startListening(localeId, controller);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch Etqan Projects
|
||||
Future<void> fetchEtqanProjects(BuildContext context) async {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
Utils.showLoading(context);
|
||||
getEtqanProjectsList?.clear();
|
||||
getEtqanProjectsList = await EtqanApiClient().getEtqanProjects();
|
||||
Utils.hideLoading(context);
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Fetch Etqan Employee Requests
|
||||
Future<void> fetchEtqanEmployeeRequests(BuildContext context) async {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
Utils.showLoading(context);
|
||||
getEtqanEmployeeRequestsList?.clear();
|
||||
getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests();
|
||||
Utils.hideLoading(context);
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Fetch Etqan Employee Incident Report
|
||||
Future<void> fetchEtqanEmployeeIncidentRequests(BuildContext context, String ticketNo) async {
|
||||
isLoading = true;
|
||||
notifyListeners();
|
||||
Utils.showLoading(context);
|
||||
getEtqanEmployeeIncidnetRequest?.clear();
|
||||
getEtqanEmployeeIncidnetRequest = await EtqanApiClient().getEtqanIncidentRequests(ticketNo);
|
||||
Utils.hideLoading(context);
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Set selected project
|
||||
void setSelectedProject(EtqanGetProjectsResponse? project) {
|
||||
selectedProject = project;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Set selected employee number
|
||||
void setSelectedEmployeeNumber(String? employeeNumber) {
|
||||
selectedEmployeeNumber = employeeNumber;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Set attachment with base64
|
||||
void setAttachment(File file, String base64) {
|
||||
attachment = file;
|
||||
attachmentBase64 = base64;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Remove attachment
|
||||
void removeAttachment() {
|
||||
attachment = null;
|
||||
attachmentBase64 = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Set description
|
||||
void setDescription(String value) {
|
||||
description = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Validate description
|
||||
bool isDescriptionValid() {
|
||||
return description.trim().isNotEmpty;
|
||||
}
|
||||
|
||||
// Set isAnonymous
|
||||
void setIsAnonymous(bool value) {
|
||||
isAnonymous = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Clear create request form
|
||||
void clearCreateRequestForm() {
|
||||
selectedProject = null;
|
||||
selectedEmployeeNumber = null;
|
||||
attachment = null;
|
||||
attachmentBase64 = null;
|
||||
description = '';
|
||||
isAnonymous = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Submit request
|
||||
Future<Map<String, dynamic>?> submitRequest(BuildContext context) async {
|
||||
try {
|
||||
Utils.showLoading(context);
|
||||
Map<String, dynamic>? response = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64, isAnonymous);
|
||||
Utils.hideLoading(context);
|
||||
if (response != null) {
|
||||
getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests();
|
||||
notifyListeners();
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
} catch (ex) {
|
||||
Utils.hideLoading(context);
|
||||
Utils.handleException(ex, context, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_isListening) {
|
||||
_speechToText.stop();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,360 @@
|
||||
import 'dart:io';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mohem_flutter_app/app_state/app_state.dart';
|
||||
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/config/routes.dart';
|
||||
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
|
||||
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart';
|
||||
import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart';
|
||||
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
|
||||
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
|
||||
import 'package:mohem_flutter_app/widgets/image_picker.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class EtqanOvrCreateRequest extends StatefulWidget {
|
||||
const EtqanOvrCreateRequest({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_EtqanOvrCreateRequestState createState() => _EtqanOvrCreateRequestState();
|
||||
}
|
||||
|
||||
class _EtqanOvrCreateRequestState extends State<EtqanOvrCreateRequest> {
|
||||
final TextEditingController _descriptionController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
var provider = context.read<EtqanOvrProviderModel>();
|
||||
provider.initSpeech();
|
||||
provider.fetchEtqanProjects(context);
|
||||
provider.clearCreateRequestForm();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Stop listening if active
|
||||
context.read<EtqanOvrProviderModel>().stopListening();
|
||||
_descriptionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBarWidget(context, title: LocaleKeys.reportIncident.tr(), showHomeButton: false),
|
||||
body: Consumer<EtqanOvrProviderModel>(
|
||||
builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
(ListView(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.all(21),
|
||||
children: <Widget>[
|
||||
_buildDropdownField(
|
||||
label: LocaleKeys.project.tr(),
|
||||
hint: LocaleKeys.selectProject.tr(),
|
||||
value: _getProjectDisplayName(provider.selectedProject),
|
||||
onTap: () => _showProjectSelectionSheet(context, provider),
|
||||
isMandatory: true,
|
||||
),
|
||||
16.height,
|
||||
// _buildDropdownField(
|
||||
// label: 'Employee Number',
|
||||
// hint: 'Write a message',
|
||||
// value: provider.selectedEmployeeNumber,
|
||||
// onTap: () {
|
||||
// // TODO: Show employee selection dialog
|
||||
// },
|
||||
// ),
|
||||
// 16.height,
|
||||
_buildAttachmentSection(provider),
|
||||
16.height,
|
||||
_buildDescriptionField(provider),
|
||||
8.height,
|
||||
_buildAnonymousCheckbox(provider),
|
||||
24.height,
|
||||
],
|
||||
)).expanded,
|
||||
DefaultButton(LocaleKeys.submit.tr(), () async {
|
||||
provider.setDescription(_descriptionController.text);
|
||||
if (provider.selectedProject == null) {
|
||||
Utils.showErrorDialog(
|
||||
context: context,
|
||||
message: LocaleKeys.pleaseSelectProject.tr(),
|
||||
onOkTapped: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!provider.isDescriptionValid()) {
|
||||
Utils.showErrorDialog(
|
||||
context: context,
|
||||
message: LocaleKeys.pleaseEnterDescription.tr(),
|
||||
onOkTapped: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
Map<String, dynamic>? ticketInfo = await provider.submitRequest(context);
|
||||
if (ticketInfo != null) {
|
||||
String ticketNumber = ticketInfo['ticketNumber'] ?? '';
|
||||
String ticketId = ticketInfo['id'] ?? '';
|
||||
// String successMessage = '${LocaleKeys.requestCreatedSuccessfully.tr()}\n\n${LocaleKeys.ticketNumber.tr()}: $ticketNumber\n${LocaleKeys.ticketId.tr()}: $ticketId';
|
||||
String successMessage = '${LocaleKeys.requestCreatedSuccessfully.tr()}\n\n${LocaleKeys.ticketNumber.tr()}: $ticketNumber';
|
||||
Utils.showErrorDialog(
|
||||
context: context,
|
||||
message: successMessage,
|
||||
onOkTapped: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
// Navigator.popAndPushNamed(context, AppRoutes.etqanOvr);
|
||||
},
|
||||
onCloseTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
// Navigator.popAndPushNamed(context, AppRoutes.etqanOvr);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).insideContainer,
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String? _getProjectDisplayName(EtqanGetProjectsResponse? project) {
|
||||
if (project == null) return null;
|
||||
return AppState().isArabic(context) ? project.projectAr : project.projectEn;
|
||||
}
|
||||
|
||||
void _showProjectSelectionSheet(BuildContext context, EtqanOvrProviderModel provider) {
|
||||
if (provider.getEtqanProjectsList == null || provider.getEtqanProjectsList!.isEmpty) {
|
||||
return;
|
||||
}
|
||||
showMyBottomSheet(
|
||||
context,
|
||||
callBackFunc: () {},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Text(LocaleKeys.selectProject.tr(), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xff2B353E))),
|
||||
),
|
||||
const Divider(),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: provider.getEtqanProjectsList!.length,
|
||||
separatorBuilder: (BuildContext context, int index) => const Divider(height: 1),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
EtqanGetProjectsResponse project = provider.getEtqanProjectsList![index];
|
||||
bool isSelected = provider.selectedProject?.projectId == project.projectId;
|
||||
return ListTile(
|
||||
title: Text(
|
||||
AppState().isArabic(context) ? (project.projectAr ?? '') : (project.projectEn ?? ''),
|
||||
style: TextStyle(fontSize: 14, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? MyColors.gradiantEndColor : const Color(0xff2B353E)),
|
||||
),
|
||||
trailing: isSelected ? const Icon(Icons.check, color: MyColors.gradiantEndColor) : null,
|
||||
onTap: () {
|
||||
provider.setSelectedProject(project);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
16.height,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDropdownField({required String label, required String hint, String? value, required VoidCallback onTap, bool isMandatory = false}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Text(label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)),
|
||||
if (isMandatory) const Text(' *', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Colors.red, letterSpacing: -0.44)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(value ?? hint, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: value != null ? const Color(0xff2B353E) : const Color(0xff575757), letterSpacing: -0.56)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Icon(Icons.keyboard_arrow_down, color: Color(0xff575757)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAttachmentSection(EtqanOvrProviderModel provider) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(LocaleKeys.supportingDocument.tr(), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E))),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(LocaleKeys.attachments.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E))),
|
||||
if (provider.attachment == null)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
ImageOptions.showImageOptionsNew(context, true, (String base64, File file) {
|
||||
provider.setAttachment(file, base64);
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
|
||||
decoration: BoxDecoration(color: MyColors.gradiantEndColor, borderRadius: BorderRadius.circular(8)),
|
||||
child: Text(LocaleKeys.add.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (provider.attachment != null) ...<Widget>[
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
const Icon(Icons.attach_file, size: 18, color: Color(0xff575757)),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text(provider.attachment!.path.split('/').last, style: const TextStyle(fontSize: 12, color: Color(0xff575757)), overflow: TextOverflow.ellipsis)),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
provider.removeAttachment();
|
||||
},
|
||||
child: const Icon(Icons.close, size: 18, color: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDescriptionField(EtqanOvrProviderModel provider) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Text(LocaleKeys.description.tr(), style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)),
|
||||
const Text(' *', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Colors.red, letterSpacing: -0.44)),
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
onTap:
|
||||
provider.speechEnabled
|
||||
? () {
|
||||
String localeId = AppState().isArabic(context) ? 'ar_SA' : 'en_US';
|
||||
provider.toggleListening(localeId, _descriptionController);
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(color: provider.isListening ? MyColors.gradiantEndColor.withOpacity(0.1) : Colors.transparent, borderRadius: BorderRadius.circular(20)),
|
||||
child: Icon(
|
||||
provider.isListening ? Icons.mic : Icons.mic_none,
|
||||
color: provider.isListening ? MyColors.gradiantEndColor : (provider.speechEnabled ? MyColors.grey57Color : Colors.grey.shade300),
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (provider.isListening)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(width: 8, height: 8, decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(4))),
|
||||
const SizedBox(width: 8),
|
||||
const Text('Listening...', style: TextStyle(fontSize: 12, color: MyColors.gradiantEndColor, fontWeight: FontWeight.w500)),
|
||||
],
|
||||
),
|
||||
),
|
||||
TextField(
|
||||
controller: _descriptionController,
|
||||
maxLines: 4,
|
||||
onChanged: (String value) => provider.setDescription(value),
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xff2B353E), letterSpacing: -0.44),
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: LocaleKeys.writeAMessage.tr(),
|
||||
hintStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xff575757), letterSpacing: -0.56),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAnonymousCheckbox(EtqanOvrProviderModel provider) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
provider.setIsAnonymous(!provider.isAnonymous);
|
||||
},
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Checkbox(
|
||||
value: provider.isAnonymous,
|
||||
onChanged: (bool? value) {
|
||||
if (value != null) {
|
||||
provider.setIsAnonymous(value);
|
||||
}
|
||||
},
|
||||
activeColor: MyColors.gradiantEndColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
|
||||
),
|
||||
|
||||
Expanded(child: Text(LocaleKeys.submitAsAnonymous.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E), letterSpacing: -0.44))),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
import 'package:easy_localization/easy_localization.dart' as lclize;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:mohem_flutter_app/app_state/app_state.dart';
|
||||
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||
import 'package:mohem_flutter_app/config/routes.dart';
|
||||
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
|
||||
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart';
|
||||
import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/src/intl/text_direction.dart' as intl;
|
||||
import 'package:sizer/sizer.dart';
|
||||
|
||||
class EtqanOvrHome extends StatefulWidget {
|
||||
const EtqanOvrHome({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_EtqanOvrHomeState createState() => _EtqanOvrHomeState();
|
||||
}
|
||||
|
||||
class _EtqanOvrHomeState extends State<EtqanOvrHome> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.read<EtqanOvrProviderModel>().fetchEtqanEmployeeRequests(context);
|
||||
});
|
||||
}
|
||||
|
||||
Color _getStatusColor(String status) {
|
||||
String statusLower = status.toLowerCase();
|
||||
if (statusLower.contains('open') || statusLower.contains('مفتوح')) {
|
||||
return const Color(0xff2196F3); // Blue for open
|
||||
} else if (statusLower.contains('progress') || statusLower.contains('قيد التنفيذ')) {
|
||||
return const Color(0xffFF9800); // Orange for in progress
|
||||
} else if (statusLower.contains('closed') || statusLower.contains('مغلق')) {
|
||||
return const Color(0xff4CAF50); // Green for closed
|
||||
}
|
||||
return MyColors.grey70Color; // Default gray
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBarWidget(context, title: LocaleKeys.myIncidents.tr()),
|
||||
body: Consumer<EtqanOvrProviderModel>(
|
||||
builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
(provider.getEtqanEmployeeRequestsList == null || provider.getEtqanEmployeeRequestsList!.isEmpty
|
||||
? Utils.getNoDataWidget(context)
|
||||
: ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
EtqanGetEmployeeOvrRequestsResponse data = provider.getEtqanEmployeeRequestsList![index];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
context.read<EtqanOvrProviderModel>().setTicketId(data.ticketNumber ?? '', status: data.status, statusAr: data.statusAr, isAnonymous: data.isAnonymous);
|
||||
Navigator.pushNamed(context, AppRoutes.etqanGetIncidientRequest);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15), border: Border.all(color: const Color(0xffefefef), width: 1)),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
if (data.status != null && data.status!.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: _getStatusColor(data.status ?? "").withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: _getStatusColor(data.status ?? "").withOpacity(0.3), width: 1),
|
||||
),
|
||||
child: (AppState().isArabic(context) ? (data.statusAr ?? "") : (data.status ?? "")).toText11(
|
||||
color: _getStatusColor(data.status ?? ""),
|
||||
isBold: true,
|
||||
),
|
||||
),
|
||||
if (data.isAnonymous == true)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: MyColors.grey70Color.withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: MyColors.grey70Color.withOpacity(0.3), width: 1),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.visibility_off, size: 12, color: MyColors.grey70Color),
|
||||
const SizedBox(width: 4),
|
||||
(AppState().isArabic(context) ? "مجهول" : "Anonymous").toText11(color: MyColors.grey70Color, isBold: true),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
(data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor),
|
||||
Directionality(textDirection: TextDirection.ltr, child: (data.createdDate!.split(" ").first ?? "").toText12(color: MyColors.grey70Color)),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [(data.description ?? "").toText12(color: MyColors.grey57Color).expanded]),
|
||||
3.height,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
RotatedBox(
|
||||
quarterTurns: AppState().isArabic(context) ? 2 : 0,
|
||||
child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.grey70Color, width: 16, height: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) => 12.height,
|
||||
itemCount: provider.getEtqanEmployeeRequestsList!.length,
|
||||
))
|
||||
.expanded,
|
||||
DefaultButton(LocaleKeys.reportIncident.tr(), () async {
|
||||
await Navigator.pushNamed(context, AppRoutes.etqanCreateRequest);
|
||||
// getOpenTickets();
|
||||
}).insideContainer,
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,235 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mohem_flutter_app/app_state/app_state.dart';
|
||||
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
|
||||
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
||||
import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart';
|
||||
import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:open_filex/open_filex.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class EtqanOvrRequestDetailed extends StatefulWidget {
|
||||
const EtqanOvrRequestDetailed({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_EtqanOvrRequestDetailedState createState() => _EtqanOvrRequestDetailedState();
|
||||
}
|
||||
|
||||
class _EtqanOvrRequestDetailedState extends State<EtqanOvrRequestDetailed> {
|
||||
List<String> get fieldOrder => ['Status', 'Project', 'Created', 'Is Anonymous', 'Occurrence Time', 'OVR Reference No', 'Involved Employee Number', 'Description'];
|
||||
|
||||
List<String> get hiddenFields => ['OVR Id'];
|
||||
|
||||
Color _getStatusColor(String status) {
|
||||
String statusLower = status.toLowerCase();
|
||||
if (statusLower.contains('open') || statusLower.contains('مفتوح')) {
|
||||
return const Color(0xff2196F3); // Blue for open
|
||||
} else if (statusLower.contains('progress') || statusLower.contains('قيد التنفيذ')) {
|
||||
return const Color(0xffFF9800); // Orange for in progress
|
||||
} else if (statusLower.contains('closed') || statusLower.contains('مغلق')) {
|
||||
return const Color(0xff4CAF50); // Green for closed
|
||||
}
|
||||
return MyColors.grey70Color; // Default gray
|
||||
}
|
||||
|
||||
Future<void> _viewAttachment(BuildContext context, String base64Data) async {
|
||||
try {
|
||||
Uint8List bytes = base64.decode(base64Data);
|
||||
String dir = (await getApplicationDocumentsDirectory()).path;
|
||||
String ext = 'pdf';
|
||||
if (base64Data.startsWith('/9j/')) {
|
||||
ext = 'jpg';
|
||||
} else if (base64Data.startsWith('iVBORw0KGgo')) {
|
||||
ext = 'png';
|
||||
}
|
||||
File file = File("$dir/${DateTime.now().millisecondsSinceEpoch}.$ext");
|
||||
await file.writeAsBytes(bytes);
|
||||
OpenFilex.open(file.path);
|
||||
} catch (e) {
|
||||
Utils.showToast("Cannot open attachment.");
|
||||
}
|
||||
}
|
||||
|
||||
List<EtqanGetIncidentRequestResponse> _getOrderedList(List<EtqanGetIncidentRequestResponse> list, EtqanOvrProviderModel provider) {
|
||||
List<EtqanGetIncidentRequestResponse> orderedList = [];
|
||||
|
||||
List<EtqanGetIncidentRequestResponse> filteredList = list.where((EtqanGetIncidentRequestResponse item) => !hiddenFields.any((f) => f.toLowerCase() == item.key?.toLowerCase())).toList();
|
||||
|
||||
for (String fieldName in fieldOrder) {
|
||||
// Check if this is Status or Is Anonymous - inject from provider
|
||||
if (fieldName.toLowerCase() == 'status' && provider.getCurrentStatus.isNotEmpty) {
|
||||
orderedList.add(EtqanGetIncidentRequestResponse(
|
||||
key: 'Status',
|
||||
keyAr: 'الحالة',
|
||||
value: provider.getCurrentStatus,
|
||||
valueAr: provider.getCurrentStatusAr.isNotEmpty ? provider.getCurrentStatusAr : provider.getCurrentStatus,
|
||||
));
|
||||
} else if (fieldName.toLowerCase() == 'is anonymous') {
|
||||
orderedList.add(EtqanGetIncidentRequestResponse(
|
||||
key: 'Is Anonymous',
|
||||
keyAr: 'مجهول',
|
||||
value: provider.getCurrentIsAnonymous.toString(),
|
||||
valueAr: provider.getCurrentIsAnonymous.toString(),
|
||||
));
|
||||
} else {
|
||||
// Regular field from API
|
||||
var item = filteredList.firstWhere((e) => e.key?.toLowerCase() == fieldName.toLowerCase(), orElse: () => EtqanGetIncidentRequestResponse());
|
||||
if (item.key != null) {
|
||||
orderedList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var item in filteredList) {
|
||||
if (!fieldOrder.any((f) => f.toLowerCase() == item.key?.toLowerCase())) {
|
||||
orderedList.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return orderedList;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
EtqanOvrProviderModel provider = context.read<EtqanOvrProviderModel>();
|
||||
provider.fetchEtqanEmployeeIncidentRequests(context, provider.getTicketId);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBarWidget(context, title: LocaleKeys.incidentDetails.tr()),
|
||||
body: Consumer<EtqanOvrProviderModel>(
|
||||
builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) {
|
||||
if (provider.getEtqanEmployeeIncidnetRequest == null || provider.getEtqanEmployeeIncidnetRequest!.isEmpty) {
|
||||
return Utils.getNoDataWidget(context);
|
||||
}
|
||||
|
||||
List<EtqanGetIncidentRequestResponse> orderedList = _getOrderedList(provider.getEtqanEmployeeIncidnetRequest!, provider);
|
||||
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: const Color(0xffefefef), width: 1),
|
||||
boxShadow: <BoxShadow>[BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 2))],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children:
|
||||
orderedList.asMap().entries.map((MapEntry<int, EtqanGetIncidentRequestResponse> entry) {
|
||||
int index = entry.key;
|
||||
EtqanGetIncidentRequestResponse data = entry.value;
|
||||
bool isLast = index == orderedList.length - 1;
|
||||
bool isAttachment = data.key?.toLowerCase() == 'attachment';
|
||||
bool isStatus = data.key?.toLowerCase() == 'status';
|
||||
bool isAnonymous = data.key?.toLowerCase() == 'is anonymous';
|
||||
|
||||
String displayKey = context.locale.languageCode == "ar" ? (data.keyAr ?? data.key ?? "") : (data.key ?? "");
|
||||
String displayValue = context.locale.languageCode == "ar" ? (data.valueAr ?? data.value ?? "") : (data.value ?? "");
|
||||
|
||||
bool hasSubValues = data.subValues != null && data.subValues!.isNotEmpty;
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(flex: 2, child: displayKey.toText12(isBold: true, color: MyColors.grey57Color)),
|
||||
8.width,
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: isStatus
|
||||
? displayValue.toText14(
|
||||
color: _getStatusColor(displayValue),
|
||||
isBold: true,
|
||||
)
|
||||
: isAnonymous
|
||||
? (displayValue.toLowerCase() == 'true' || displayValue.toLowerCase() == 'yes' || displayValue == '1'
|
||||
? (context.locale.languageCode == "ar" ? "نعم" : "Yes")
|
||||
: (context.locale.languageCode == "ar" ? "لا" : "No")
|
||||
).toText14(color: MyColors.darkTextColor)
|
||||
: isAttachment && hasSubValues
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: data.subValues!.asMap().entries.map((entry) {
|
||||
int subIndex = entry.key;
|
||||
String base64Data = entry.value;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: subIndex < data.subValues!.length - 1 ? 8.0 : 0),
|
||||
child: GestureDetector(
|
||||
onTap: () => _viewAttachment(context, base64Data),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: context.locale.languageCode == "ar" ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
data.subValues!.length > 1 ? '${LocaleKeys.viewAttachment.tr()} ${subIndex + 1}' : LocaleKeys.viewAttachment.tr(),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: MyColors.green9CColor,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: MyColors.green9CColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)
|
||||
: hasSubValues
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (displayValue.isNotEmpty) displayValue.toText14(color: MyColors.darkTextColor),
|
||||
if (displayValue.isNotEmpty) 8.height,
|
||||
...data.subValues!
|
||||
.map(
|
||||
(subValue) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [const Text("• ", style: TextStyle(fontSize: 14)), Expanded(child: subValue.toText14(color: MyColors.darkTextColor))],
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
],
|
||||
)
|
||||
: (displayValue.isNotEmpty ? displayValue : "-").toText14(color: MyColors.darkTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!isLast) const Padding(padding: EdgeInsets.only(left: 14, right: 14), child: Divider(height: 1, thickness: 1, color: Color(0xffefefef))),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).paddingOnly(bottom: 20),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mohem_flutter_app/api/dashboard_api_client.dart';
|
||||
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
||||
import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart';
|
||||
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||
|
||||
class ParkingQrScreen extends StatefulWidget {
|
||||
const ParkingQrScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_RequestDetailsState createState() => _RequestDetailsState();
|
||||
}
|
||||
|
||||
class _RequestDetailsState extends State<ParkingQrScreen> {
|
||||
GetEmployeeParking? qrData;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getTicketDetails();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBarWidget(context, title: LocaleKeys.parkingQr.tr()),
|
||||
body:
|
||||
qrData?.getEmployeeParking == null
|
||||
? Center(child: Text(LocaleKeys.noDataAvailable.tr()))
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.2), spreadRadius: 5, blurRadius: 10, offset: const Offset(0, 3))],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// QR Code Image
|
||||
if (qrData!.getEmployeeParking!.qrCodeImageBase64 != null && qrData!.getEmployeeParking!.qrCodeImageBase64!.isNotEmpty)
|
||||
Image.memory(Utils.dataFromBase64String(qrData!.getEmployeeParking!.qrCodeImageBase64!), width: 300, height: 300, fit: BoxFit.contain)
|
||||
else
|
||||
Container(
|
||||
width: 300,
|
||||
height: 300,
|
||||
color: MyColors.lightGreyEFColor,
|
||||
child: Center(child: Text(LocaleKeys.noDataAvailable.tr(), style: const TextStyle(color: MyColors.grey67Color))),
|
||||
),
|
||||
10.height,
|
||||
Container(height: 1, color: MyColors.lightGreyE3Color, margin: const EdgeInsets.symmetric(vertical: 10)),
|
||||
20.height,
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Employee Code
|
||||
if (qrData!.getEmployeeParking!.employeeCode != null && qrData!.getEmployeeParking!.employeeCode!.isNotEmpty) ...[
|
||||
Text(LocaleKeys.employeeNumber.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)),
|
||||
8.height,
|
||||
Text(qrData!.getEmployeeParking!.employeeCode!, style: const TextStyle(fontSize: 18, color: MyColors.darkTextColor, fontWeight: FontWeight.w600)),
|
||||
8.height,
|
||||
],
|
||||
if (qrData!.getEmployeeParking!.createdAt != null) ...[
|
||||
Text(LocaleKeys.creationDate.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)),
|
||||
8.height,
|
||||
Text(
|
||||
DateFormat('dd MMM yyyy, hh:mm a').format(qrData!.getEmployeeParking!.createdAt!),
|
||||
style: const TextStyle(fontSize: 16, color: MyColors.darkTextColor, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void getTicketDetails() async {
|
||||
try {
|
||||
Utils.showLoading(context);
|
||||
qrData = await DashboardApiClient().getEmployeeParkingQrCode();
|
||||
Utils.hideLoading(context);
|
||||
setState(() {});
|
||||
} catch (ex) {
|
||||
Utils.hideLoading(context);
|
||||
Utils.handleException(ex, context, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,333 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:mohem_flutter_app/app_state/app_state.dart';
|
||||
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||
import 'package:mohem_flutter_app/classes/inAppWebView.dart';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
|
||||
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
||||
import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart';
|
||||
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
|
||||
import 'package:mohem_flutter_app/theme/colors.dart' as theme_colors;
|
||||
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||
import 'package:mohem_flutter_app/widgets/sso_webview_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||
|
||||
class CoursesScreen extends StatefulWidget {
|
||||
const CoursesScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_CoursesScreenState createState() => _CoursesScreenState();
|
||||
}
|
||||
|
||||
class _CoursesScreenState extends State<CoursesScreen> {
|
||||
late DashboardProviderModel data;
|
||||
final RefreshController _refreshController = RefreshController(initialRefresh: false);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
data = Provider.of<DashboardProviderModel>(context, listen: false);
|
||||
_onRefresh();
|
||||
});
|
||||
}
|
||||
|
||||
void _onRefresh() async {
|
||||
await data.fetchCourses(context);
|
||||
_refreshController.refreshCompleted();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBarWidget(
|
||||
context,
|
||||
title: LocaleKeys.courses.tr(),
|
||||
),
|
||||
body: Consumer<DashboardProviderModel>(
|
||||
builder: (context, provider, child) {
|
||||
return SmartRefresher(
|
||||
enablePullDown: true,
|
||||
enablePullUp: false,
|
||||
controller: _refreshController,
|
||||
onRefresh: _onRefresh,
|
||||
child: provider.isCoursesLoading
|
||||
? _buildShimmerLoading()
|
||||
: provider.coursesList.isEmpty
|
||||
? _buildEmptyState()
|
||||
: _buildCoursesList(provider.coursesList),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildShimmerLoading() {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(21),
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xff000000).withOpacity(.05),
|
||||
blurRadius: 26,
|
||||
offset: const Offset(0, -3),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 20,
|
||||
width: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
).toShimmer(),
|
||||
12.height,
|
||||
Container(
|
||||
height: 14,
|
||||
width: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
).toShimmer(),
|
||||
const Spacer(),
|
||||
Container(
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
).toShimmer(),
|
||||
],
|
||||
).paddingAll(14),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
return Utils.getNoDataWidget(context);
|
||||
}
|
||||
|
||||
Widget _buildCoursesList(List<CourseStatus> courses) {
|
||||
return ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(21),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return _buildCourseCard(courses[index]);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) => 12.height,
|
||||
itemCount: courses.length,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCourseCard(CourseStatus course) {
|
||||
bool isArabic = AppState().isArabic(context);
|
||||
String courseName = isArabic && course.cNameAr != null && course.cNameAr!.isNotEmpty
|
||||
? course.cNameAr!
|
||||
: course.cFullname ?? '';
|
||||
|
||||
String status = course.cStatus ?? 'Unknown';
|
||||
Color statusColor = _getStatusColor(status);
|
||||
|
||||
return InkWell(
|
||||
onTap: () => _openCourse(course.cUrl, course.token),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Course Title
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: MyColors.lightGreyEFColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.school_rounded,
|
||||
color: MyColors.gradiantStartColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: courseName.toText14(isBold: true, color: MyColors.darkTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
12.height,
|
||||
// Status Badge
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
_getStatusIcon(status),
|
||||
size: 16,
|
||||
color: statusColor,
|
||||
),
|
||||
6.width,
|
||||
LocaleKeys.status.tr().toText11(color: MyColors.grey98Color),
|
||||
8.width,
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: statusColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: statusColor.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: status.toText10(color: statusColor, isBold: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
10.height,
|
||||
// Start Date
|
||||
if (course.cEnddate != null && course.cEnddate!.isNotEmpty)
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.calendar_month_rounded,
|
||||
size: 16,
|
||||
color: MyColors.darkIconColor,
|
||||
),
|
||||
6.width,
|
||||
LocaleKeys.endDateT.tr().toText11(color: MyColors.grey98Color),
|
||||
8.width,
|
||||
Expanded(
|
||||
child: _formatDate(course.cEnddate!).toText11(
|
||||
color: MyColors.darkTextColor,
|
||||
isBold: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (course.cStartdate != null && course.cStartdate!.isNotEmpty) 14.height,
|
||||
// Open Button
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
LocaleKeys.open.tr().toText12(color: MyColors.gradiantStartColor, isUnderLine: true),
|
||||
6.width,
|
||||
RotatedBox(
|
||||
quarterTurns: AppState().isArabic(context) ? 2 : 4,
|
||||
child: SvgPicture.asset(
|
||||
"assets/images/arrow_next.svg",
|
||||
color: MyColors.gradiantStartColor,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).objectContainerView(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
IconData _getStatusIcon(String status) {
|
||||
if (status.toLowerCase().contains('passed')) {
|
||||
return Icons.check_circle_rounded;
|
||||
} else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) {
|
||||
return Icons.cancel_rounded;
|
||||
} else if (status.toLowerCase().contains('in progress')) {
|
||||
return Icons.pending_rounded;
|
||||
} else {
|
||||
return Icons.info_rounded;
|
||||
}
|
||||
}
|
||||
|
||||
Color _getStatusColor(String status) {
|
||||
if (status.toLowerCase().contains('passed') && !status.toLowerCase().contains('not passed')) {
|
||||
return Colors.green;
|
||||
} else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) {
|
||||
return Colors.red;
|
||||
} else if (status.toLowerCase().contains('in progress')) {
|
||||
return Colors.yellow[700]!;
|
||||
} else {
|
||||
return MyColors.grey98Color;
|
||||
}
|
||||
}
|
||||
|
||||
DateTime? _parseDate(String? date) {
|
||||
if (date == null || date.isEmpty) return null;
|
||||
try {
|
||||
return DateTime.parse(date.replaceAll(' ', 'T'));
|
||||
} catch (e) {
|
||||
try {
|
||||
DateFormat format = DateFormat('yyyy-MM-dd HH:mm:ss');
|
||||
return format.parse(date);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _formatDate(String date) {
|
||||
DateTime? dateTime = _parseDate(date);
|
||||
if (dateTime == null) return date;
|
||||
|
||||
bool isArabic = AppState().isArabic(context);
|
||||
DateFormat format = DateFormat('dd MMM yyyy, hh:mm a', isArabic ? 'ar' : 'en');
|
||||
return format.format(dateTime);
|
||||
}
|
||||
|
||||
void _openCourse(String? url, String? jwtToken) {
|
||||
|
||||
|
||||
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: url, jwtToken: jwtToken)));
|
||||
//Utils.showToast(LocaleKeys.courseUrlNotAvailable.tr());
|
||||
|
||||
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// MyInAppBrowser browser = MyInAppBrowser(
|
||||
// onExitCallback: () {},
|
||||
// onLoadStartCallback: (String url) {},
|
||||
// );
|
||||
//
|
||||
// var settings = InAppBrowserClassSettings(
|
||||
// webViewSettings: InAppWebViewSettings(
|
||||
// useShouldOverrideUrlLoading: false,
|
||||
// transparentBackground: false,
|
||||
// ),
|
||||
// browserSettings: InAppBrowserSettings(
|
||||
// hideUrlBar: true,
|
||||
// hideTitleBar: false,
|
||||
// toolbarTopBackgroundColor: theme_colors.primaryColor,
|
||||
// ),
|
||||
// );
|
||||
//
|
||||
// browser.openUrlRequest(
|
||||
// urlRequest: URLRequest(url: WebUri(url)),
|
||||
// settings: settings,
|
||||
// );
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_refreshController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mohem_flutter_app/app_state/app_state.dart';
|
||||
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||
import 'package:mohem_flutter_app/classes/date_uitl.dart';
|
||||
import 'package:mohem_flutter_app/classes/utils.dart';
|
||||
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
|
||||
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
|
||||
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
|
||||
import 'package:mohem_flutter_app/models/dashboard/get_accural_ticket_balance_model.dart';
|
||||
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
|
||||
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
|
||||
import 'package:mohem_flutter_app/widgets/balances_dashboard_widget.dart';
|
||||
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
|
||||
import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart';
|
||||
import 'package:mohem_flutter_app/widgets/sso_webview_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class TicketDetailedScreen extends StatefulWidget {
|
||||
final String? url;
|
||||
final String? jwtToken;
|
||||
|
||||
const TicketDetailedScreen({super.key, this.url, this.jwtToken});
|
||||
|
||||
@override
|
||||
State<TicketDetailedScreen> createState() => _TicketDetailedScreenState();
|
||||
}
|
||||
|
||||
class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
|
||||
DashboardProviderModel? dashboardProviderModel;
|
||||
late DateTime accrualDateTime;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
dashboardProviderModel = Provider.of<DashboardProviderModel>(context, listen: false);
|
||||
accrualDateTime = DateTime.now();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void changeAccrualDate(bool showLoading) async {
|
||||
try {
|
||||
if (showLoading) Utils.showLoading(context);
|
||||
await dashboardProviderModel?.fetchTicketAccuralBalance(context, accrualDateTime);
|
||||
if (showLoading) Utils.hideLoading(context);
|
||||
setState(() {});
|
||||
} catch (ex) {
|
||||
if (showLoading) Utils.hideLoading(context);
|
||||
Utils.handleException(ex, context, null);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBarWidget(context, title:LocaleKeys.ticketDetails.tr()),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
ListView(
|
||||
padding: const EdgeInsets.all(21),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: <Widget>[
|
||||
if (dashboardProviderModel == null && dashboardProviderModel?.accrualTicketBalanceList == null) ...<Widget>[Utils.getNoDataWidget(context).paddingOnly(top: 50)] else ...<Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
boxShadow: <BoxShadow>[BoxShadow(color: Colors.grey.withOpacity(0.2), spreadRadius: 2, blurRadius: 5, offset: const Offset(0, 3))],
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
LocaleKeys.currentTicketBalance.tr().toText20().expanded,
|
||||
Row(
|
||||
children: <Widget>[
|
||||
const Icon(Icons.calendar_month_rounded, color: MyColors.darkIconColor, size: 16),
|
||||
5.width,
|
||||
DateUtil.formatDateToDate(accrualDateTime, AppState().isArabic(context)).toText13(isUnderLine: true),
|
||||
8.width,
|
||||
],
|
||||
).onPress(() async {
|
||||
DateTime selectedDate = await Utils.selectDate(context, accrualDateTime);
|
||||
if (selectedDate != accrualDateTime) {
|
||||
accrualDateTime = selectedDate;
|
||||
changeAccrualDate(true);
|
||||
}
|
||||
}),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children:
|
||||
dashboardProviderModel!.accrualTicketBalanceList!.map<Widget>((GetTicketAccuralBalanceModel item) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
item.accuralPlanCode.toString().replaceAll('_', ' ').toCamelCase.toText14(),
|
||||
item.accrualNetEntitlement.toString().toText16(color: MyColors.textMixColor, isBold: true),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 21),
|
||||
dashboardProviderModel == null && dashboardProviderModel!.ticketHistoryTransactionList == null
|
||||
? const SizedBox()
|
||||
: ListView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: <Widget>[
|
||||
LocaleKeys.ticketHistory.tr().toText20().expanded,
|
||||
12.height,
|
||||
ListView.separated(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
itemBuilder: (BuildContext cxt, int cardIndex) {
|
||||
var transactionDetails = dashboardProviderModel!.ticketHistoryTransactionList![cardIndex];
|
||||
const allowedSegmentNames = <String>{
|
||||
"TICKETS_ROUTE",
|
||||
"TRAVELER_NAME",
|
||||
"TICKETS_EFFECTIVE_DATE",
|
||||
"TICKET_ARRIVAL",
|
||||
"TICKET_AMOUNT",
|
||||
"TICKET_COMP_SHARE",
|
||||
"TICKET_EMP_SHARE",
|
||||
};
|
||||
var uniqueDetails =
|
||||
transactionDetails
|
||||
.where((item) => item.dISPLAYFLAG != 'N' && item.sEGMENTPROMPT != null && item.sEGMENTPROMPT!.isNotEmpty && allowedSegmentNames.contains(item.sEGMENTNAME))
|
||||
.toList();
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: (uniqueDetails.length / 2).ceil(),
|
||||
itemBuilder: (context, rowIndex) {
|
||||
int firstIndex = rowIndex * 2;
|
||||
int? secondIndex = (firstIndex + 1 < uniqueDetails.length) ? firstIndex + 1 : null;
|
||||
bool isLastRow = (rowIndex == (uniqueDetails.length / 2).ceil() - 1);
|
||||
var item1 = uniqueDetails[firstIndex];
|
||||
var child1 = ItemDetailViewCol(item1.sEGMENTPROMPT!, item1.vARCHAR2VALUE ?? item1.nUMBERVALUE?.toString() ?? "");
|
||||
Widget child2;
|
||||
if (secondIndex != null) {
|
||||
var item2 = uniqueDetails[secondIndex];
|
||||
child2 = ItemDetailViewCol(item2.sEGMENTPROMPT!, item2.vARCHAR2VALUE ?? item2.nUMBERVALUE?.toString() ?? "");
|
||||
} else {
|
||||
child2 = const SizedBox(); // Empty widget if there is no second item
|
||||
}
|
||||
return ItemDetailGrid(child1, child2, isItLast: isLastRow);
|
||||
},
|
||||
),
|
||||
],
|
||||
).objectContainerView();
|
||||
},
|
||||
separatorBuilder: (BuildContext cxt, int index) => 12.height,
|
||||
itemCount: dashboardProviderModel!.ticketHistoryTransactionList!.length,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
).expanded,
|
||||
DefaultButton(LocaleKeys.proceedBooking.tr(), () {
|
||||
if (widget.url != null && widget.jwtToken != null) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: widget.url, jwtToken: widget.jwtToken)));
|
||||
}
|
||||
}).insideContainer,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||