Compare commits
17 Commits
master
...
etqan_ovr_
| Author | SHA1 | Date |
|---|---|---|
|
|
dae746587f | 19 hours ago |
|
|
facb3bf4e6 | 19 hours ago |
|
|
71d03ba664 | 19 hours ago |
|
|
9f663df3cc | 21 hours ago |
|
|
a8f390d49f | 1 day ago |
|
|
2c7d3bf963 | 4 days ago |
|
|
31d1804f7a | 2 weeks ago |
|
|
048c9b395f | 2 weeks ago |
|
|
eea35e4d77 | 2 weeks ago |
|
|
12d8fd95ba | 1 month ago |
|
|
eebaafcf7c | 1 month ago |
|
|
a28eb4426b | 1 month ago |
|
|
dfa7d9a713 | 1 month ago |
|
|
9112b26987 | 1 month ago |
|
|
74bcf3e1b7 | 2 months ago |
|
|
912f7b23f1 | 2 months ago |
|
|
99414bf787 | 2 months ago |
@ -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,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,53 @@
|
||||
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;
|
||||
String? ticketNumber;
|
||||
|
||||
EtqanGetEmployeeOvrRequestsResponse({
|
||||
this.createdDate,
|
||||
this.description,
|
||||
this.id,
|
||||
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"],
|
||||
ticketNumber: json["ticketNumber"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"createdDate": createdDate,
|
||||
"description": description,
|
||||
"id": id,
|
||||
"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,253 @@
|
||||
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;
|
||||
|
||||
// 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 ?? '';
|
||||
|
||||
// Setter for ticketId
|
||||
void setTicketId(String ticketId) {
|
||||
_ticketId = ticketId;
|
||||
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,355 @@
|
||||
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';
|
||||
Utils.showErrorDialog(
|
||||
context: context,
|
||||
message: successMessage,
|
||||
onOkTapped: () {
|
||||
Navigator.popAndPushNamed(context, AppRoutes.etqanOvr);
|
||||
},
|
||||
onCloseTap: () {
|
||||
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,108 @@
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
@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 ?? '');
|
||||
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(
|
||||
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,
|
||||
const SizedBox(width: 5,),
|
||||
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,195 @@
|
||||
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/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 => ['Project', 'Created', 'Occurrence Time', 'OVR Reference No', 'Involved Employee Number', 'Description'];
|
||||
|
||||
List<String> get hiddenFields => ['OVR Id', 'Is Anonymous'];
|
||||
|
||||
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) {
|
||||
List<EtqanGetIncidentRequestResponse> orderedList = [];
|
||||
|
||||
List<EtqanGetIncidentRequestResponse> filteredList = list.where((EtqanGetIncidentRequestResponse item) => !hiddenFields.any((f) => f.toLowerCase() == item.key?.toLowerCase())).toList();
|
||||
|
||||
for (String fieldName in fieldOrder) {
|
||||
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!);
|
||||
|
||||
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';
|
||||
|
||||
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:
|
||||
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(),
|
||||
// textAlign: context.locale.languageCode == "ar" ? TextAlign.start : TextAlign.start,
|
||||
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),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue