From d6a36b42d6f7555f0bd958b4c05ce276c470ccd0 Mon Sep 17 00:00:00 2001 From: EngDann Date: Wed, 14 Aug 2024 11:51:23 -0300 Subject: [PATCH 01/15] Fire alarm completo e integrado Co-authored-by: Ramires rocha Co-authored-by: Kauan Jose Co-authored-by: OscarDeBrito Co-authored-by: Pedro Lucas --- .../0038_remove_equipmentphoto_observation.py | 17 +++++++++++++++++ api/sigeie/settings.py | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 api/equipments/migrations/0038_remove_equipmentphoto_observation.py diff --git a/api/equipments/migrations/0038_remove_equipmentphoto_observation.py b/api/equipments/migrations/0038_remove_equipmentphoto_observation.py new file mode 100644 index 00000000..26bd5069 --- /dev/null +++ b/api/equipments/migrations/0038_remove_equipmentphoto_observation.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2 on 2024-08-14 13:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('equipments', '0037_atmosphericdischargeequipment_observation_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='equipmentphoto', + name='observation', + ), + ] diff --git a/api/sigeie/settings.py b/api/sigeie/settings.py index 3892e1df..8bd1b060 100644 --- a/api/sigeie/settings.py +++ b/api/sigeie/settings.py @@ -6,7 +6,7 @@ DEBUG = True -ALLOWED_HOSTS = ['127.0.0.1', '10.0.2.2', '192.168.1.141'] +ALLOWED_HOSTS = ['127.0.0.1', '10.0.2.2', '192.168.1.142'] INSTALLED_APPS = [ 'django.contrib.admin', From 5ce604ad27a59660c3efd42c8def2ed1e87c7ead Mon Sep 17 00:00:00 2001 From: EngDann Date: Wed, 14 Aug 2024 12:00:11 -0300 Subject: [PATCH 02/15] Fire alarm completo e integrado --- .../sige_ie/lib/core/data/universalURL.dart | 2 +- .../fire_alarm/fire_alarm_request_model.dart | 12 ++++--- .../fire_alarm_response_by_area_model.dart | 27 +++++++------- .../fire_alarm/fire_alarm_response_model.dart | 27 +++++++------- .../data/fire_alarm/fire_alarm_service.dart | 13 ++++--- .../feature/fire_alarm/add_fire_alarm.dart | 35 ++++++++++++------- .../feature/fire_alarm/list_fire_alarms.dart | 8 ++--- 7 files changed, 72 insertions(+), 52 deletions(-) diff --git a/frontend/sige_ie/lib/core/data/universalURL.dart b/frontend/sige_ie/lib/core/data/universalURL.dart index df0a439b..bfc10c71 100644 --- a/frontend/sige_ie/lib/core/data/universalURL.dart +++ b/frontend/sige_ie/lib/core/data/universalURL.dart @@ -1,3 +1,3 @@ const String urlUniversal = - 'http://192.168.1.141:8000'; + 'http://192.168.1.142:8000'; /* local host: http://10.0.2.2:8000 */ \ No newline at end of file diff --git a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_request_model.dart b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_request_model.dart index b84ecd16..e4465a6f 100644 --- a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_request_model.dart @@ -2,18 +2,20 @@ class FireAlarmRequestModel { int? area; int? system; int? quantity; + String? observation; - FireAlarmRequestModel({ - required this.area, - required this.system, - this.quantity, - }); + FireAlarmRequestModel( + {required this.area, + required this.system, + required this.quantity, + required this.observation}); Map toJson() { return { 'area': area, 'system': system, 'quantity': quantity, + 'observation': observation }; } } diff --git a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_by_area_model.dart index ef320836..449d911c 100644 --- a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_by_area_model.dart @@ -4,23 +4,24 @@ class FireAlarmEquipmentResponseByAreaModel { String equipmentCategory; int system; int quantity; + String? observation; - FireAlarmEquipmentResponseByAreaModel({ - required this.id, - required this.area, - required this.equipmentCategory, - required this.system, - required this.quantity, - }); + FireAlarmEquipmentResponseByAreaModel( + {required this.id, + required this.area, + required this.equipmentCategory, + required this.system, + required this.quantity, + required this.observation}); factory FireAlarmEquipmentResponseByAreaModel.fromJson( Map json) { return FireAlarmEquipmentResponseByAreaModel( - id: json['id'], - area: json['area'], - equipmentCategory: json['equipment_category'], - system: json['system'], - quantity: json['quantity'], - ); + id: json['id'], + area: json['area'], + equipmentCategory: json['equipment_category'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_model.dart b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_model.dart index c6430edf..a7308ac2 100644 --- a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_response_model.dart @@ -4,22 +4,23 @@ class FireAlarmResponseModel { int equipment; int system; int quantity; + String? observation; - FireAlarmResponseModel({ - required this.id, - required this.area, - required this.equipment, - required this.system, - required this.quantity, - }); + FireAlarmResponseModel( + {required this.id, + required this.area, + required this.equipment, + required this.system, + required this.quantity, + required this.observation}); factory FireAlarmResponseModel.fromJson(Map json) { return FireAlarmResponseModel( - id: json['id'], - area: json['area'], - equipment: json['equipment'], - system: json['system'], - quantity: json['quantity'], - ); + id: json['id'], + area: json['area'], + equipment: json['equipment'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_service.dart b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_service.dart index 0b6c44a2..5f83d3d0 100644 --- a/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_service.dart +++ b/frontend/sige_ie/lib/equipments/data/fire_alarm/fire_alarm_service.dart @@ -28,13 +28,18 @@ class FireAlarmEquipmentService { .map((data) => FireAlarmEquipmentResponseByAreaModel.fromJson(data)) .toList(); } else { - _logger.info( - 'Failed to load fire alarm equipment with status code: ${response.statusCode}'); + String errorMessage = + 'Failed to load fire alarm equipment. Status code: ${response.statusCode}'; + if (response.statusCode == 404) { + errorMessage = 'Fire alarm equipment not found for areaId $areaId'; + } + _logger.warning(errorMessage); _logger.info('Response body: ${response.body}'); - throw Exception('Failed to load fire alarm equipment'); + throw Exception(errorMessage); } } catch (e) { - _logger.info('Error during get fire alarm equipment list: $e'); + _logger + .severe('Error during get fire alarm equipment list from $url: $e'); throw Exception('Failed to load fire alarm equipment'); } } diff --git a/frontend/sige_ie/lib/equipments/feature/fire_alarm/add_fire_alarm.dart b/frontend/sige_ie/lib/equipments/feature/fire_alarm/add_fire_alarm.dart index 8c164ef1..7280de42 100644 --- a/frontend/sige_ie/lib/equipments/feature/fire_alarm/add_fire_alarm.dart +++ b/frontend/sige_ie/lib/equipments/feature/fire_alarm/add_fire_alarm.dart @@ -95,7 +95,10 @@ class _AddEquipmentScreenState extends State { setState(() { equipmentId = fireAlarmResponseModel!.equipment; _quantity.text = fireAlarmResponseModel!.quantity.toString(); + _observationsController.text = + fireAlarmResponseModel!.observation ?? ''; print('Loaded quantity: ${_quantity.text}'); + print('Loaded observations: ${_observationsController.text}'); }); _fetchEquipmentDetails(fireAlarmResponseModel!.equipment); @@ -172,18 +175,20 @@ class _AddEquipmentScreenState extends State { await personalEquipmentCategoryService .getAllPersonalEquipmentCategoryBySystem(widget.systemId); - setState(() { - genericEquipmentTypes = genericEquipmentCategoryList - .map((e) => {'id': e.id, 'name': e.name, 'type': 'generico'}) - .toList(); - personalEquipmentTypes = personalEquipmentCategoryList - .map((e) => {'id': e.id, 'name': e.name, 'type': 'pessoal'}) - .toList(); - personalEquipmentMap = { - for (var equipment in personalEquipmentCategoryList) - equipment.name: equipment.id - }; - }); + if (mounted) { + setState(() { + genericEquipmentTypes = genericEquipmentCategoryList + .map((e) => {'id': e.id, 'name': e.name, 'type': 'generico'}) + .toList(); + personalEquipmentTypes = personalEquipmentCategoryList + .map((e) => {'id': e.id, 'name': e.name, 'type': 'pessoal'}) + .toList(); + personalEquipmentMap = { + for (var equipment in personalEquipmentCategoryList) + equipment.name: equipment.id + }; + }); + } } @override @@ -330,6 +335,9 @@ class _AddEquipmentScreenState extends State { area: widget.areaId, system: widget.systemId, quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); print('Fire Alarm Model: ${fireAlarmModel.toJson()}'); @@ -626,6 +634,9 @@ class _AddEquipmentScreenState extends State { area: widget.areaId, system: widget.systemId, quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); final FireAlarmEquipmentRequestModel fireAlarmEquipmentDetail = diff --git a/frontend/sige_ie/lib/equipments/feature/fire_alarm/list_fire_alarms.dart b/frontend/sige_ie/lib/equipments/feature/fire_alarm/list_fire_alarms.dart index 61b8b48f..7a774fd7 100644 --- a/frontend/sige_ie/lib/equipments/feature/fire_alarm/list_fire_alarms.dart +++ b/frontend/sige_ie/lib/equipments/feature/fire_alarm/list_fire_alarms.dart @@ -208,14 +208,14 @@ class _ListFireAlarmsState extends State { IconButton( icon: const Icon(Icons.edit, color: Colors.blue), - onPressed: () => - _editFireAlarm(context, fireAlarmEquipment.id), + onPressed: () => _editFireAlarm( + context, fireAlarmEquipment.id), ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), - onPressed: () => - _confirmDelete(context, fireAlarmEquipment.id), + onPressed: () => _confirmDelete( + context, fireAlarmEquipment.id), ), ], ), From 4c37f4b28b3416d2db44244277891b2f274d7583 Mon Sep 17 00:00:00 2001 From: EngDann Date: Wed, 14 Aug 2024 12:17:10 -0300 Subject: [PATCH 03/15] Structured Cabling completo e integrado Co-authored-by: Ramires rocha Co-authored-by: Kauan Jose Co-authored-by: OscarDeBrito Co-authored-by: Pedro Lucas --- .../structured_cabling_request_model.dart | 13 ++++- ...ctured_cabling_response_by_area_model.dart | 27 ++++----- .../structured_cabling_response_model.dart | 27 ++++----- .../add_structured_cabling.dart | 55 ++++++++++++++++++- 4 files changed, 93 insertions(+), 29 deletions(-) diff --git a/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_request_model.dart b/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_request_model.dart index dbe7230f..2ecee00d 100644 --- a/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_request_model.dart @@ -2,11 +2,20 @@ class StructuredCablingRequestModel { int? area; int? system; int? quantity; + String? observation; StructuredCablingRequestModel( - {required this.area, required this.system, this.quantity}); + {required this.area, + required this.system, + required this.quantity, + required this.observation}); Map toJson() { - return {'area': area, 'system': system, 'quantity': quantity}; + return { + 'area': area, + 'system': system, + 'quantity': quantity, + 'observation': observation + }; } } diff --git a/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_by_area_model.dart index 604bdbd4..3a7885ab 100644 --- a/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_by_area_model.dart @@ -4,23 +4,24 @@ class StructuredCablingEquipmentResponseByAreaModel { String equipmentCategory; int system; int quantity; + String? observation; - StructuredCablingEquipmentResponseByAreaModel({ - required this.id, - required this.area, - required this.equipmentCategory, - required this.system, - required this.quantity, - }); + StructuredCablingEquipmentResponseByAreaModel( + {required this.id, + required this.area, + required this.equipmentCategory, + required this.system, + required this.quantity, + required this.observation}); factory StructuredCablingEquipmentResponseByAreaModel.fromJson( Map json) { return StructuredCablingEquipmentResponseByAreaModel( - id: json['id'], - area: json['area'], - equipmentCategory: json['equipment_category'], - system: json['system'], - quantity: json['quantity'], - ); + id: json['id'], + area: json['area'], + equipmentCategory: json['equipment_category'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_model.dart b/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_model.dart index 28f41b3f..93795199 100644 --- a/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/structured_cabling/structured_cabling_response_model.dart @@ -4,22 +4,23 @@ class StructuredCablingResponseModel { int equipment; int system; int quantity; + String? observation; - StructuredCablingResponseModel({ - required this.id, - required this.area, - required this.equipment, - required this.system, - required this.quantity, - }); + StructuredCablingResponseModel( + {required this.id, + required this.area, + required this.equipment, + required this.system, + required this.quantity, + required this.observation}); factory StructuredCablingResponseModel.fromJson(Map json) { return StructuredCablingResponseModel( - id: json['id'], - area: json['area'], - equipment: json['equipment'], - system: json['system'], - quantity: json['quantity'], - ); + id: json['id'], + area: json['area'], + equipment: json['equipment'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/feature/structured_cabling/add_structured_cabling.dart b/frontend/sige_ie/lib/equipments/feature/structured_cabling/add_structured_cabling.dart index 06541e54..1abebcb3 100644 --- a/frontend/sige_ie/lib/equipments/feature/structured_cabling/add_structured_cabling.dart +++ b/frontend/sige_ie/lib/equipments/feature/structured_cabling/add_structured_cabling.dart @@ -68,6 +68,7 @@ class _AddStructuredCablingScreenState extends State { GenericEquipmentCategoryService genericEquipmentCategoryService = GenericEquipmentCategoryService(); final TextEditingController _quantity = TextEditingController(); + final TextEditingController _observationsController = TextEditingController(); String? _selectedType; int? _selectedGenericEquipmentCategoryId; int? _selectedPersonalEquipmentCategoryId; @@ -97,6 +98,8 @@ class _AddStructuredCablingScreenState extends State { setState(() { equipmentId = structuredCablingResponseModel!.equipment; _quantity.text = structuredCablingResponseModel!.quantity.toString(); + _observationsController.text = + structuredCablingResponseModel!.observation ?? ''; print('Loaded quantity: ${_quantity.text}'); }); @@ -191,6 +194,7 @@ class _AddStructuredCablingScreenState extends State { @override void dispose() { _quantity.dispose(); + _observationsController.dispose(); _images.clear(); super.dispose(); } @@ -335,6 +339,9 @@ class _AddStructuredCablingScreenState extends State { area: widget.areaId, system: widget.systemId, quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); print('Structured Cabling Model: ${structuredCablingModel.toJson()}'); @@ -400,6 +407,7 @@ class _AddStructuredCablingScreenState extends State { _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -556,6 +564,10 @@ class _AddStructuredCablingScreenState extends State { style: TextStyle(fontWeight: FontWeight.bold)), Text(_quantity.text), const SizedBox(height: 10), + const Text('Observações:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_observationsController.text), + const SizedBox(height: 10), const Text('Imagens:', style: TextStyle(fontWeight: FontWeight.bold)), Wrap( @@ -628,6 +640,9 @@ class _AddStructuredCablingScreenState extends State { area: widget.areaId, system: widget.systemId, quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); final StructuredCablingEquipmentRequestModel @@ -681,6 +696,7 @@ class _AddStructuredCablingScreenState extends State { _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -850,7 +866,7 @@ class _AddStructuredCablingScreenState extends State { }, child: const Text('Limpar seleção'), ), - const SizedBox(height: 30), + const SizedBox(height: 8), const Text('Quantidade', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), @@ -877,6 +893,43 @@ class _AddStructuredCablingScreenState extends State { ), ), const SizedBox(height: 15), + const Text('Observações', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _observationsController, + maxLines: 3, + maxLength: 500, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: + 'Digite suas observações aqui (máx 500 caracteres)', + ), + buildCounter: (BuildContext context, + {required int currentLength, + required bool isFocused, + required int? maxLength}) { + return Text( + '$currentLength / $maxLength', + style: TextStyle( + fontSize: 12, + color: currentLength > maxLength! + ? Colors.red + : Colors.grey, + ), + ); + }, + ), + ), + const SizedBox(height: 15), IconButton( icon: const Icon(Icons.camera_alt), onPressed: _pickImage, From 8431f5ffc9b28d41351ab858ddaf427534af4176 Mon Sep 17 00:00:00 2001 From: EngDann Date: Wed, 14 Aug 2024 12:32:12 -0300 Subject: [PATCH 04/15] Eletrical Load completo e integrado Co-authored-by: Ramires rocha Co-authored-by: Kauan Jose Co-authored-by: OscarDeBrito Co-authored-by: Pedro Lucas --- .../eletrical_load_request_model.dart | 1 + .../eletrical_load_request_model.dart.dart | 7 ++++-- ...eletrical_load_response_by_area_model.dart | 22 ++++++++++--------- .../eletrical_load_response_model.dart | 22 ++++++++++--------- .../electrical_line/add_electrical_line.dart | 1 - .../electrical_load/add_electrical_load.dart | 13 +++++++++-- 6 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart diff --git a/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart new file mode 100644 index 00000000..b9f78bb0 --- /dev/null +++ b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart @@ -0,0 +1 @@ +// TODO Implement this library. \ No newline at end of file diff --git a/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart.dart b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart.dart index 10f7d370..1b6fb0b3 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_request_model.dart.dart @@ -5,6 +5,7 @@ class EletricalLoadRequestModel { double? power; String? brand; String? model; + String? observation; EletricalLoadRequestModel( {required this.area, @@ -12,7 +13,8 @@ class EletricalLoadRequestModel { required this.quantity, required this.power, required this.brand, - required this.model}); + required this.model, + required this.observation}); Map toJson() { return { @@ -21,7 +23,8 @@ class EletricalLoadRequestModel { 'quantity': quantity, 'power': power, 'brand': brand, - 'model': model + 'model': model, + 'observation': observation }; } } diff --git a/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_by_area_model.dart index f85b8c03..81e6b15b 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_by_area_model.dart @@ -7,6 +7,7 @@ class EletricalLoadEquipmentResponseByAreaModel { int? power; String? brand; String? model; + String? observation; EletricalLoadEquipmentResponseByAreaModel( {required this.id, @@ -16,19 +17,20 @@ class EletricalLoadEquipmentResponseByAreaModel { required this.quantity, required this.power, required this.brand, - required this.model}); + required this.model, + required this.observation}); factory EletricalLoadEquipmentResponseByAreaModel.fromJson( Map json) { return EletricalLoadEquipmentResponseByAreaModel( - id: json['id'], - area: json['area'], - equipmentCategory: json['equipment_category'], - system: json['system'], - quantity: json['quantity'], - power: json['power'], - brand: json['brand'], - model: json['model'], - ); + id: json['id'], + area: json['area'], + equipmentCategory: json['equipment_category'], + system: json['system'], + quantity: json['quantity'], + power: json['power'], + brand: json['brand'], + model: json['model'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_model.dart index d7b86542..a71d65b8 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical-load/eletrical_load_response_model.dart @@ -7,6 +7,7 @@ class EletricalLoadResponseModel { int power; String brand; String model; + String? observation; EletricalLoadResponseModel( {required this.id, @@ -16,18 +17,19 @@ class EletricalLoadResponseModel { required this.quantity, required this.power, required this.brand, - required this.model}); + required this.model, + required this.observation}); factory EletricalLoadResponseModel.fromJson(Map json) { return EletricalLoadResponseModel( - id: json['id'], - area: json['area'], - equipment: json['equipment'], - system: json['system'], - quantity: json['quantity'], - power: json['power'], - brand: json['brand'], - model: json['model'], - ); + id: json['id'], + area: json['area'], + equipment: json['equipment'], + system: json['system'], + quantity: json['quantity'], + power: json['power'], + brand: json['brand'], + model: json['model'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart b/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart index 4fdbc4eb..084def23 100644 --- a/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart +++ b/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart @@ -7,7 +7,6 @@ import 'package:sige_ie/config/app_styles.dart'; import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_equipment_request_model.dart'; import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_request_model.dart'; import 'package:sige_ie/equipments/data/equipment_service.dart'; - import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_request_model.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_service.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_response_model.dart'; diff --git a/frontend/sige_ie/lib/equipments/feature/electrical_load/add_electrical_load.dart b/frontend/sige_ie/lib/equipments/feature/electrical_load/add_electrical_load.dart index 4025fc68..fad75328 100644 --- a/frontend/sige_ie/lib/equipments/feature/electrical_load/add_electrical_load.dart +++ b/frontend/sige_ie/lib/equipments/feature/electrical_load/add_electrical_load.dart @@ -4,7 +4,6 @@ import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:sige_ie/config/app_styles.dart'; import 'package:sige_ie/equipments/data/eletrical-load/eletrical_load_equipment_request_model.dart'; -import 'package:sige_ie/equipments/data/eletrical-load/eletrical_load_request_model.dart.dart'; import 'package:sige_ie/equipments/data/eletrical-load/eletrical_load_response_model.dart'; import 'package:sige_ie/equipments/data/eletrical-load/eletrical_load_service.dart'; import 'package:sige_ie/equipments/data/equipment_service.dart'; @@ -16,6 +15,8 @@ import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_request_model.dart'; import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_service.dart'; +import '../../data/eletrical-load/eletrical_load_request_model.dart.dart'; + class ImageData { int id; File imageFile; @@ -27,7 +28,7 @@ class ImageData { required this.imageFile, required this.description, this.toDelete = false, - }) : id = id ?? -1; // Valor padrão indicando ID inexistente + }) : id = id ?? -1; } List _images = []; @@ -107,6 +108,8 @@ class _AddElectricalLoadEquipmentScreenState _brandController.text = electricalLoadResponseModel!.brand; _modelController.text = electricalLoadResponseModel!.model; _powerController.text = electricalLoadResponseModel!.power.toString(); + _observationsController.text = + electricalLoadResponseModel!.observation ?? ''; print('Loaded quantity: ${_quantityController.text}'); }); @@ -349,6 +352,9 @@ class _AddElectricalLoadEquipmentScreenState brand: _brandController.text, model: _modelController.text, power: double.tryParse(_powerController.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); print('Electrical Load Model: ${electricalLoadModel.toJson()}'); @@ -665,6 +671,9 @@ class _AddElectricalLoadEquipmentScreenState brand: _brandController.text, model: _modelController.text, power: double.tryParse(_powerController.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); final EletricalLoadEquipmentRequestModel electricalLoadEquipmentDetail = From 1bdbec31e6a13766bc61296ebf891381c910b40a Mon Sep 17 00:00:00 2001 From: EngDann Date: Thu, 15 Aug 2024 14:52:22 -0300 Subject: [PATCH 05/15] Eletrical Circuit completo e integrado --- .../eletrical_circuit_request_model.dart | 15 +- ...trical_circuit_response_by_area_model.dart | 26 +- .../eletrical_circuit_response_model.dart | 17 +- .../add_electrical_circuit.dart | 582 +++++++++++++----- .../electrical_circuit_list.dart | 330 +++++----- 5 files changed, 641 insertions(+), 329 deletions(-) diff --git a/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_request_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_request_model.dart index d52d7a4b..3c7899d6 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_request_model.dart @@ -1,23 +1,26 @@ class EletricalCircuitRequestModel { int? area; int? system; - int? quantity; int? size; - String isolamento; + String? type_wire; + String? type_circuit_breaker; + String? observation; EletricalCircuitRequestModel( {required this.area, required this.system, - required this.quantity, required this.size, - required this.isolamento}); + required this.type_wire, + required this.type_circuit_breaker, + required this.observation}); Map toJson() { return { 'area': area, 'system': system, - 'quantity': quantity, 'size': size, - 'isolamento': isolamento + 'observation': observation, + 'type_wire': type_wire, + 'type_circuit_breaker': type_circuit_breaker }; } } diff --git a/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_by_area_model.dart index 536ed48a..0b158bd0 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_by_area_model.dart @@ -2,30 +2,32 @@ class EletricalCircuitEquipmentResponseByAreaModel { int id; int area; int system; - int quantity; int size; String equipmentCategory; - String isolamento; + String type_wire; + String type_circuit_breaker; + String? observation; EletricalCircuitEquipmentResponseByAreaModel( {required this.id, required this.area, required this.system, - required this.quantity, required this.size, required this.equipmentCategory, - required this.isolamento}); + required this.observation, + required this.type_wire, + required this.type_circuit_breaker}); factory EletricalCircuitEquipmentResponseByAreaModel.fromJson( Map json) { return EletricalCircuitEquipmentResponseByAreaModel( - id: json['id'], - area: json['area'], - system: json['system'], - quantity: json['quantity'], - size: json['size'], - equipmentCategory: json['equipment_category'], - isolamento: json['isolamento'], - ); + id: json['id'], + area: json['area'], + system: json['system'], + size: json['size'], + equipmentCategory: json['equipment_category'], + observation: json['observation'], + type_wire: json['type_wire'], + type_circuit_breaker: json['type_circuit_breaker']); } } diff --git a/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_model.dart index 8a0dfa09..f43889ca 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical_circuit/eletrical_circuit_response_model.dart @@ -1,20 +1,22 @@ class EletricalCircuitResponseModel { int id; int area; - int equipment; int system; - int quantity; int size; - String isolament; + int equipment; + String type_wire; + String type_circuit_breaker; + String? observation; EletricalCircuitResponseModel( {required this.id, required this.area, required this.equipment, required this.system, - required this.quantity, required this.size, - required this.isolament}); + required this.observation, + required this.type_wire, + required this.type_circuit_breaker}); factory EletricalCircuitResponseModel.fromJson(Map json) { return EletricalCircuitResponseModel( @@ -22,8 +24,9 @@ class EletricalCircuitResponseModel { area: json['area'], equipment: json['equipment'], system: json['system'], - quantity: json['quantity'], size: json['size'], - isolament: json['isolament']); + observation: json['observation'], + type_wire: json['type_wire'], + type_circuit_breaker: json['type_circuit_breaker']); } } diff --git a/frontend/sige_ie/lib/equipments/feature/electrical_circuit/add_electrical_circuit.dart b/frontend/sige_ie/lib/equipments/feature/electrical_circuit/add_electrical_circuit.dart index c40f015c..00b1fa50 100644 --- a/frontend/sige_ie/lib/equipments/feature/electrical_circuit/add_electrical_circuit.dart +++ b/frontend/sige_ie/lib/equipments/feature/electrical_circuit/add_electrical_circuit.dart @@ -1,86 +1,177 @@ -import 'dart:math'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'dart:io'; import 'package:image_picker/image_picker.dart'; import 'package:sige_ie/config/app_styles.dart'; -import 'package:sige_ie/equipments/data/eletrical_circuit/eletrical_circuit_equipment_request_model.dart'; -import 'package:sige_ie/equipments/data/eletrical_circuit/eletrical_circuit_request_model.dart'; -import 'package:sige_ie/equipments/data/equipment_service.dart'; +import 'package:sige_ie/equipments/data/eletrical_circuit/eletrical_circuit_response_model.dart'; +import 'package:sige_ie/equipments/data/eletrical_circuit/eletrical_circuit_service.dart'; + import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_request_model.dart'; +import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_response_model.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_service.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_response_model.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_service.dart'; +import 'package:sige_ie/equipments/data/equipment_service.dart'; +import 'package:sige_ie/equipments/data/eletrical_circuit/eletrical_circuit_equipment_request_model.dart'; +import 'package:sige_ie/equipments/data/eletrical_circuit/eletrical_circuit_request_model.dart'; import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_request_model.dart'; import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_service.dart'; class ImageData { - File imageFile; int id; + File imageFile; String description; + bool toDelete; - ImageData(this.imageFile, this.description) : id = Random().nextInt(1000000); + ImageData({ + int? id, + required this.imageFile, + required this.description, + this.toDelete = false, + }) : id = id ?? -1; } List _images = []; -Map> categoryImagesMap = {}; -class AddElectricalCircuitEquipmentScreen extends StatefulWidget { +class AddElectricalCircuit extends StatefulWidget { final String areaName; final String localName; final int localId; final int systemId; final int areaId; + final int? electricalCircuitId; + final bool isEdit; - const AddElectricalCircuitEquipmentScreen({ + const AddElectricalCircuit({ super.key, required this.areaName, required this.systemId, required this.localName, required this.localId, required this.areaId, + this.electricalCircuitId, + this.isEdit = false, }); @override - _AddElectricalCircuitEquipmentScreenState createState() => - _AddElectricalCircuitEquipmentScreenState(); + _AddEquipmentScreenState createState() => _AddEquipmentScreenState(); } -class _AddElectricalCircuitEquipmentScreenState - extends State { +class _AddEquipmentScreenState extends State { EquipmentService equipmentService = EquipmentService(); + EletricalCircuitEquipmentService electricalCircuitService = + EletricalCircuitEquipmentService(); EquipmentPhotoService equipmentPhotoService = EquipmentPhotoService(); PersonalEquipmentCategoryService personalEquipmentCategoryService = PersonalEquipmentCategoryService(); GenericEquipmentCategoryService genericEquipmentCategoryService = GenericEquipmentCategoryService(); - - final _equipmentQuantityController = TextEditingController(); - final _breakerLocationController = TextEditingController(); - final _breakerStateController = TextEditingController(); - final _wireTypeController = TextEditingController(); - final _dimensionController = TextEditingController(); - String? _selectedTypeToDelete; - String? _selectCircuitType; - String? _newEquipmentTypeName; + final TextEditingController _observationsController = TextEditingController(); + final TextEditingController _conductorSpecificationController = + TextEditingController(); + final TextEditingController _gaugeController = TextEditingController(); + String? _selectedType; + String? _selectedCircuitBreaker; int? _selectedGenericEquipmentCategoryId; int? _selectedPersonalEquipmentCategoryId; bool _isPersonalEquipmentCategorySelected = false; - + String? _newEquipmentTypeName; + String? _selectedTypeToDelete; + int? equipmentId; List> genericEquipmentTypes = []; List> personalEquipmentTypes = []; Map personalEquipmentMap = {}; + EletricalCircuitResponseModel? electricalCircuitResponseModel; @override void initState() { super.initState(); _fetchEquipmentCategory(); + if (widget.isEdit && widget.electricalCircuitId != null) { + _initializeData(widget.electricalCircuitId!); + } } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _fetchEquipmentCategory(); + Future _initializeData(int electricalCircuitId) async { + try { + await _fetchElectricalCircuit(electricalCircuitId); + + if (electricalCircuitResponseModel != null) { + setState(() { + equipmentId = electricalCircuitResponseModel!.equipment; + _observationsController.text = + electricalCircuitResponseModel!.observation ?? ''; + _conductorSpecificationController.text = + electricalCircuitResponseModel!.type_wire; + _gaugeController.text = + electricalCircuitResponseModel!.size.toString(); + _selectedCircuitBreaker = + electricalCircuitResponseModel!.type_circuit_breaker; + + _fetchEquipmentDetails(electricalCircuitResponseModel!.equipment); + _fetchExistingPhotos(electricalCircuitResponseModel!.equipment); + }); + } + } catch (error) { + print('Error: $error'); + } + } + + Future _fetchElectricalCircuit(int electricalCircuitId) async { + electricalCircuitResponseModel = await electricalCircuitService + .getEletricalCircuitById(electricalCircuitId); + } + + Future _fetchEquipmentDetails(int equipmentId) async { + try { + final equipmentDetails = + await equipmentService.getEquipmentById(equipmentId); + + setState(() { + _isPersonalEquipmentCategorySelected = + equipmentDetails['personal_equipment_category'] != null; + + if (_isPersonalEquipmentCategorySelected) { + _selectedPersonalEquipmentCategoryId = + equipmentDetails['personal_equipment_category']; + _selectedType = personalEquipmentTypes.firstWhere((element) => + element['id'] == _selectedPersonalEquipmentCategoryId)['name'] + as String; + } else { + _selectedGenericEquipmentCategoryId = + equipmentDetails['generic_equipment_category']; + _selectedType = genericEquipmentTypes.firstWhere((element) => + element['id'] == _selectedGenericEquipmentCategoryId)['name'] + as String; + } + }); + } catch (e) { + print('Erro ao buscar detalhes do equipamento: $e'); + } + } + + void _fetchExistingPhotos(int equipmentId) async { + try { + List photos = + await equipmentPhotoService.getPhotosByEquipmentId(equipmentId); + + List imageList = []; + + for (var photo in photos) { + File imageFile = await photo.toFile(); + imageList.add(ImageData( + id: photo.id, + imageFile: imageFile, + description: photo.description ?? '', + )); + } + + setState(() { + _images = imageList; + }); + } catch (e) { + print('Erro ao buscar fotos existentes: $e'); + } } Future _fetchEquipmentCategory() async { @@ -110,12 +201,10 @@ class _AddElectricalCircuitEquipmentScreenState @override void dispose() { - _equipmentQuantityController.dispose(); - _breakerLocationController.dispose(); - _breakerStateController.dispose(); - _wireTypeController.dispose(); - _dimensionController.dispose(); - categoryImagesMap[widget.systemId]?.clear(); + _images.clear(); + _observationsController.dispose(); + _conductorSpecificationController.dispose(); + _gaugeController.dispose(); super.dispose(); } @@ -132,8 +221,9 @@ class _AddElectricalCircuitEquipmentScreenState } void _showImageDialog(File imageFile, {ImageData? existingImage}) { - TextEditingController descriptionController = - TextEditingController(text: existingImage?.description ?? ''); + TextEditingController descriptionController = TextEditingController( + text: existingImage?.description ?? '', + ); showDialog( context: context, builder: (BuildContext context) { @@ -161,17 +251,17 @@ class _AddElectricalCircuitEquipmentScreenState child: const Text('Salvar'), onPressed: () { setState(() { + String? description = descriptionController.text.isEmpty + ? null + : descriptionController.text; if (existingImage != null) { - existingImage.description = descriptionController.text; + existingImage.description = description ?? ''; } else { - final imageData = - ImageData(imageFile, descriptionController.text); - final systemId = widget.systemId; - if (!categoryImagesMap.containsKey(systemId)) { - categoryImagesMap[systemId] = []; - } - categoryImagesMap[systemId]!.add(imageData); - _images = categoryImagesMap[systemId]!; + final imageData = ImageData( + imageFile: imageFile, + description: description ?? '', + ); + _images.add(imageData); } }); Navigator.of(context).pop(); @@ -189,11 +279,11 @@ class _AddElectricalCircuitEquipmentScreenState context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text('Adicionar novo tipo de Circuito Elétrico'), + title: const Text('Adicionar novo tipo de equipamento'), content: TextField( controller: typeController, decoration: const InputDecoration( - hintText: 'Digite o novo tipo de Circuito Elétrico'), + hintText: 'Digite o novo tipo de equipamento'), ), actions: [ TextButton( @@ -211,7 +301,7 @@ class _AddElectricalCircuitEquipmentScreenState }); _registerPersonalEquipmentType().then((_) { setState(() { - _selectCircuitType = null; + _selectedType = null; _selectedGenericEquipmentCategoryId = null; _fetchEquipmentCategory(); }); @@ -226,6 +316,125 @@ class _AddElectricalCircuitEquipmentScreenState ); } + void _updateEquipment() async { + int? genericEquipmentCategory; + int? personalEquipmentCategory; + + if (_isPersonalEquipmentCategorySelected) { + genericEquipmentCategory = null; + personalEquipmentCategory = _selectedPersonalEquipmentCategoryId; + } else { + genericEquipmentCategory = _selectedGenericEquipmentCategoryId; + personalEquipmentCategory = null; + } + + final Map equipmentTypeUpdate = { + "generic_equipment_category": genericEquipmentCategory, + "personal_equipment_category": personalEquipmentCategory, + }; + + print('Equipment Type Update: $equipmentTypeUpdate'); + + bool typeUpdateSuccess = await equipmentService.updateEquipment( + equipmentId!, equipmentTypeUpdate); + + if (typeUpdateSuccess) { + final EletricalCircuitRequestModel electricalCircuitModel = + EletricalCircuitRequestModel( + area: widget.areaId, + system: widget.systemId, + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + size: int.tryParse(_gaugeController.text), + type_wire: _conductorSpecificationController.text, + type_circuit_breaker: _selectedCircuitBreaker ?? '', + ); + + print('Electrical Circuit Model: ${electricalCircuitModel.toJson()}'); + + bool electricalCircuitUpdateSuccess = + await electricalCircuitService.updateEletricalCircuit( + widget.electricalCircuitId!, electricalCircuitModel); + + if (electricalCircuitUpdateSuccess) { + await Future.wait(_images + .where((imageData) => imageData.toDelete) + .map((imageData) async { + await equipmentPhotoService.deletePhoto(imageData.id); + })); + + await Future.wait(_images + .where((imageData) => !imageData.toDelete) + .map((imageData) async { + if (imageData.id == -1) { + await equipmentPhotoService.createPhoto( + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } else { + await equipmentPhotoService.updatePhoto( + imageData.id, + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } + })); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Detalhes do equipamento atualizados com sucesso.'), + backgroundColor: Colors.green, + ), + ); + Navigator.pushReplacementNamed( + context, + '/electricalCircuitList', + arguments: { + 'areaName': widget.areaName, + 'systemId': widget.systemId, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + setState(() { + _selectedType = null; + _selectedPersonalEquipmentCategoryId = null; + _selectedGenericEquipmentCategoryId = null; + _images.clear(); + _observationsController.clear(); + _conductorSpecificationController.clear(); + _gaugeController.clear(); + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar os detalhes do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar o tipo do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } + Future _registerPersonalEquipmentType() async { int systemId = widget.systemId; PersonalEquipmentCategoryRequestModel personalEquipmentTypeRequestModel = @@ -238,7 +447,8 @@ class _AddElectricalCircuitEquipmentScreenState if (id != -1) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento registrado com sucesso.'), + content: + Text('Equipamento de circuito elétrico registrado com sucesso.'), backgroundColor: Colors.green, ), ); @@ -253,7 +463,8 @@ class _AddElectricalCircuitEquipmentScreenState } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao registrar o equipamento.'), + content: + Text('Falha ao registrar o equipamento de circuito elétrico.'), backgroundColor: Colors.red, ), ); @@ -281,7 +492,7 @@ class _AddElectricalCircuitEquipmentScreenState return; } - int equipmentId = personalEquipmentMap[_selectedTypeToDelete]!; + int personalCategoryId = personalEquipmentMap[_selectedTypeToDelete]!; showDialog( context: context, @@ -302,13 +513,14 @@ class _AddElectricalCircuitEquipmentScreenState onPressed: () async { Navigator.of(context).pop(); bool success = await personalEquipmentCategoryService - .deletePersonalEquipmentCategory(equipmentId); + .deletePersonalEquipmentCategory(personalCategoryId); if (success) { setState(() { personalEquipmentTypes.removeWhere( (element) => element['name'] == _selectedTypeToDelete); _selectedTypeToDelete = null; + _selectedType = null; _fetchEquipmentCategory(); }); ScaffoldMessenger.of(context).showSnackBar( @@ -334,12 +546,11 @@ class _AddElectricalCircuitEquipmentScreenState } void _showConfirmationDialog() { - if (_equipmentQuantityController.text.isEmpty || - _breakerLocationController.text.isEmpty || - _breakerStateController.text.isEmpty || - _wireTypeController.text.isEmpty || - _dimensionController.text.isEmpty || - (_selectCircuitType == null && _newEquipmentTypeName == null)) { + if (_selectedType == null && + _newEquipmentTypeName == null && + _selectedCircuitBreaker == null && + _conductorSpecificationController.text.isEmpty && + _gaugeController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Por favor, preencha todos os campos.'), @@ -358,32 +569,30 @@ class _AddElectricalCircuitEquipmentScreenState children: [ const Text('Tipo:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_selectCircuitType ?? _newEquipmentTypeName ?? ''), + Text(_selectedType ?? _newEquipmentTypeName ?? ''), const SizedBox(height: 10), - const Text('Quantidade:', + const Text('Disjuntor (tipo):', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentQuantityController.text), + Text(_selectedCircuitBreaker ?? ''), const SizedBox(height: 10), - const Text('Disjuntor(Local):', + const Text('Especificação do Condutor:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_breakerLocationController.text), + Text(_conductorSpecificationController.text), const SizedBox(height: 10), - const Text('Disjuntor(Estado):', + const Text('Bitola:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_breakerStateController.text), + Text(_gaugeController.text), const SizedBox(height: 10), - const Text('Tipo de Fio:', + const Text('Observações:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_wireTypeController.text), - const SizedBox(height: 10), - const Text('Dimensão:', - style: TextStyle(fontWeight: FontWeight.bold)), - Text(_dimensionController.text), + Text(_observationsController.text), const SizedBox(height: 10), const Text('Imagens:', style: TextStyle(fontWeight: FontWeight.bold)), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Padding( padding: const EdgeInsets.all(4.0), child: GestureDetector( @@ -391,8 +600,12 @@ class _AddElectricalCircuitEquipmentScreenState existingImage: imageData), child: Column( children: [ - Image.file(imageData.imageFile, - width: 100, height: 100, fit: BoxFit.cover), + Image.file( + imageData.imageFile, + width: 100, + height: 100, + fit: BoxFit.cover, + ), Text(imageData.description), ], ), @@ -411,9 +624,16 @@ class _AddElectricalCircuitEquipmentScreenState }, ), TextButton( - child: const Text('Adicionar'), + child: widget.isEdit + ? const Text('Atualizar') + : const Text('Adicionar'), onPressed: () { - _registerEquipment(); + if (widget.isEdit) { + _updateEquipment(); + } else { + _registerEquipment(); + } + Navigator.of(context).pop(); }, ), ], @@ -438,9 +658,12 @@ class _AddElectricalCircuitEquipmentScreenState EletricalCircuitRequestModel( area: widget.areaId, system: widget.systemId, - quantity: null, - size: null, - isolamento: '', + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + size: int.tryParse(_gaugeController.text), + type_wire: _conductorSpecificationController.text, + type_circuit_breaker: _selectedCircuitBreaker ?? '', ); final EletricalCircuitEquipmentRequestModel @@ -451,16 +674,20 @@ class _AddElectricalCircuitEquipmentScreenState eletricalCircuitRequestModel: electricalCircuitModel, ); - int? equipmentId = await equipmentService + int? id = await equipmentService .createElectricalCircuit(electricalCircuitEquipmentDetail); + setState(() { + equipmentId = id; + }); - if (equipmentId != null) { + if (equipmentId != null && equipmentId != 0) { await Future.wait(_images.map((imageData) async { await equipmentPhotoService.createPhoto( EquipmentPhotoRequestModel( photo: imageData.imageFile, - description: imageData.description, - equipment: equipmentId, + description: + imageData.description.isEmpty ? null : imageData.description, + equipment: equipmentId!, ), ); })); @@ -483,15 +710,13 @@ class _AddElectricalCircuitEquipmentScreenState }, ); setState(() { - _equipmentQuantityController.clear(); - _breakerLocationController.clear(); - _breakerStateController.clear(); - _wireTypeController.clear(); - _dimensionController.clear(); - _selectCircuitType = null; + _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); + _conductorSpecificationController.clear(); + _gaugeController.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -503,6 +728,13 @@ class _AddElectricalCircuitEquipmentScreenState } } + Future _deletePhoto(int photoId) async { + setState(() { + _images.firstWhere((imageData) => imageData.id == photoId).toDelete = + true; + }); + } + @override Widget build(BuildContext context) { List> combinedTypes = [ @@ -517,6 +749,15 @@ class _AddElectricalCircuitEquipmentScreenState leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () { + setState(() { + _selectedType = null; + _selectedPersonalEquipmentCategoryId = null; + _selectedGenericEquipmentCategoryId = null; + _images.clear(); + _observationsController.clear(); + _conductorSpecificationController.clear(); + _gaugeController.clear(); + }); Navigator.pushReplacementNamed( context, '/electricalCircuitList', @@ -543,12 +784,16 @@ class _AddElectricalCircuitEquipmentScreenState borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)), ), - child: const Center( - child: Text('Adicionar Circuito Elétrico', - style: TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: Colors.white)), + child: Center( + child: Text( + widget.electricalCircuitId == null + ? 'Adicionar Equipamento' + : 'Editar Equipamento', + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: Colors.white), + ), ), ), Padding( @@ -574,12 +819,12 @@ class _AddElectricalCircuitEquipmentScreenState } ] + combinedTypes, - value: _selectCircuitType, + value: _selectedType, onChanged: (newValue) { if (newValue != 'Selecione o tipo de Circuito Elétrico') { setState(() { - _selectCircuitType = newValue; + _selectedType = newValue; Map selected = combinedTypes.firstWhere((element) => element['name'] == newValue); @@ -631,60 +876,44 @@ class _AddElectricalCircuitEquipmentScreenState ), ], ), - const SizedBox(height: 8), TextButton( onPressed: () { setState(() { - _selectCircuitType = null; + _selectedType = null; }); }, child: const Text('Limpar seleção'), ), - const SizedBox(height: 30), - const Text('Quantidade', + const Text('Disjuntor (tipo)', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), - Container( - decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: BorderRadius.circular(10), + DropdownButton( + hint: const Text( + 'Escolha um tipo', + style: TextStyle(color: Colors.grey), ), - child: TextField( - controller: _equipmentQuantityController, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], - decoration: const InputDecoration( - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10, vertical: 15), + value: _selectedCircuitBreaker, + isExpanded: true, + underline: Container(), + onChanged: (newValue) { + setState(() { + _selectedCircuitBreaker = newValue; + }); + }, + items: const [ + DropdownMenuItem( + value: 'Magnético', + child: Text('Magnético'), ), - ), - ), - const SizedBox(height: 30), - const Text('Disjuntor(Local)', - style: - TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), - const SizedBox(height: 8), - Container( - decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: BorderRadius.circular(10), - ), - child: TextField( - controller: _breakerLocationController, - keyboardType: TextInputType.text, - decoration: const InputDecoration( - border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10, vertical: 15), + DropdownMenuItem( + value: 'DR', + child: Text('DR'), ), - ), + ], ), - const SizedBox(height: 30), - const Text('Disjuntor(Estado)', + const SizedBox(height: 15), + const Text('Especificação do Condutor', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -694,17 +923,17 @@ class _AddElectricalCircuitEquipmentScreenState borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _breakerStateController, - keyboardType: TextInputType.text, + controller: _conductorSpecificationController, decoration: const InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: 'Digite a especificação do condutor', ), ), ), - const SizedBox(height: 30), - const Text('Tipo de Fio', + const SizedBox(height: 15), + const Text('Bitola', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -714,17 +943,21 @@ class _AddElectricalCircuitEquipmentScreenState borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _wireTypeController, - keyboardType: TextInputType.text, + controller: _gaugeController, + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], decoration: const InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: 'Digite a bitola', ), ), ), - const SizedBox(height: 30), - const Text('Dimensão', + const SizedBox(height: 15), + const Text('Observações', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -734,13 +967,30 @@ class _AddElectricalCircuitEquipmentScreenState borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _dimensionController, - keyboardType: TextInputType.text, + controller: _observationsController, + maxLines: 3, + maxLength: 500, decoration: const InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: + 'Digite suas observações aqui (máx 500 caracteres)', ), + buildCounter: (BuildContext context, + {required int currentLength, + required bool isFocused, + required int? maxLength}) { + return Text( + '$currentLength / $maxLength', + style: TextStyle( + fontSize: 12, + color: currentLength > maxLength! + ? Colors.red + : Colors.grey, + ), + ); + }, ), ), const SizedBox(height: 15), @@ -749,7 +999,9 @@ class _AddElectricalCircuitEquipmentScreenState onPressed: _pickImage, ), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Stack( alignment: Alignment.topRight, children: [ @@ -770,10 +1022,7 @@ class _AddElectricalCircuitEquipmentScreenState icon: const Icon(Icons.remove_circle, color: AppColors.warn), onPressed: () { - setState(() { - _images.removeWhere( - (element) => element.id == imageData.id); - }); + _deletePhoto(imageData.id); }, ), ], @@ -795,9 +1044,11 @@ class _AddElectricalCircuitEquipmentScreenState borderRadius: BorderRadius.circular(10), ))), onPressed: _showConfirmationDialog, - child: const Text( - 'ADICIONAR EQUIPAMENTO', - style: TextStyle( + child: Text( + widget.isEdit + ? 'ATUALIZAR EQUIPAMENTO' + : 'ADICIONAR EQUIPAMENTO', + style: const TextStyle( fontSize: 17, fontWeight: FontWeight.bold), ), ), @@ -816,12 +1067,12 @@ class _AddElectricalCircuitEquipmentScreenState context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text('Excluir tipo de Circuito Elétrico'), + title: const Text('Excluir tipo de equipamento'), content: Column( mainAxisSize: MainAxisSize.min, children: [ const Text( - 'Selecione um tipo de Circuito Elétrico para excluir:', + 'Selecione um equipamento para excluir:', textAlign: TextAlign.center, ), StatefulBuilder( @@ -884,20 +1135,23 @@ class _AddElectricalCircuitEquipmentScreenState ), padding: const EdgeInsets.symmetric(horizontal: 10), child: DropdownButton( - hint: Text(items.first['name'] as String, + hint: Text( + items.isNotEmpty + ? items.first['name'] as String + : 'Selecione uma opção', style: const TextStyle(color: Colors.grey)), - value: value, + value: items.any((item) => item['name'] == value) ? value : null, isExpanded: true, underline: Container(), onChanged: enabled ? onChanged : null, - items: items.map>((Map value) { + items: items.map>((Map item) { return DropdownMenuItem( - value: value['name'] as String, - enabled: value['name'] != 'Selecione o tipo de Circuito Elétrico', + value: item['name'] as String, + enabled: item['name'] != 'Escolha um tipo', child: Text( - value['name'] as String, + item['name'] as String, style: TextStyle( - color: value['name'] == 'Selecione o tipo de Circuito Elétrico' + color: item['name'] == 'Escolha um tipo' ? Colors.grey : Colors.black, ), diff --git a/frontend/sige_ie/lib/equipments/feature/electrical_circuit/electrical_circuit_list.dart b/frontend/sige_ie/lib/equipments/feature/electrical_circuit/electrical_circuit_list.dart index 0682ad53..6f933388 100644 --- a/frontend/sige_ie/lib/equipments/feature/electrical_circuit/electrical_circuit_list.dart +++ b/frontend/sige_ie/lib/equipments/feature/electrical_circuit/electrical_circuit_list.dart @@ -12,195 +12,245 @@ class ListCircuitEquipment extends StatefulWidget { final int areaId; const ListCircuitEquipment({ - Key? key, + super.key, required this.areaName, required this.systemId, required this.localName, required this.localId, required this.areaId, - }) : super(key: key); + }); @override _ListCircuitEquipmentState createState() => _ListCircuitEquipmentState(); } class _ListCircuitEquipmentState extends State { - List equipmentList = []; - bool isLoading = true; - final EletricalCircuitEquipmentService _service = + late Future> + _electricalCircuitList; + final EletricalCircuitEquipmentService _electricalCircuitService = EletricalCircuitEquipmentService(); + final GlobalKey _scaffoldMessengerKey = + GlobalKey(); + @override void initState() { super.initState(); - fetchEquipmentList(); + _electricalCircuitList = + _electricalCircuitService.getEletricalCircuitListByArea(widget.areaId); } - Future fetchEquipmentList() async { - try { - final List equipmentList = - await _service.getEletricalCircuitListByArea(widget.areaId); - if (mounted) { - setState(() { - /* this.equipmentList = equipmentList; */ - isLoading = false; - }); - } - } catch (e) { - print('Error fetching equipment list: $e'); - if (mounted) { - setState(() { - isLoading = false; - }); - } - } + void navigateToAddElectricalCircuit(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddElectricalCircuit( + areaName: widget.areaName, + systemId: widget.systemId, + localName: widget.localName, + localId: widget.localId, + areaId: widget.areaId, + electricalCircuitId: null, + ), + ), + ); } - void navigateToAddEquipment(BuildContext context) { + void _editElectricalCircuit(BuildContext context, int electricalCircuitId) { Navigator.push( context, MaterialPageRoute( - builder: (context) => AddElectricalCircuitEquipmentScreen( + builder: (context) => AddElectricalCircuit( areaName: widget.areaName, systemId: widget.systemId, localName: widget.localName, localId: widget.localId, areaId: widget.areaId, + electricalCircuitId: electricalCircuitId, + isEdit: true, ), ), ); } - void _editEquipment(BuildContext context, String equipment) { - // Implement the logic to edit the equipment + Future _deleteElectricalCircuit( + BuildContext context, int electricalCircuitId) async { + try { + await _electricalCircuitService + .deleteEletricalCircuit(electricalCircuitId); + setState(() { + _electricalCircuitList = _electricalCircuitService + .getEletricalCircuitListByArea(widget.areaId); + }); + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Equipamento deletado com sucesso'), + backgroundColor: Colors.green, + ), + ); + } catch (e) { + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Falha ao deletar o equipamento'), + backgroundColor: Colors.red, + ), + ); + } } - void _deleteEquipment(BuildContext context, String equipment) { - // Implement the logic to delete the equipment + void _confirmDelete(BuildContext context, int electricalCircuitId) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirmar Exclusão'), + content: const Text( + 'Você tem certeza que deseja excluir este equipamento?'), + actions: [ + TextButton( + child: const Text('Cancelar'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Excluir'), + onPressed: () { + Navigator.of(context).pop(); + _deleteElectricalCircuit(context, electricalCircuitId); + }, + ), + ], + ); + }, + ); } @override Widget build(BuildContext context) { String systemTitle = 'Circuito Elétrico'; - return Scaffold( - appBar: AppBar( - backgroundColor: AppColors.sigeIeBlue, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pushReplacementNamed( - context, - '/systemLocation', - arguments: { - 'areaName': widget.areaName, - 'localName': widget.localName, - 'localId': widget.localId, - 'areaId': widget.areaId, - }, - ); - }, + return ScaffoldMessenger( + key: _scaffoldMessengerKey, + child: Scaffold( + appBar: AppBar( + backgroundColor: AppColors.sigeIeBlue, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () { + Navigator.pushReplacementNamed( + context, + '/systemLocation', + arguments: { + 'areaName': widget.areaName, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + }, + ), ), - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), - decoration: const BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: - BorderRadius.vertical(bottom: Radius.circular(20)), - ), - child: Center( - child: Text( - '${widget.areaName} - $systemTitle', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: AppColors.lightText, + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), + decoration: const BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: + BorderRadius.vertical(bottom: Radius.circular(20)), + ), + child: Center( + child: Text( + '${widget.areaName} - $systemTitle', + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: AppColors.lightText, + ), ), ), ), - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : equipmentList.isNotEmpty - ? Column( - children: equipmentList.map((equipment) { - return Container( - margin: - const EdgeInsets.symmetric(vertical: 5), - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - children: [ - Expanded( - child: Padding( - padding: - const EdgeInsets.only(left: 10), - child: Text( - equipment, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ), - ), - IconButton( - icon: const Icon(Icons.edit, - color: Colors.blue), - onPressed: () => _editEquipment( - context, equipment), - ), - IconButton( - icon: const Icon(Icons.delete, - color: Colors.red), - onPressed: () => _deleteEquipment( - context, equipment), - ), - ], - ), - ), - ); - }).toList(), - ) - : const Center( - child: Text( - 'Você ainda não tem equipamentos', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black54, + const SizedBox(height: 20), + FutureBuilder>( + future: _electricalCircuitList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Erro: ${snapshot.error}')); + } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var electricalCircuit = snapshot.data![index]; + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + title: Text( + electricalCircuit.equipmentCategory, + style: const TextStyle( + color: AppColors.lightText, + fontWeight: FontWeight.bold), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit, + color: Colors.blue), + onPressed: () => _editElectricalCircuit( + context, electricalCircuit.id), + ), + IconButton( + icon: const Icon(Icons.delete, + color: Colors.red), + onPressed: () => _confirmDelete( + context, electricalCircuit.id), ), - ), + ], ), - const SizedBox(height: 40), - ], + ), + ); + }, + ); + } else { + return const Padding( + padding: EdgeInsets.all(10.0), + child: Center( + child: Text( + 'Nenhum equipamento encontrado.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black54), + ), + ), + ); + } + }, ), - ), - ], + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => navigateToAddElectricalCircuit(context), + backgroundColor: AppColors.sigeIeYellow, + child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), - ), - floatingActionButton: FloatingActionButton( - onPressed: () => navigateToAddEquipment(context), - backgroundColor: AppColors.sigeIeYellow, - child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), ); } From 58552f557b5554dc8b46e27ad4381cc5bc2bf958 Mon Sep 17 00:00:00 2001 From: EngDann Date: Thu, 15 Aug 2024 16:56:01 -0300 Subject: [PATCH 06/15] =?UTF-8?q?Atmospheric=20Discharges=20completo,=20ma?= =?UTF-8?q?s=20n=C3=A3o=20integrado=20Co-authored-by:=20Ramires=20rocha=20?= =?UTF-8?q??= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kauan Jose Co-authored-by: OscarDeBrito Co-authored-by: Pedro Lucas --- api/sigeie/settings.py | 2 +- .../sige_ie/lib/core/data/universalURL.dart | 2 +- .../atmospheric_request_model.dart | 13 +- .../atmospheric_response_by_area_model.dart | 27 +- .../atmospheric_response_model.dart | 27 +- .../data/atmospheric/atmospheric_service.dart | 41 +- .../add_atmospheric_discharges_equipment.dart | 427 +++++++++++++++--- .../atmospheric_discharges_list.dart | 240 ++++++---- 8 files changed, 556 insertions(+), 223 deletions(-) diff --git a/api/sigeie/settings.py b/api/sigeie/settings.py index 8bd1b060..e5c75f3e 100644 --- a/api/sigeie/settings.py +++ b/api/sigeie/settings.py @@ -6,7 +6,7 @@ DEBUG = True -ALLOWED_HOSTS = ['127.0.0.1', '10.0.2.2', '192.168.1.142'] +ALLOWED_HOSTS = ['127.0.0.1', '10.0.2.2', '192.168.1.142', '192.168.133.230'] INSTALLED_APPS = [ 'django.contrib.admin', diff --git a/frontend/sige_ie/lib/core/data/universalURL.dart b/frontend/sige_ie/lib/core/data/universalURL.dart index bfc10c71..f6f9dc0c 100644 --- a/frontend/sige_ie/lib/core/data/universalURL.dart +++ b/frontend/sige_ie/lib/core/data/universalURL.dart @@ -1,3 +1,3 @@ const String urlUniversal = - 'http://192.168.1.142:8000'; + 'http://192.168.133.230:8000'; /* local host: http://10.0.2.2:8000 */ \ No newline at end of file diff --git a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_request_model.dart b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_request_model.dart index 948bb642..d4f7a52f 100644 --- a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_request_model.dart @@ -2,11 +2,20 @@ class AtmosphericRequestModel { int? area; int? system; int? quantity; + String? observation; AtmosphericRequestModel( - {required this.area, required this.system, this.quantity}); + {required this.area, + required this.system, + required this.quantity, + required this.observation}); Map toJson() { - return {'area': area, 'system': system, 'quantity': quantity}; + return { + 'area': area, + 'system': system, + 'quantity': quantity, + 'observation': observation + }; } } diff --git a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_by_area_model.dart index 2eef833f..32fc94ef 100644 --- a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_by_area_model.dart @@ -4,23 +4,24 @@ class AtmosphericEquipmentResponseByAreaModel { String equipmentCategory; int system; int quantity; + String? observation; - AtmosphericEquipmentResponseByAreaModel({ - required this.id, - required this.area, - required this.equipmentCategory, - required this.system, - required this.quantity, - }); + AtmosphericEquipmentResponseByAreaModel( + {required this.id, + required this.area, + required this.equipmentCategory, + required this.system, + required this.quantity, + required this.observation}); factory AtmosphericEquipmentResponseByAreaModel.fromJson( Map json) { return AtmosphericEquipmentResponseByAreaModel( - id: json['id'], - area: json['area'], - equipmentCategory: json['equipment_category'], - system: json['system'], - quantity: json['quantity'], - ); + id: json['id'], + area: json['area'], + equipmentCategory: json['equipment_category'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_model.dart b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_model.dart index 43f56819..bcc1ee16 100644 --- a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_response_model.dart @@ -4,22 +4,23 @@ class AtmosphericResponseModel { int equipment; int system; int quantity; + String? observation; - AtmosphericResponseModel({ - required this.id, - required this.area, - required this.equipment, - required this.system, - required this.quantity, - }); + AtmosphericResponseModel( + {required this.id, + required this.area, + required this.equipment, + required this.system, + required this.quantity, + required this.observation}); factory AtmosphericResponseModel.fromJson(Map json) { return AtmosphericResponseModel( - id: json['id'], - area: json['area'], - equipment: json['equipment'], - system: json['system'], - quantity: json['quantity'], - ); + id: json['id'], + area: json['area'], + equipment: json['equipment'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart index 78cff314..1a6a7140 100644 --- a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart +++ b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart @@ -18,7 +18,7 @@ class AtmosphericEquipmentService { Future> getAtmosphericListByArea(int areaId) async { - var url = Uri.parse('${baseUrl}structured-cabling/by-area/$areaId/'); + var url = Uri.parse('${baseUrl}atmospheric-discharges/by-area/$areaId/'); try { var response = await client.get(url); @@ -30,37 +30,38 @@ class AtmosphericEquipmentService { .toList(); } else { _logger.info( - 'Failed to load structured cabling equipment with status code: ${response.statusCode}'); + 'Failed to load atmospheric discharges equipment with status code: ${response.statusCode}'); _logger.info('Response body: ${response.body}'); - throw Exception('Failed to load structured cabling equipment'); + throw Exception('Failed to load atmospheric discharges equipment'); } } catch (e) { - _logger.info('Error during get structured cabling equipment list: $e'); - throw Exception('Failed to load structured cabling equipment'); + _logger + .info('Error during get atmospheric discharges equipment list: $e'); + throw Exception('Failed to load atmospheric discharges equipment'); } } Future deleteAtmospheric(int AtmosphericId) async { - var url = Uri.parse('${baseUrl}structured-cabling/$AtmosphericId/'); + var url = Uri.parse('${baseUrl}atmospheric-discharges/$AtmosphericId/'); try { var response = await client.delete(url); if (response.statusCode == 204) { _logger.info( - 'Successfully deleted structured cabling equipment with ID: $AtmosphericId'); + 'Successfully deleted atmospheric discharges equipment with ID: $AtmosphericId'); } else { _logger.info( - 'Failed to delete structured cabling equipment with status code: ${response.statusCode}'); + 'Failed to delete atmospheric discharges equipment with status code: ${response.statusCode}'); _logger.info('Response body: ${response.body}'); - throw Exception('Failed to delete structured cabling equipment'); + throw Exception('Failed to delete atmospheric discharges equipment'); } } catch (e) { - _logger.info('Error during delete structured cabling equipment: $e'); - throw Exception('Failed to delete structured cabling equipment'); + _logger.info('Error during delete atmospheric discharges equipment: $e'); + throw Exception('Failed to delete atmospheric discharges equipment'); } } Future getAtmosphericById(int AtmosphericId) async { - var url = Uri.parse('${baseUrl}structured-cabling/$AtmosphericId/'); + var url = Uri.parse('${baseUrl}atmospheric-discharges/$AtmosphericId/'); try { var response = await client.get(url); if (response.statusCode == 200) { @@ -68,18 +69,18 @@ class AtmosphericEquipmentService { return AtmosphericResponseModel.fromJson(jsonResponse); } else { _logger.info( - 'Failed to load structured cabling with status code: ${response.statusCode}'); - throw Exception('Failed to load structured cabling'); + 'Failed to load atmospheric discharges with status code: ${response.statusCode}'); + throw Exception('Failed to load atmospheric discharges'); } } catch (e) { - _logger.info('Error during get structured cabling: $e'); - throw Exception('Failed to load structured cabling'); + _logger.info('Error during get atmospheric discharges: $e'); + throw Exception('Failed to load atmospheric discharges'); } } Future updateAtmospheric(int AtmosphericId, AtmosphericRequestModel AtmosphericIdRequestModel) async { - var url = Uri.parse('${baseUrl}structured-cabling/$AtmosphericId/'); + var url = Uri.parse('${baseUrl}atmospheric-discharges/$AtmosphericId/'); try { var response = await client.put( @@ -90,15 +91,15 @@ class AtmosphericEquipmentService { if (response.statusCode == 200) { _logger.info( - 'Successfully updated structured cabling equipment with ID: $AtmosphericId'); + 'Successfully updated atmospheric discharges equipment with ID: $AtmosphericId'); return true; } else { _logger.info( - 'Failed to update structured cabling equipment with status code: ${response.statusCode}'); + 'Failed to update atmospheric discharges equipment with status code: ${response.statusCode}'); return false; } } catch (e) { - _logger.info('Error during update structured cabling equipment: $e'); + _logger.info('Error during update atmospheric discharges equipment: $e'); return false; } } diff --git a/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/add_atmospheric_discharges_equipment.dart b/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/add_atmospheric_discharges_equipment.dart index a765d372..917c7223 100644 --- a/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/add_atmospheric_discharges_equipment.dart +++ b/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/add_atmospheric_discharges_equipment.dart @@ -1,13 +1,15 @@ import 'dart:io'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:sige_ie/config/app_styles.dart'; import 'package:sige_ie/equipments/data/atmospheric/atmospheric_equipment_request_model.dart'; import 'package:sige_ie/equipments/data/atmospheric/atmospheric_request_model.dart'; +import 'package:sige_ie/equipments/data/atmospheric/atmospheric_response_model.dart'; +import 'package:sige_ie/equipments/data/atmospheric/atmospheric_service.dart'; import 'package:sige_ie/equipments/data/equipment_service.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_request_model.dart'; +import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_response_model.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_service.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_response_model.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_service.dart'; @@ -18,63 +20,152 @@ class ImageData { int id; File imageFile; String description; + bool toDelete; - ImageData(this.imageFile, this.description) : id = Random().nextInt(1000000); + ImageData({ + int? id, + required this.imageFile, + required this.description, + this.toDelete = false, + }) : id = id ?? -1; } List _images = []; -Map> categoryImagesMap = {}; -class AddAtmosphericEquipmentScreen extends StatefulWidget { +class AddAtmosphericDischarge extends StatefulWidget { final String areaName; final String localName; final int localId; final int systemId; final int areaId; + final int? dischargeId; + final bool isEdit; - const AddAtmosphericEquipmentScreen({ + const AddAtmosphericDischarge({ super.key, required this.areaName, required this.systemId, required this.localName, required this.localId, required this.areaId, + this.dischargeId, + this.isEdit = false, }); @override - _AddEquipmentScreenState createState() => _AddEquipmentScreenState(); + _AddAtmosphericDischargeScreenState createState() => + _AddAtmosphericDischargeScreenState(); } -class _AddEquipmentScreenState extends State { +class _AddAtmosphericDischargeScreenState + extends State { EquipmentService equipmentService = EquipmentService(); + AtmosphericEquipmentService atmosphericService = + AtmosphericEquipmentService(); EquipmentPhotoService equipmentPhotoService = EquipmentPhotoService(); PersonalEquipmentCategoryService personalEquipmentCategoryService = PersonalEquipmentCategoryService(); GenericEquipmentCategoryService genericEquipmentCategoryService = GenericEquipmentCategoryService(); - final _equipmentQuantityController = TextEditingController(); + final TextEditingController _quantity = TextEditingController(); + final TextEditingController _observationsController = TextEditingController(); String? _selectedType; - String? _selectedTypeToDelete; - String? _newEquipmentTypeName; int? _selectedGenericEquipmentCategoryId; int? _selectedPersonalEquipmentCategoryId; bool _isPersonalEquipmentCategorySelected = false; - + String? _newEquipmentTypeName; + String? _selectedTypeToDelete; + int? equipmentId; List> genericEquipmentTypes = []; List> personalEquipmentTypes = []; Map personalEquipmentMap = {}; + AtmosphericResponseModel? atmosphericResponseModel; @override void initState() { super.initState(); _fetchEquipmentCategory(); + if (widget.isEdit && widget.dischargeId != null) { + _initializeData(widget.dischargeId!); + } } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _fetchEquipmentCategory(); + Future _initializeData(int dischargeId) async { + try { + await _fetchAtmosphericEquipment(dischargeId); + + if (atmosphericResponseModel != null) { + setState(() { + equipmentId = atmosphericResponseModel!.equipment; + _quantity.text = atmosphericResponseModel!.quantity.toString(); + _observationsController.text = + atmosphericResponseModel!.observation ?? ''; + }); + + _fetchEquipmentDetails(atmosphericResponseModel!.equipment); + _fetchExistingPhotos(atmosphericResponseModel!.equipment); + } + } catch (error) { + print('Error: $error'); + } + } + + Future _fetchAtmosphericEquipment(int dischargeId) async { + atmosphericResponseModel = + await atmosphericService.getAtmosphericById(dischargeId); + } + + Future _fetchEquipmentDetails(int equipmentId) async { + try { + final equipmentDetails = + await equipmentService.getEquipmentById(equipmentId); + + setState(() { + _isPersonalEquipmentCategorySelected = + equipmentDetails['personal_equipment_category'] != null; + + if (_isPersonalEquipmentCategorySelected) { + _selectedPersonalEquipmentCategoryId = + equipmentDetails['personal_equipment_category']; + _selectedType = personalEquipmentTypes.firstWhere((element) => + element['id'] == _selectedPersonalEquipmentCategoryId)['name'] + as String; + } else { + _selectedGenericEquipmentCategoryId = + equipmentDetails['generic_equipment_category']; + _selectedType = genericEquipmentTypes.firstWhere((element) => + element['id'] == _selectedGenericEquipmentCategoryId)['name'] + as String; + } + }); + } catch (e) { + print('Erro ao buscar detalhes do equipamento: $e'); + } + } + + void _fetchExistingPhotos(int equipmentId) async { + try { + List photos = + await equipmentPhotoService.getPhotosByEquipmentId(equipmentId); + + List imageList = []; + + for (var photo in photos) { + File imageFile = await photo.toFile(); + imageList.add(ImageData( + id: photo.id, + imageFile: imageFile, + description: photo.description ?? '', + )); + } + + setState(() { + _images = imageList; + }); + } catch (e) { + print('Erro ao buscar fotos existentes: $e'); + } } Future _fetchEquipmentCategory() async { @@ -104,8 +195,9 @@ class _AddEquipmentScreenState extends State { @override void dispose() { - _equipmentQuantityController.dispose(); - categoryImagesMap[widget.systemId]?.clear(); + _quantity.dispose(); + _images.clear(); + _observationsController.dispose(); super.dispose(); } @@ -151,17 +243,17 @@ class _AddEquipmentScreenState extends State { child: const Text('Salvar'), onPressed: () { setState(() { + String? description = descriptionController.text.isEmpty + ? null + : descriptionController.text; if (existingImage != null) { - existingImage.description = descriptionController.text; + existingImage.description = description ?? ''; } else { - final imageData = - ImageData(imageFile, descriptionController.text); - final systemId = widget.systemId; - if (!categoryImagesMap.containsKey(systemId)) { - categoryImagesMap[systemId] = []; - } - categoryImagesMap[systemId]!.add(imageData); - _images = categoryImagesMap[systemId]!; + final imageData = ImageData( + imageFile: imageFile, + description: description ?? '', + ); + _images.add(imageData); } }); Navigator.of(context).pop(); @@ -216,6 +308,115 @@ class _AddEquipmentScreenState extends State { ); } + void _updateEquipment() async { + int? genericEquipmentCategory; + int? personalEquipmentCategory; + + if (_isPersonalEquipmentCategorySelected) { + genericEquipmentCategory = null; + personalEquipmentCategory = _selectedPersonalEquipmentCategoryId; + } else { + genericEquipmentCategory = _selectedGenericEquipmentCategoryId; + personalEquipmentCategory = null; + } + + final Map equipmentTypeUpdate = { + "generic_equipment_category": genericEquipmentCategory, + "personal_equipment_category": personalEquipmentCategory, + }; + + bool typeUpdateSuccess = await equipmentService.updateEquipment( + equipmentId!, equipmentTypeUpdate); + + if (typeUpdateSuccess) { + final AtmosphericRequestModel atmosphericModel = AtmosphericRequestModel( + area: widget.areaId, + system: widget.systemId, + quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + ); + + bool atmosphericUpdateSuccess = await atmosphericService + .updateAtmospheric(widget.dischargeId!, atmosphericModel); + + if (atmosphericUpdateSuccess) { + await Future.wait(_images + .where((imageData) => imageData.toDelete) + .map((imageData) async { + await equipmentPhotoService.deletePhoto(imageData.id); + })); + + await Future.wait(_images + .where((imageData) => !imageData.toDelete) + .map((imageData) async { + if (imageData.id == -1) { + await equipmentPhotoService.createPhoto( + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } else { + await equipmentPhotoService.updatePhoto( + imageData.id, + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } + })); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Detalhes do equipamento atualizados com sucesso.'), + backgroundColor: Colors.green, + ), + ); + Navigator.pushReplacementNamed( + context, + '/listatmosphericEquipment', + arguments: { + 'areaName': widget.areaName, + 'systemId': widget.systemId, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + setState(() { + _quantity.clear(); + _selectedType = null; + _selectedPersonalEquipmentCategoryId = null; + _selectedGenericEquipmentCategoryId = null; + _images.clear(); + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar os detalhes do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar o tipo do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } + Future _registerPersonalEquipmentType() async { int systemId = widget.systemId; PersonalEquipmentCategoryRequestModel personalEquipmentTypeRequestModel = @@ -228,7 +429,7 @@ class _AddEquipmentScreenState extends State { if (id != -1) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento registrado com sucesso.'), + content: Text('Tipo de equipamento registrado com sucesso.'), backgroundColor: Colors.green, ), ); @@ -243,7 +444,7 @@ class _AddEquipmentScreenState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao registrar o equipamento.'), + content: Text('Falha ao registrar o tipo de equipamento.'), backgroundColor: Colors.red, ), ); @@ -278,8 +479,8 @@ class _AddEquipmentScreenState extends State { builder: (BuildContext context) { return AlertDialog( title: const Text('Confirmar Exclusão'), - content: - const Text('Tem certeza de que deseja excluir este equipamento?'), + content: const Text( + 'Tem certeza de que deseja excluir este tipo de equipamento?'), actions: [ TextButton( child: const Text('Cancelar'), @@ -299,6 +500,7 @@ class _AddEquipmentScreenState extends State { personalEquipmentTypes.removeWhere( (element) => element['name'] == _selectedTypeToDelete); _selectedTypeToDelete = null; + _selectedType = null; _fetchEquipmentCategory(); }); ScaffoldMessenger.of(context).showSnackBar( @@ -324,7 +526,7 @@ class _AddEquipmentScreenState extends State { } void _showConfirmationDialog() { - if (_equipmentQuantityController.text.isEmpty || + if (_quantity.text.isEmpty || (_selectedType == null && _newEquipmentTypeName == null)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -348,12 +550,18 @@ class _AddEquipmentScreenState extends State { const SizedBox(height: 10), const Text('Quantidade:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentQuantityController.text), + Text(_quantity.text), + const SizedBox(height: 10), + const Text('Observações:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_observationsController.text), const SizedBox(height: 10), const Text('Imagens:', style: TextStyle(fontWeight: FontWeight.bold)), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Padding( padding: const EdgeInsets.all(4.0), child: GestureDetector( @@ -385,9 +593,16 @@ class _AddEquipmentScreenState extends State { }, ), TextButton( - child: const Text('Adicionar'), + child: widget.isEdit + ? const Text('Atualizar') + : const Text('Adicionar'), onPressed: () { - _registerEquipment(); + if (widget.isEdit) { + _updateEquipment(); + } else { + _registerEquipment(); + } + Navigator.of(context).pop(); }, ), ], @@ -411,6 +626,10 @@ class _AddEquipmentScreenState extends State { final AtmosphericRequestModel atmosphericModel = AtmosphericRequestModel( area: widget.areaId, system: widget.systemId, + quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); final AtmosphericEquipmentRequestModel atmosphericEquipmentDetail = @@ -420,16 +639,20 @@ class _AddEquipmentScreenState extends State { atmosphericRequestModel: atmosphericModel, ); - int? equipmentId = + int? id = await equipmentService.createAtmospheric(atmosphericEquipmentDetail); + setState(() { + equipmentId = id; + }); - if (equipmentId != null) { + if (equipmentId != null && equipmentId != 0) { await Future.wait(_images.map((imageData) async { await equipmentPhotoService.createPhoto( EquipmentPhotoRequestModel( photo: imageData.imageFile, - description: imageData.description, - equipment: equipmentId, + description: + imageData.description.isEmpty ? null : imageData.description, + equipment: equipmentId!, ), ); })); @@ -452,11 +675,12 @@ class _AddEquipmentScreenState extends State { }, ); setState(() { - _equipmentQuantityController.clear(); + _quantity.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -468,6 +692,13 @@ class _AddEquipmentScreenState extends State { } } + Future _deletePhoto(int photoId) async { + setState(() { + _images.firstWhere((imageData) => imageData.id == photoId).toDelete = + true; + }); + } + @override Widget build(BuildContext context) { List> combinedTypes = [ @@ -483,11 +714,12 @@ class _AddEquipmentScreenState extends State { icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () { setState(() { - _equipmentQuantityController.clear(); + _quantity.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); Navigator.pushReplacementNamed( context, @@ -515,12 +747,17 @@ class _AddEquipmentScreenState extends State { borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)), ), - child: const Center( - child: Text('Adicionar equipamento', - style: TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: Colors.white)), + child: Center( + child: Text( + widget.dischargeId == null + ? 'Adicionar Equipamento' + : 'Editar Equipamento', + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), ), ), Padding( @@ -528,7 +765,7 @@ class _AddEquipmentScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Tipos de descargas atmosféricas', + const Text('Tipos de Descargas Atmosféricas', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -548,7 +785,8 @@ class _AddEquipmentScreenState extends State { combinedTypes, value: _selectedType, onChanged: (newValue) { - if (newValue != 'Selecione o tipo de equipamento') { + if (newValue != + 'Selecione o tipo de descarga atmosférica') { setState(() { _selectedType = newValue; Map selected = @@ -602,7 +840,6 @@ class _AddEquipmentScreenState extends State { ), ], ), - const SizedBox(height: 8), TextButton( onPressed: () { setState(() { @@ -611,7 +848,6 @@ class _AddEquipmentScreenState extends State { }, child: const Text('Limpar seleção'), ), - const SizedBox(height: 30), const Text('Quantidade', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), @@ -622,7 +858,7 @@ class _AddEquipmentScreenState extends State { borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _equipmentQuantityController, + controller: _quantity, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly @@ -632,6 +868,46 @@ class _AddEquipmentScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 15), ), + onChanged: (value) { + print('Quantity changed: $value'); + }, + ), + ), + const SizedBox(height: 15), + const Text('Observações', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _observationsController, + maxLines: 3, + maxLength: 500, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: + 'Digite suas observações aqui (máx 500 caracteres)', + ), + buildCounter: (BuildContext context, + {required int currentLength, + required bool isFocused, + required int? maxLength}) { + return Text( + '$currentLength / $maxLength', + style: TextStyle( + fontSize: 12, + color: currentLength > maxLength! + ? Colors.red + : Colors.grey, + ), + ); + }, ), ), const SizedBox(height: 15), @@ -640,7 +916,9 @@ class _AddEquipmentScreenState extends State { onPressed: _pickImage, ), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Stack( alignment: Alignment.topRight, children: [ @@ -661,10 +939,7 @@ class _AddEquipmentScreenState extends State { icon: const Icon(Icons.remove_circle, color: AppColors.warn), onPressed: () { - setState(() { - _images.removeWhere( - (element) => element.id == imageData.id); - }); + _deletePhoto(imageData.id); }, ), ], @@ -675,20 +950,22 @@ class _AddEquipmentScreenState extends State { Center( child: ElevatedButton( style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(AppColors.sigeIeYellow), - foregroundColor: - MaterialStateProperty.all(AppColors.sigeIeBlue), - minimumSize: - MaterialStateProperty.all(const Size(185, 55)), - shape: - MaterialStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ))), + backgroundColor: + MaterialStateProperty.all(AppColors.sigeIeYellow), + foregroundColor: + MaterialStateProperty.all(AppColors.sigeIeBlue), + minimumSize: + MaterialStateProperty.all(const Size(185, 55)), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + )), + ), onPressed: _showConfirmationDialog, - child: const Text( - 'ADICIONAR EQUIPAMENTO', - style: TextStyle( + child: Text( + widget.isEdit + ? 'ATUALIZAR EQUIPAMENTO' + : 'ADICIONAR EQUIPAMENTO', + style: const TextStyle( fontSize: 17, fontWeight: FontWeight.bold), ), ), @@ -784,13 +1061,15 @@ class _AddEquipmentScreenState extends State { items: items.map>((Map value) { return DropdownMenuItem( value: value['name'] as String, - enabled: value['name'] != 'Selecione o tipo de equipamento', + enabled: + value['name'] != 'Selecione o tipo de descarga atmosférica', child: Text( value['name'] as String, style: TextStyle( - color: value['name'] == 'Selecione o tipo de equipamento' - ? Colors.grey - : Colors.black, + color: + value['name'] == 'Selecione o tipo de descarga atmosférica' + ? Colors.grey + : Colors.black, ), ), ); diff --git a/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart b/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart index 6d600f8e..1570b885 100644 --- a/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart +++ b/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart @@ -12,13 +12,13 @@ class ListAtmosphericEquipment extends StatefulWidget { final int areaId; const ListAtmosphericEquipment({ - Key? key, + super.key, required this.areaName, required this.systemId, required this.localName, required this.localId, required this.areaId, - }) : super(key: key); + }); @override _ListAtmosphericEquipmentState createState() => @@ -26,58 +26,100 @@ class ListAtmosphericEquipment extends StatefulWidget { } class _ListAtmosphericEquipmentState extends State { - List equipmentList = []; - bool isLoading = true; - final AtmosphericEquipmentService _service = AtmosphericEquipmentService(); + late Future> _atmosphericList; + final AtmosphericEquipmentService _atmosphericService = + AtmosphericEquipmentService(); @override void initState() { super.initState(); - fetchEquipmentList(); + _atmosphericList = + _atmosphericService.getAtmosphericListByArea(widget.areaId); } - Future fetchEquipmentList() async { - try { - final List equipmentList = - await _service.getAtmosphericListByArea(widget.areaId); - if (mounted) { - setState(() { -/* this.equipmentList = equipmentList; - */ - isLoading = false; - }); - } - } catch (e) { - print('Error fetching equipment list: $e'); - if (mounted) { - setState(() { - isLoading = false; - }); - } - } + void navigateToAddAtmosphericDischarge(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddAtmosphericDischarge( + areaName: widget.areaName, + systemId: widget.systemId, + localName: widget.localName, + localId: widget.localId, + areaId: widget.areaId, + dischargeId: null, + ), + ), + ); } - void navigateToAddEquipment(BuildContext context) { + void _editAtmosphericDischarge(BuildContext context, int dischargeId) { Navigator.push( context, MaterialPageRoute( - builder: (context) => AddAtmosphericEquipmentScreen( + builder: (context) => AddAtmosphericDischarge( areaName: widget.areaName, systemId: widget.systemId, localName: widget.localName, localId: widget.localId, areaId: widget.areaId, + dischargeId: dischargeId, + isEdit: true, ), ), ); } - void _editEquipment(BuildContext context, String equipment) { - // Implement the logic to edit the equipment + Future _deleteAtmosphericDischarge( + BuildContext context, int dischargeId) async { + try { + await _atmosphericService.deleteAtmospheric(dischargeId); + setState(() { + _atmosphericList = + _atmosphericService.getAtmosphericListByArea(widget.areaId); + }); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Equipamento deletado com sucesso'), + backgroundColor: Colors.green, + ), + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao deletar o equipamento'), + backgroundColor: Colors.red, + ), + ); + } } - void _deleteEquipment(BuildContext context, String equipment) { - // Implement the logic to delete the equipment + void _confirmDelete(BuildContext context, int dischargeId) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirmar Exclusão'), + content: const Text( + 'Você tem certeza que deseja excluir este equipamento?'), + actions: [ + TextButton( + child: const Text('Cancelar'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Excluir'), + onPressed: () { + Navigator.of(context).pop(); + _deleteAtmosphericDischarge(context, dischargeId); + }, + ), + ], + ); + }, + ); } @override @@ -105,9 +147,10 @@ class _ListAtmosphericEquipmentState extends State { ), body: SingleChildScrollView( child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( + width: double.infinity, padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), decoration: const BoxDecoration( color: AppColors.sigeIeBlue, @@ -127,79 +170,78 @@ class _ListAtmosphericEquipmentState extends State { ), ), const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : equipmentList.isNotEmpty - ? Column( - children: equipmentList.map((equipment) { - return Container( - margin: - const EdgeInsets.symmetric(vertical: 5), - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - children: [ - Expanded( - child: Padding( - padding: - const EdgeInsets.only(left: 10), - child: Text( - equipment, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ), - ), - IconButton( - icon: const Icon(Icons.edit, - color: Colors.blue), - onPressed: () => _editEquipment( - context, equipment), - ), - IconButton( - icon: const Icon(Icons.delete, - color: Colors.red), - onPressed: () => _deleteEquipment( - context, equipment), - ), - ], - ), - ), - ); - }).toList(), - ) - : const Center( - child: Text( - 'Você ainda não tem equipamentos', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black54, - ), + FutureBuilder>( + future: _atmosphericList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Erro: ${snapshot.error}')); + } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var discharge = snapshot.data![index]; + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + title: Text( + discharge.equipmentCategory, + style: const TextStyle( + color: AppColors.lightText, + fontWeight: FontWeight.bold), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: + const Icon(Icons.edit, color: Colors.blue), + onPressed: () => _editAtmosphericDischarge( + context, discharge.id), ), - ), - const SizedBox(height: 40), - ], - ), + IconButton( + icon: + const Icon(Icons.delete, color: Colors.red), + onPressed: () => + _confirmDelete(context, discharge.id), + ), + ], + ), + ), + ); + }, + ); + } else { + return const Padding( + padding: EdgeInsets.all(10.0), + child: Center( + child: Text( + 'Nenhum equipamento encontrado.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black54), + ), + ), + ); + } + }, ), ], ), ), floatingActionButton: FloatingActionButton( - onPressed: () => navigateToAddEquipment(context), + onPressed: () => navigateToAddAtmosphericDischarge(context), backgroundColor: AppColors.sigeIeYellow, child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), From 7968fcbba8637f2f3f21fcedbd7ba7679e09170a Mon Sep 17 00:00:00 2001 From: EngDann Date: Sat, 17 Aug 2024 13:35:32 -0300 Subject: [PATCH 07/15] Ilumination completo e integrado Co-authored-by: Ramires rocha Co-authored-by: Kauan Jose Co-authored-by: OscarDeBrito Co-authored-by: Pedro Lucas --- api/sigeie/settings.py | 2 +- .../sige_ie/lib/core/data/universalURL.dart | 2 +- .../data/atmospheric/atmospheric_service.dart | 3 +- .../distribution_request_model.dart | 3 +- .../eletrical_line_request_model.dart | 1 + .../ilumination_request_model.dart | 11 +- .../iluminations/ilumination_response.dart | 7 +- .../ilumination_response_by_area_model.dart | 46 +- .../iluminations/ilumination_service.dart | 2 + .../refrigerations_request_model.dart | 1 + .../add_distribuition_board.dart | 2 +- .../add_ilumination_equipment.dart | 536 +++++++++++++++--- .../ilumination_equipment_list.dart | 330 ++++++----- .../lib/maps/controller/maps_controller.dart | 3 - 14 files changed, 681 insertions(+), 268 deletions(-) diff --git a/api/sigeie/settings.py b/api/sigeie/settings.py index e5c75f3e..c3da8211 100644 --- a/api/sigeie/settings.py +++ b/api/sigeie/settings.py @@ -6,7 +6,7 @@ DEBUG = True -ALLOWED_HOSTS = ['127.0.0.1', '10.0.2.2', '192.168.1.142', '192.168.133.230'] +ALLOWED_HOSTS = ['127.0.0.1', '10.0.2.2', '192.168.1.142', '192.168.15.36'] INSTALLED_APPS = [ 'django.contrib.admin', diff --git a/frontend/sige_ie/lib/core/data/universalURL.dart b/frontend/sige_ie/lib/core/data/universalURL.dart index f6f9dc0c..73bba549 100644 --- a/frontend/sige_ie/lib/core/data/universalURL.dart +++ b/frontend/sige_ie/lib/core/data/universalURL.dart @@ -1,3 +1,3 @@ const String urlUniversal = - 'http://192.168.133.230:8000'; + 'http://192.168.15.36:8000'; /* local host: http://10.0.2.2:8000 */ \ No newline at end of file diff --git a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart index 1a6a7140..5aa41ec1 100644 --- a/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart +++ b/frontend/sige_ie/lib/equipments/data/atmospheric/atmospheric_service.dart @@ -21,7 +21,8 @@ class AtmosphericEquipmentService { var url = Uri.parse('${baseUrl}atmospheric-discharges/by-area/$areaId/'); try { var response = await client.get(url); - + print('Status Code: ${response.statusCode}'); + print('Response Body: ${response.body}'); if (response.statusCode == 200) { List dataList = jsonDecode(response.body); return dataList diff --git a/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart b/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart index fa939372..09e313da 100644 --- a/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart @@ -1,8 +1,9 @@ class DistributionRequestModel { + /* Obs e power = int? */ int? area; int? system; int? quantity; - String? power; + int? power; bool dr; bool dps; bool grounding; diff --git a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart index 14e5a8a5..14aec855 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart @@ -1,4 +1,5 @@ class EletricalLineRequestModel { + /* obs */ int? area; int? system; int? quantity; diff --git a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_request_model.dart b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_request_model.dart index dddb286e..b90afd92 100644 --- a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_request_model.dart @@ -3,8 +3,9 @@ class IluminationRequestModel { int? system; int? quantity; int? power; - String tecnology; - String format; + String? tecnology; + String? format; + String? observation; IluminationRequestModel( {required this.area, @@ -12,7 +13,8 @@ class IluminationRequestModel { required this.quantity, required this.format, required this.power, - required this.tecnology}); + required this.tecnology, + required this.observation}); Map toJson() { return { @@ -21,7 +23,8 @@ class IluminationRequestModel { 'quantity': quantity, 'format': format, 'power': power, - 'tecnology': tecnology + 'tecnology': tecnology, + 'observation': observation }; } } diff --git a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response.dart b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response.dart index 7d8ccb9a..ffbabe6c 100644 --- a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response.dart +++ b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response.dart @@ -7,6 +7,7 @@ class IluminationResponseModel { int power; String tecnology; String format; + String? observation; IluminationResponseModel( {required this.id, @@ -16,7 +17,8 @@ class IluminationResponseModel { required this.quantity, required this.format, required this.power, - required this.tecnology}); + required this.tecnology, + required this.observation}); factory IluminationResponseModel.fromJson(Map json) { return IluminationResponseModel( @@ -27,6 +29,7 @@ class IluminationResponseModel { quantity: json['quantity'], format: json['format'], power: json['power'], - tecnology: json['tecnology']); + tecnology: json['tecnology'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response_by_area_model.dart index dd8a6913..8e743385 100644 --- a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_response_by_area_model.dart @@ -1,33 +1,29 @@ class IluminationEquipmentResponseByAreaModel { - int id; - int area; - String equipmentCategory; - int system; - int quantity; - int power; - String tecnology; - String format; + final int id; + final int area; + final String equipmentCategory; + final int system; + final int quantity; + final String observation; - IluminationEquipmentResponseByAreaModel( - {required this.id, - required this.area, - required this.equipmentCategory, - required this.system, - required this.quantity, - required this.format, - required this.power, - required this.tecnology}); + IluminationEquipmentResponseByAreaModel({ + required this.id, + required this.area, + required this.equipmentCategory, + required this.system, + required this.quantity, + required this.observation, + }); factory IluminationEquipmentResponseByAreaModel.fromJson( Map json) { return IluminationEquipmentResponseByAreaModel( - id: json['id'], - area: json['area'], - equipmentCategory: json['equipment_category'], - system: json['system'], - quantity: json['quantity'], - format: json['format'], - power: json['power'], - tecnology: json['tecnology']); + id: json['id'], + area: json['area'], + equipmentCategory: json['equipment_category'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation'], + ); } } diff --git a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_service.dart b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_service.dart index 483ad73e..e238f907 100644 --- a/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_service.dart +++ b/frontend/sige_ie/lib/equipments/data/iluminations/ilumination_service.dart @@ -21,6 +21,8 @@ class IluminationEquipmentService { var url = Uri.parse('${baseUrl}iluminations/by-area/$areaId/'); try { var response = await client.get(url); + print('Status Code: ${response.statusCode}'); + print('Response Body: ${response.body}'); if (response.statusCode == 200) { List dataList = jsonDecode(response.body); diff --git a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart index 7ead5b85..cdb88474 100644 --- a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart @@ -1,4 +1,5 @@ class RefrigerationsRequestModel { + /* obs */ int? area; int? system; int? quantity; diff --git a/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart b/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart index 50733697..ff28b84f 100644 --- a/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart +++ b/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart @@ -428,7 +428,7 @@ class _AddDistribuitionBoardState extends State { area: widget.areaId, system: widget.systemId, quantity: null, - power: '', + power: null, dr: true, dps: true, grounding: true, diff --git a/frontend/sige_ie/lib/equipments/feature/iluminations/add_ilumination_equipment.dart b/frontend/sige_ie/lib/equipments/feature/iluminations/add_ilumination_equipment.dart index dc6ca49d..efc176df 100644 --- a/frontend/sige_ie/lib/equipments/feature/iluminations/add_ilumination_equipment.dart +++ b/frontend/sige_ie/lib/equipments/feature/iluminations/add_ilumination_equipment.dart @@ -1,17 +1,18 @@ import 'dart:io'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:sige_ie/config/app_styles.dart'; - import 'package:sige_ie/equipments/data/iluminations/ilumination__equipment_request_model.dart'; -import 'package:sige_ie/equipments/data/iluminations/ilumination_request_model.dart'; +import 'package:sige_ie/equipments/data/iluminations/ilumination_response.dart'; +import 'package:sige_ie/equipments/data/iluminations/ilumination_service.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_request_model.dart'; +import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_response_model.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_service.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_response_model.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_service.dart'; import 'package:sige_ie/equipments/data/equipment_service.dart'; +import 'package:sige_ie/equipments/data/iluminations/ilumination_request_model.dart'; import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_request_model.dart'; import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_service.dart'; @@ -19,12 +20,17 @@ class ImageData { int id; File imageFile; String description; + bool toDelete; - ImageData(this.imageFile, this.description) : id = Random().nextInt(1000000); + ImageData({ + int? id, + required this.imageFile, + required this.description, + this.toDelete = false, + }) : id = id ?? -1; } List _images = []; -Map> categoryImagesMap = {}; class AddIluminationEquipmentScreen extends StatefulWidget { final String areaName; @@ -32,6 +38,8 @@ class AddIluminationEquipmentScreen extends StatefulWidget { final int localId; final int systemId; final int areaId; + final int? iluminationId; + final bool isEdit; const AddIluminationEquipmentScreen({ super.key, @@ -40,43 +48,130 @@ class AddIluminationEquipmentScreen extends StatefulWidget { required this.localName, required this.localId, required this.areaId, + this.iluminationId, + this.isEdit = false, }); @override - _AddIlluminationScreenState createState() => _AddIlluminationScreenState(); + _AddIluminationEquipmentScreenState createState() => + _AddIluminationEquipmentScreenState(); } -class _AddIlluminationScreenState extends State { +class _AddIluminationEquipmentScreenState + extends State { EquipmentService equipmentService = EquipmentService(); + IluminationEquipmentService iluminationService = + IluminationEquipmentService(); EquipmentPhotoService equipmentPhotoService = EquipmentPhotoService(); PersonalEquipmentCategoryService personalEquipmentCategoryService = PersonalEquipmentCategoryService(); GenericEquipmentCategoryService genericEquipmentCategoryService = GenericEquipmentCategoryService(); - final _equipmentQuantityController = TextEditingController(); - final _equipmentPowerController = TextEditingController(); + final TextEditingController _quantity = TextEditingController(); + final TextEditingController _power = TextEditingController(); + final TextEditingController _formatController = TextEditingController(); + final TextEditingController _tecnologyController = TextEditingController(); + final TextEditingController _observationsController = TextEditingController(); String? _selectedType; - String? _selectedTypeToDelete; - String? _newEquipmentTypeName; int? _selectedGenericEquipmentCategoryId; int? _selectedPersonalEquipmentCategoryId; bool _isPersonalEquipmentCategorySelected = false; - + String? _newEquipmentTypeName; + String? _selectedTypeToDelete; + int? equipmentId; List> genericEquipmentTypes = []; List> personalEquipmentTypes = []; Map personalEquipmentMap = {}; + IluminationResponseModel? iluminationResponseModel; @override void initState() { super.initState(); _fetchEquipmentCategory(); + if (widget.isEdit && widget.iluminationId != null) { + _initializeData(widget.iluminationId!); + } } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _fetchEquipmentCategory(); + Future _initializeData(int iluminationId) async { + try { + await _fetchIluminationEquipment(iluminationId); + + if (iluminationResponseModel != null) { + setState(() { + equipmentId = iluminationResponseModel!.equipment; + _quantity.text = iluminationResponseModel!.quantity.toString(); + _power.text = iluminationResponseModel!.power.toString(); + _formatController.text = iluminationResponseModel!.format; + _tecnologyController.text = iluminationResponseModel!.tecnology; + _observationsController.text = + iluminationResponseModel!.observation ?? ''; + }); + + _fetchEquipmentDetails(iluminationResponseModel!.equipment); + _fetchExistingPhotos(iluminationResponseModel!.equipment); + } + } catch (error) { + print('Error: $error'); + } + } + + Future _fetchIluminationEquipment(int iluminationId) async { + iluminationResponseModel = + await iluminationService.getIluminationById(iluminationId); + } + + Future _fetchEquipmentDetails(int equipmentId) async { + try { + final equipmentDetails = + await equipmentService.getEquipmentById(equipmentId); + + setState(() { + _isPersonalEquipmentCategorySelected = + equipmentDetails['personal_equipment_category'] != null; + + if (_isPersonalEquipmentCategorySelected) { + _selectedPersonalEquipmentCategoryId = + equipmentDetails['personal_equipment_category']; + _selectedType = personalEquipmentTypes.firstWhere((element) => + element['id'] == _selectedPersonalEquipmentCategoryId)['name'] + as String; + } else { + _selectedGenericEquipmentCategoryId = + equipmentDetails['generic_equipment_category']; + _selectedType = genericEquipmentTypes.firstWhere((element) => + element['id'] == _selectedGenericEquipmentCategoryId)['name'] + as String; + } + }); + } catch (e) { + print('Erro ao buscar detalhes do equipamento: $e'); + } + } + + void _fetchExistingPhotos(int equipmentId) async { + try { + List photos = + await equipmentPhotoService.getPhotosByEquipmentId(equipmentId); + + List imageList = []; + + for (var photo in photos) { + File imageFile = await photo.toFile(); + imageList.add(ImageData( + id: photo.id, + imageFile: imageFile, + description: photo.description ?? '', + )); + } + + setState(() { + _images = imageList; + }); + } catch (e) { + print('Erro ao buscar fotos existentes: $e'); + } } Future _fetchEquipmentCategory() async { @@ -106,9 +201,12 @@ class _AddIlluminationScreenState extends State { @override void dispose() { - _equipmentQuantityController.dispose(); - _equipmentPowerController.dispose(); - categoryImagesMap[widget.systemId]?.clear(); + _quantity.dispose(); + _power.dispose(); + _formatController.dispose(); + _tecnologyController.dispose(); + _images.clear(); + _observationsController.dispose(); super.dispose(); } @@ -125,8 +223,9 @@ class _AddIlluminationScreenState extends State { } void _showImageDialog(File imageFile, {ImageData? existingImage}) { - TextEditingController descriptionController = - TextEditingController(text: existingImage?.description ?? ''); + TextEditingController descriptionController = TextEditingController( + text: existingImage?.description ?? '', + ); showDialog( context: context, builder: (BuildContext context) { @@ -154,17 +253,17 @@ class _AddIlluminationScreenState extends State { child: const Text('Salvar'), onPressed: () { setState(() { + String? description = descriptionController.text.isEmpty + ? null + : descriptionController.text; if (existingImage != null) { - existingImage.description = descriptionController.text; + existingImage.description = description ?? ''; } else { - final imageData = - ImageData(imageFile, descriptionController.text); - final systemId = widget.systemId; - if (!categoryImagesMap.containsKey(systemId)) { - categoryImagesMap[systemId] = []; - } - categoryImagesMap[systemId]!.add(imageData); - _images = categoryImagesMap[systemId]!; + final imageData = ImageData( + imageFile: imageFile, + description: description ?? '', + ); + _images.add(imageData); } }); Navigator.of(context).pop(); @@ -219,6 +318,129 @@ class _AddIlluminationScreenState extends State { ); } + void _updateEquipment() async { + int? genericEquipmentCategory; + int? personalEquipmentCategory; + + if (_isPersonalEquipmentCategorySelected) { + genericEquipmentCategory = null; + personalEquipmentCategory = _selectedPersonalEquipmentCategoryId; + } else { + genericEquipmentCategory = _selectedGenericEquipmentCategoryId; + personalEquipmentCategory = null; + } + + final Map equipmentTypeUpdate = { + "generic_equipment_category": genericEquipmentCategory, + "personal_equipment_category": personalEquipmentCategory, + }; + + print('Quantity: ${_quantity.text}'); + print('Equipment Type Update: $equipmentTypeUpdate'); + + bool typeUpdateSuccess = await equipmentService.updateEquipment( + equipmentId!, equipmentTypeUpdate); + + if (typeUpdateSuccess) { + final IluminationRequestModel iluminationModel = IluminationRequestModel( + area: widget.areaId, + system: widget.systemId, + quantity: int.tryParse(_quantity.text), + power: int.tryParse(_power.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + format: + _formatController.text.isNotEmpty ? _formatController.text : null, + tecnology: _tecnologyController.text.isNotEmpty + ? _tecnologyController.text + : null, + ); + + print('Ilumination Model: ${iluminationModel.toJson()}'); + + bool iluminationUpdateSuccess = await iluminationService + .updateIlumination(widget.iluminationId!, iluminationModel); + + if (iluminationUpdateSuccess) { + await Future.wait(_images + .where((imageData) => imageData.toDelete) + .map((imageData) async { + await equipmentPhotoService.deletePhoto(imageData.id); + })); + + await Future.wait(_images + .where((imageData) => !imageData.toDelete) + .map((imageData) async { + if (imageData.id == -1) { + await equipmentPhotoService.createPhoto( + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } else { + await equipmentPhotoService.updatePhoto( + imageData.id, + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } + })); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Detalhes do equipamento atualizados com sucesso.'), + backgroundColor: Colors.green, + ), + ); + Navigator.pushReplacementNamed( + context, + '/listIluminationEquipment', + arguments: { + 'areaName': widget.areaName, + 'systemId': widget.systemId, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + setState(() { + _quantity.clear(); + _power.clear(); + _formatController.clear(); + _tecnologyController.clear(); + _selectedType = null; + _selectedPersonalEquipmentCategoryId = null; + _selectedGenericEquipmentCategoryId = null; + _images.clear(); + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar os detalhes do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar o tipo do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } + Future _registerPersonalEquipmentType() async { int systemId = widget.systemId; PersonalEquipmentCategoryRequestModel personalEquipmentTypeRequestModel = @@ -231,7 +453,8 @@ class _AddIlluminationScreenState extends State { if (id != -1) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento registrado com sucesso.'), + content: + Text('Tipo de equipamento de iluminação registrado com sucesso.'), backgroundColor: Colors.green, ), ); @@ -246,7 +469,8 @@ class _AddIlluminationScreenState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao registrar o equipamento.'), + content: + Text('Falha ao registrar o tipo de equipamento de iluminação.'), backgroundColor: Colors.red, ), ); @@ -274,7 +498,7 @@ class _AddIlluminationScreenState extends State { return; } - int equipmentId = personalEquipmentMap[_selectedTypeToDelete]!; + int personalCategoryId = personalEquipmentMap[_selectedTypeToDelete]!; showDialog( context: context, @@ -295,13 +519,14 @@ class _AddIlluminationScreenState extends State { onPressed: () async { Navigator.of(context).pop(); bool success = await personalEquipmentCategoryService - .deletePersonalEquipmentCategory(equipmentId); + .deletePersonalEquipmentCategory(personalCategoryId); if (success) { setState(() { personalEquipmentTypes.removeWhere( (element) => element['name'] == _selectedTypeToDelete); _selectedTypeToDelete = null; + _selectedType = null; _fetchEquipmentCategory(); }); ScaffoldMessenger.of(context).showSnackBar( @@ -327,9 +552,11 @@ class _AddIlluminationScreenState extends State { } void _showConfirmationDialog() { - if (_equipmentQuantityController.text.isEmpty || - _equipmentPowerController.text.isEmpty || - (_selectedType == null && _newEquipmentTypeName == null)) { + if (_quantity.text.isEmpty || + _power.text.isEmpty || + (_selectedType == null && _newEquipmentTypeName == null) || + _formatController.text.isEmpty || + _tecnologyController.text.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Por favor, preencha todos os campos.'), @@ -352,16 +579,30 @@ class _AddIlluminationScreenState extends State { const SizedBox(height: 10), const Text('Quantidade:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentQuantityController.text), + Text(_quantity.text), + const SizedBox(height: 10), + const Text('Potência:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_power.text), + const SizedBox(height: 10), + const Text('Formato:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_formatController.text), + const SizedBox(height: 10), + const Text('Tecnologia:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_tecnologyController.text), const SizedBox(height: 10), - const Text('Potência (KW):', + const Text('Observações:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentPowerController.text), + Text(_observationsController.text), const SizedBox(height: 10), const Text('Imagens:', style: TextStyle(fontWeight: FontWeight.bold)), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Padding( padding: const EdgeInsets.all(4.0), child: GestureDetector( @@ -393,9 +634,16 @@ class _AddIlluminationScreenState extends State { }, ), TextButton( - child: const Text('Adicionar'), + child: widget.isEdit + ? const Text('Atualizar') + : const Text('Adicionar'), onPressed: () { - _registerEquipment(); + if (widget.isEdit) { + _updateEquipment(); + } else { + _registerEquipment(); + } + Navigator.of(context).pop(); }, ), ], @@ -419,10 +667,15 @@ class _AddIlluminationScreenState extends State { final IluminationRequestModel iluminationModel = IluminationRequestModel( area: widget.areaId, system: widget.systemId, - quantity: null, - format: '', - power: null, - tecnology: '', + quantity: int.tryParse(_quantity.text), + power: int.tryParse(_power.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + format: _formatController.text.isNotEmpty ? _formatController.text : null, + tecnology: _tecnologyController.text.isNotEmpty + ? _tecnologyController.text + : null, ); final IluminationEquipmentRequestModel iluminationEquipmentDetail = @@ -432,16 +685,20 @@ class _AddIlluminationScreenState extends State { iluminationRequestModel: iluminationModel, ); - int? equipmentId = + int? id = await equipmentService.createIlumination(iluminationEquipmentDetail); + setState(() { + equipmentId = id; + }); - if (equipmentId != null) { + if (equipmentId != null && equipmentId != 0) { await Future.wait(_images.map((imageData) async { await equipmentPhotoService.createPhoto( EquipmentPhotoRequestModel( photo: imageData.imageFile, - description: imageData.description, - equipment: equipmentId, + description: + imageData.description.isEmpty ? null : imageData.description, + equipment: equipmentId!, ), ); })); @@ -464,12 +721,15 @@ class _AddIlluminationScreenState extends State { }, ); setState(() { - _equipmentQuantityController.clear(); - _equipmentPowerController.clear(); + _quantity.clear(); + _power.clear(); + _formatController.clear(); + _tecnologyController.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -481,6 +741,13 @@ class _AddIlluminationScreenState extends State { } } + Future _deletePhoto(int photoId) async { + setState(() { + _images.firstWhere((imageData) => imageData.id == photoId).toDelete = + true; + }); + } + @override Widget build(BuildContext context) { List> combinedTypes = [ @@ -496,12 +763,15 @@ class _AddIlluminationScreenState extends State { icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () { setState(() { - _equipmentQuantityController.clear(); - _equipmentPowerController.clear(); + _quantity.clear(); + _power.clear(); + _formatController.clear(); + _tecnologyController.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); Navigator.pushReplacementNamed( context, @@ -529,12 +799,17 @@ class _AddIlluminationScreenState extends State { borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)), ), - child: const Center( - child: Text('Adicionar equipamento', - style: TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: Colors.white)), + child: Center( + child: Text( + widget.iluminationId == null + ? 'Adicionar Equipamento' + : 'Editar Equipamento', + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), ), ), Padding( @@ -561,7 +836,7 @@ class _AddIlluminationScreenState extends State { combinedTypes, value: _selectedType, onChanged: (newValue) { - if (newValue != 'Selecione o tipo de equipamento') { + if (newValue != 'Selecione o tipo de iluminação') { setState(() { _selectedType = newValue; Map selected = @@ -615,7 +890,6 @@ class _AddIlluminationScreenState extends State { ), ], ), - const SizedBox(height: 8), TextButton( onPressed: () { setState(() { @@ -624,8 +898,7 @@ class _AddIlluminationScreenState extends State { }, child: const Text('Limpar seleção'), ), - const SizedBox(height: 30), - const Text('Potência (KW)', + const Text('Potência (W)', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -635,8 +908,11 @@ class _AddIlluminationScreenState extends State { borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _equipmentPowerController, + controller: _power, keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], decoration: const InputDecoration( border: InputBorder.none, contentPadding: @@ -644,7 +920,7 @@ class _AddIlluminationScreenState extends State { ), ), ), - const SizedBox(height: 30), + const SizedBox(height: 15), const Text('Quantidade', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), @@ -655,7 +931,7 @@ class _AddIlluminationScreenState extends State { borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _equipmentQuantityController, + controller: _quantity, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly @@ -668,12 +944,91 @@ class _AddIlluminationScreenState extends State { ), ), const SizedBox(height: 15), + const Text('Formato', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _formatController, + keyboardType: TextInputType.text, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + ), + ), + ), + const SizedBox(height: 15), + const Text('Tecnologia', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _tecnologyController, + keyboardType: TextInputType.text, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + ), + ), + ), + const SizedBox(height: 15), + const Text('Observações', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _observationsController, + maxLines: 3, + maxLength: 500, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: + 'Digite suas observações aqui (máx 500 caracteres)', + ), + buildCounter: (BuildContext context, + {required int currentLength, + required bool isFocused, + required int? maxLength}) { + return Text( + '$currentLength / $maxLength', + style: TextStyle( + fontSize: 12, + color: currentLength > maxLength! + ? Colors.red + : Colors.grey, + ), + ); + }, + ), + ), + const SizedBox(height: 15), IconButton( icon: const Icon(Icons.camera_alt), onPressed: _pickImage, ), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Stack( alignment: Alignment.topRight, children: [ @@ -694,10 +1049,7 @@ class _AddIlluminationScreenState extends State { icon: const Icon(Icons.remove_circle, color: AppColors.warn), onPressed: () { - setState(() { - _images.removeWhere( - (element) => element.id == imageData.id); - }); + _deletePhoto(imageData.id); }, ), ], @@ -708,20 +1060,24 @@ class _AddIlluminationScreenState extends State { Center( child: ElevatedButton( style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(AppColors.sigeIeYellow), - foregroundColor: - MaterialStateProperty.all(AppColors.sigeIeBlue), - minimumSize: - MaterialStateProperty.all(const Size(185, 55)), - shape: - MaterialStateProperty.all(RoundedRectangleBorder( + backgroundColor: + MaterialStateProperty.all(AppColors.sigeIeYellow), + foregroundColor: + MaterialStateProperty.all(AppColors.sigeIeBlue), + minimumSize: + MaterialStateProperty.all(const Size(185, 55)), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), - ))), + ), + ), + ), onPressed: _showConfirmationDialog, - child: const Text( - 'ADICIONAR EQUIPAMENTO', - style: TextStyle( + child: Text( + widget.isEdit + ? 'ATUALIZAR EQUIPAMENTO' + : 'ADICIONAR EQUIPAMENTO', + style: const TextStyle( fontSize: 17, fontWeight: FontWeight.bold), ), ), @@ -808,8 +1164,10 @@ class _AddIlluminationScreenState extends State { ), padding: const EdgeInsets.symmetric(horizontal: 10), child: DropdownButton( - hint: Text(items.first['name'] as String, - style: const TextStyle(color: Colors.grey)), + hint: Text( + items.first['name'] as String, + style: const TextStyle(color: Colors.grey), + ), value: value, isExpanded: true, underline: Container(), @@ -817,11 +1175,11 @@ class _AddIlluminationScreenState extends State { items: items.map>((Map value) { return DropdownMenuItem( value: value['name'] as String, - enabled: value['name'] != 'Selecione o tipo de equipamento', + enabled: value['name'] != 'Selecione o tipo de iluminação', child: Text( value['name'] as String, style: TextStyle( - color: value['name'] == 'Selecione o tipo de equipamento' + color: value['name'] == 'Selecione o tipo de iluminação' ? Colors.grey : Colors.black, ), diff --git a/frontend/sige_ie/lib/equipments/feature/iluminations/ilumination_equipment_list.dart b/frontend/sige_ie/lib/equipments/feature/iluminations/ilumination_equipment_list.dart index ab8ece4f..7bb7b2e4 100644 --- a/frontend/sige_ie/lib/equipments/feature/iluminations/ilumination_equipment_list.dart +++ b/frontend/sige_ie/lib/equipments/feature/iluminations/ilumination_equipment_list.dart @@ -12,13 +12,13 @@ class ListIluminationEquipment extends StatefulWidget { final int areaId; const ListIluminationEquipment({ - Key? key, + super.key, required this.areaName, required this.systemId, required this.localName, required this.localId, required this.areaId, - }) : super(key: key); + }); @override _ListIluminationEquipmentState createState() => @@ -26,38 +26,37 @@ class ListIluminationEquipment extends StatefulWidget { } class _ListIluminationEquipmentState extends State { - List equipmentList = []; - bool isLoading = true; - final IluminationEquipmentService _service = IluminationEquipmentService(); + late Future> _iluminationList; + final IluminationEquipmentService _iluminationService = + IluminationEquipmentService(); + + final GlobalKey _scaffoldMessengerKey = + GlobalKey(); @override void initState() { super.initState(); - fetchEquipmentList(); + _iluminationList = + _iluminationService.getIluminationListByArea(widget.areaId); } - Future fetchEquipmentList() async { - try { - final List equipmentList = - await _service.getIluminationListByArea(widget.areaId); - if (mounted) { - setState(() { -/* this.equipmentList = equipmentList; - */ - isLoading = false; - }); - } - } catch (e) { - print('Error fetching equipment list: $e'); - if (mounted) { - setState(() { - isLoading = false; - }); - } - } + void navigateToAddIlumination(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddIluminationEquipmentScreen( + areaName: widget.areaName, + systemId: widget.systemId, + localName: widget.localName, + localId: widget.localId, + areaId: widget.areaId, + iluminationId: null, + ), + ), + ); } - void navigateToAddEquipment(BuildContext context) { + void _editIlumination(BuildContext context, int iluminationId) { Navigator.push( context, MaterialPageRoute( @@ -67,141 +66,192 @@ class _ListIluminationEquipmentState extends State { localName: widget.localName, localId: widget.localId, areaId: widget.areaId, + iluminationId: iluminationId, + isEdit: true, ), ), ); } - void _editEquipment(BuildContext context, String equipment) { - // Implement the logic to edit the equipment + Future _deleteIlumination( + BuildContext context, int iluminationId) async { + try { + await _iluminationService.deleteIlumination(iluminationId); + setState(() { + _iluminationList = + _iluminationService.getIluminationListByArea(widget.areaId); + }); + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Equipamento deletado com sucesso'), + backgroundColor: Colors.green, + ), + ); + } catch (e) { + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Falha ao deletar o equipamento'), + backgroundColor: Colors.red, + ), + ); + } } - void _deleteEquipment(BuildContext context, String equipment) { - // Implement the logic to delete the equipment + void _confirmDelete(BuildContext context, int iluminationId) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirmar Exclusão'), + content: const Text( + 'Você tem certeza que deseja excluir este equipamento?'), + actions: [ + TextButton( + child: const Text('Cancelar'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Excluir'), + onPressed: () { + Navigator.of(context).pop(); + _deleteIlumination(context, iluminationId); + }, + ), + ], + ); + }, + ); } @override Widget build(BuildContext context) { String systemTitle = 'Iluminação'; - return Scaffold( - appBar: AppBar( - backgroundColor: AppColors.sigeIeBlue, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pushReplacementNamed( - context, - '/systemLocation', - arguments: { - 'areaName': widget.areaName, - 'localName': widget.localName, - 'localId': widget.localId, - 'areaId': widget.areaId, - }, - ); - }, + return ScaffoldMessenger( + key: _scaffoldMessengerKey, + child: Scaffold( + appBar: AppBar( + backgroundColor: AppColors.sigeIeBlue, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () { + Navigator.pushReplacementNamed( + context, + '/systemLocation', + arguments: { + 'areaName': widget.areaName, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + }, + ), ), - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), - decoration: const BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: - BorderRadius.vertical(bottom: Radius.circular(20)), - ), - child: Center( - child: Text( - '${widget.areaName} - $systemTitle', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: AppColors.lightText, + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), + decoration: const BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: + BorderRadius.vertical(bottom: Radius.circular(20)), + ), + child: Center( + child: Text( + '${widget.areaName} - $systemTitle', + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: AppColors.lightText, + ), ), ), ), - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : equipmentList.isNotEmpty - ? Column( - children: equipmentList.map((equipment) { - return Container( - margin: - const EdgeInsets.symmetric(vertical: 5), - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - children: [ - Expanded( - child: Padding( - padding: - const EdgeInsets.only(left: 10), - child: Text( - equipment, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ), - ), - IconButton( - icon: const Icon(Icons.edit, - color: Colors.blue), - onPressed: () => _editEquipment( - context, equipment), - ), - IconButton( - icon: const Icon(Icons.delete, - color: Colors.red), - onPressed: () => _deleteEquipment( - context, equipment), - ), - ], - ), - ), - ); - }).toList(), - ) - : const Center( - child: Text( - 'Você ainda não tem equipamentos', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black54, - ), + const SizedBox(height: 20), + FutureBuilder>( + future: _iluminationList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Erro: ${snapshot.error}')); + } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var iluminationEquipment = snapshot.data![index]; + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + title: Text( + iluminationEquipment.equipmentCategory, + style: const TextStyle( + color: AppColors.lightText, + fontWeight: FontWeight.bold, ), ), - const SizedBox(height: 40), - ], + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit, + color: Colors.blue), + onPressed: () => _editIlumination( + context, iluminationEquipment.id), + ), + IconButton( + icon: const Icon(Icons.delete, + color: Colors.red), + onPressed: () => _confirmDelete( + context, iluminationEquipment.id), + ), + ], + ), + ), + ); + }, + ); + } else { + return const Padding( + padding: EdgeInsets.all(10.0), + child: Center( + child: Text( + 'Nenhum equipamento encontrado.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black54, + ), + ), + ), + ); + } + }, ), - ), - ], + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => navigateToAddIlumination(context), + backgroundColor: AppColors.sigeIeYellow, + child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), - ), - floatingActionButton: FloatingActionButton( - onPressed: () => navigateToAddEquipment(context), - backgroundColor: AppColors.sigeIeYellow, - child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), ); } diff --git a/frontend/sige_ie/lib/maps/controller/maps_controller.dart b/frontend/sige_ie/lib/maps/controller/maps_controller.dart index 5c0ccf2c..33eccac4 100644 --- a/frontend/sige_ie/lib/maps/controller/maps_controller.dart +++ b/frontend/sige_ie/lib/maps/controller/maps_controller.dart @@ -29,14 +29,12 @@ class MapsController extends GetxController { bool serviceEnabled; LocationPermission permission; - // Verifique se o serviço de localização está habilitado serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { Get.snackbar('Erro', 'Serviço de localização está desabilitado.'); return; } - // Verifique se a permissão de localização é concedida permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); @@ -51,7 +49,6 @@ class MapsController extends GetxController { return; } - // Obtenha a localização atual final Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); From 31ee5feb777c90822d57301bf2a244c4b2710fa4 Mon Sep 17 00:00:00 2001 From: EngDann Date: Sat, 17 Aug 2024 13:58:42 -0300 Subject: [PATCH 08/15] Eletrical Line completo e integrado Co-authored-by: Ramires rocha Co-authored-by: Kauan Jose Co-authored-by: OscarDeBrito Co-authored-by: Pedro Lucas --- .../eletrical_line_request_model.dart | 14 +- ...eletrical_line_response_by_area_model.dart | 16 +- .../eletrical_line_response_model.dart | 27 +- .../electrical_line/add_electrical_line.dart | 435 +++++++++++++++--- .../electrical_line/electrical_line_list.dart | 324 +++++++------ 5 files changed, 580 insertions(+), 236 deletions(-) diff --git a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart index 14aec855..b51c6f40 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_request_model.dart @@ -1,13 +1,21 @@ class EletricalLineRequestModel { - /* obs */ int? area; int? system; int? quantity; + String? observation; EletricalLineRequestModel( - {required this.area, required this.system, this.quantity}); + {required this.area, + required this.system, + required this.quantity, + required this.observation}); Map toJson() { - return {'area': area, 'system': system, 'quantity': quantity}; + return { + 'area': area, + 'system': system, + 'quantity': quantity, + 'observation': observation + }; } } diff --git a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_by_area_model.dart index b9ca1e8a..10b513ae 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_by_area_model.dart @@ -4,14 +4,15 @@ class EletricalLineEquipmentResponseByAreaModel { String equipmentCategory; int system; int quantity; + String? observation; - EletricalLineEquipmentResponseByAreaModel({ - required this.id, - required this.area, - required this.equipmentCategory, - required this.system, - required this.quantity, - }); + EletricalLineEquipmentResponseByAreaModel( + {required this.id, + required this.area, + required this.equipmentCategory, + required this.system, + required this.quantity, + required this.observation}); factory EletricalLineEquipmentResponseByAreaModel.fromJson( Map json) { @@ -21,6 +22,7 @@ class EletricalLineEquipmentResponseByAreaModel { equipmentCategory: json['equipment_category'], system: json['system'], quantity: json['quantity'], + observation: json['observation'], ); } } diff --git a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_model.dart b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_model.dart index dd56e1e1..f932dc1d 100644 --- a/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/eletrical_line/eletrical_line_response_model.dart @@ -4,22 +4,23 @@ class EletricalLineResponseModel { int equipment; int system; int quantity; + String? observation; - EletricalLineResponseModel({ - required this.id, - required this.area, - required this.equipment, - required this.system, - required this.quantity, - }); + EletricalLineResponseModel( + {required this.id, + required this.area, + required this.equipment, + required this.system, + required this.quantity, + required this.observation}); factory EletricalLineResponseModel.fromJson(Map json) { return EletricalLineResponseModel( - id: json['id'], - area: json['area'], - equipment: json['equipment'], - system: json['system'], - quantity: json['quantity'], - ); + id: json['id'], + area: json['area'], + equipment: json['equipment'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart b/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart index 084def23..c3a82434 100644 --- a/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart +++ b/frontend/sige_ie/lib/equipments/feature/electrical_line/add_electrical_line.dart @@ -1,16 +1,18 @@ -import 'dart:math'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'dart:io'; import 'package:image_picker/image_picker.dart'; import 'package:sige_ie/config/app_styles.dart'; -import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_equipment_request_model.dart'; -import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_request_model.dart'; -import 'package:sige_ie/equipments/data/equipment_service.dart'; +import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_response_model.dart'; +import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_service.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_request_model.dart'; +import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_response_model.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_service.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_response_model.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_service.dart'; +import 'package:sige_ie/equipments/data/equipment_service.dart'; +import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_equipment_request_model.dart'; +import 'package:sige_ie/equipments/data/eletrical_line/eletrical_line_request_model.dart'; import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_request_model.dart'; import 'package:sige_ie/shared/data/personal-equipment-category/personal_equipment_category_service.dart'; @@ -18,12 +20,17 @@ class ImageData { int id; File imageFile; String description; + bool toDelete; - ImageData(this.imageFile, this.description) : id = Random().nextInt(1000000); + ImageData({ + int? id, + required this.imageFile, + required this.description, + this.toDelete = false, + }) : id = id ?? -1; // Valor padrão indicando ID inexistente } List _images = []; -Map> categoryImagesMap = {}; class AddElectricalLineScreen extends StatefulWidget { final String areaName; @@ -31,6 +38,8 @@ class AddElectricalLineScreen extends StatefulWidget { final int localId; final int systemId; final int areaId; + final int? electricalLineId; + final bool isEdit; const AddElectricalLineScreen({ super.key, @@ -39,42 +48,122 @@ class AddElectricalLineScreen extends StatefulWidget { required this.localName, required this.localId, required this.areaId, + this.electricalLineId, + this.isEdit = false, }); @override - _AddEquipmentScreenState createState() => _AddEquipmentScreenState(); + _AddElectricalLineScreenState createState() => + _AddElectricalLineScreenState(); } -class _AddEquipmentScreenState extends State { +class _AddElectricalLineScreenState extends State { EquipmentService equipmentService = EquipmentService(); + EletricalLineEquipmentService electricalLineService = + EletricalLineEquipmentService(); EquipmentPhotoService equipmentPhotoService = EquipmentPhotoService(); PersonalEquipmentCategoryService personalEquipmentCategoryService = PersonalEquipmentCategoryService(); GenericEquipmentCategoryService genericEquipmentCategoryService = GenericEquipmentCategoryService(); - - final _equipmentQuantityController = TextEditingController(); + final TextEditingController _quantity = TextEditingController(); + final TextEditingController _observationsController = TextEditingController(); String? _selectedType; - String? _selectedTypeToDelete; - String? _newEquipmentTypeName; int? _selectedGenericEquipmentCategoryId; int? _selectedPersonalEquipmentCategoryId; bool _isPersonalEquipmentCategorySelected = false; - + String? _newEquipmentTypeName; + String? _selectedTypeToDelete; + int? equipmentId; List> genericEquipmentTypes = []; List> personalEquipmentTypes = []; Map personalEquipmentMap = {}; + EletricalLineResponseModel? electricalLineResponseModel; @override void initState() { super.initState(); _fetchEquipmentCategory(); + if (widget.isEdit && widget.electricalLineId != null) { + _initializeData(widget.electricalLineId!); + } } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _fetchEquipmentCategory(); + Future _initializeData(int electricalLineId) async { + try { + await _fetchElectricalLineEquipment(electricalLineId); + + if (electricalLineResponseModel != null) { + setState(() { + equipmentId = electricalLineResponseModel!.equipment; + _quantity.text = electricalLineResponseModel!.quantity.toString(); + _observationsController.text = + electricalLineResponseModel!.observation ?? ''; + }); + + _fetchEquipmentDetails(electricalLineResponseModel!.equipment); + _fetchExistingPhotos(electricalLineResponseModel!.equipment); + } + } catch (error) { + print('Error: $error'); + } + } + + Future _fetchElectricalLineEquipment(int electricalLineId) async { + electricalLineResponseModel = + await electricalLineService.getEletricalLineById(electricalLineId); + } + + Future _fetchEquipmentDetails(int equipmentId) async { + try { + final equipmentDetails = + await equipmentService.getEquipmentById(equipmentId); + + setState(() { + _isPersonalEquipmentCategorySelected = + equipmentDetails['personal_equipment_category'] != null; + + if (_isPersonalEquipmentCategorySelected) { + _selectedPersonalEquipmentCategoryId = + equipmentDetails['personal_equipment_category']; + _selectedType = personalEquipmentTypes.firstWhere((element) => + element['id'] == _selectedPersonalEquipmentCategoryId)['name'] + as String; + } else { + _selectedGenericEquipmentCategoryId = + equipmentDetails['generic_equipment_category']; + _selectedType = genericEquipmentTypes.firstWhere((element) => + element['id'] == _selectedGenericEquipmentCategoryId)['name'] + as String; + } + }); + } catch (e) { + print('Erro ao buscar detalhes do equipamento: $e'); + } + } + + void _fetchExistingPhotos(int equipmentId) async { + try { + List photos = + await equipmentPhotoService.getPhotosByEquipmentId(equipmentId); + + List imageList = []; + + for (var photo in photos) { + File imageFile = await photo.toFile(); + imageList.add(ImageData( + id: photo.id, + imageFile: imageFile, + description: photo.description ?? '', + )); + } + + setState(() { + _images = imageList; + }); + } catch (e) { + print('Erro ao buscar fotos existentes: $e'); + } } Future _fetchEquipmentCategory() async { @@ -104,8 +193,9 @@ class _AddEquipmentScreenState extends State { @override void dispose() { - _equipmentQuantityController.dispose(); - categoryImagesMap[widget.systemId]?.clear(); + _quantity.dispose(); + _images.clear(); + _observationsController.dispose(); super.dispose(); } @@ -122,8 +212,9 @@ class _AddEquipmentScreenState extends State { } void _showImageDialog(File imageFile, {ImageData? existingImage}) { - TextEditingController descriptionController = - TextEditingController(text: existingImage?.description ?? ''); + TextEditingController descriptionController = TextEditingController( + text: existingImage?.description ?? '', + ); showDialog( context: context, builder: (BuildContext context) { @@ -151,17 +242,17 @@ class _AddEquipmentScreenState extends State { child: const Text('Salvar'), onPressed: () { setState(() { + String? description = descriptionController.text.isEmpty + ? null + : descriptionController.text; if (existingImage != null) { - existingImage.description = descriptionController.text; + existingImage.description = description ?? ''; } else { - final imageData = - ImageData(imageFile, descriptionController.text); - final systemId = widget.systemId; - if (!categoryImagesMap.containsKey(systemId)) { - categoryImagesMap[systemId] = []; - } - categoryImagesMap[systemId]!.add(imageData); - _images = categoryImagesMap[systemId]!; + final imageData = ImageData( + imageFile: imageFile, + description: description ?? '', + ); + _images.add(imageData); } }); Navigator.of(context).pop(); @@ -173,17 +264,17 @@ class _AddEquipmentScreenState extends State { ); } - void _addNewElectricalType() { + void _addNewEquipmentType() { TextEditingController typeController = TextEditingController(); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text('Adicionar novo tipo de linha elétrica'), + title: const Text('Adicionar novo tipo de equipamento'), content: TextField( controller: typeController, decoration: const InputDecoration( - hintText: 'Digite o novo tipo de linha elétrica'), + hintText: 'Digite o novo tipo de equipamento'), ), actions: [ TextButton( @@ -216,6 +307,122 @@ class _AddEquipmentScreenState extends State { ); } + void _updateEquipment() async { + int? genericEquipmentCategory; + int? personalEquipmentCategory; + + if (_isPersonalEquipmentCategorySelected) { + genericEquipmentCategory = null; + personalEquipmentCategory = _selectedPersonalEquipmentCategoryId; + } else { + genericEquipmentCategory = _selectedGenericEquipmentCategoryId; + personalEquipmentCategory = null; + } + + final Map equipmentTypeUpdate = { + "generic_equipment_category": genericEquipmentCategory, + "personal_equipment_category": personalEquipmentCategory, + }; + + print('Quantity: ${_quantity.text}'); + print('Equipment Type Update: $equipmentTypeUpdate'); + + bool typeUpdateSuccess = await equipmentService.updateEquipment( + equipmentId!, equipmentTypeUpdate); + + if (typeUpdateSuccess) { + final EletricalLineRequestModel electricalLineModel = + EletricalLineRequestModel( + area: widget.areaId, + system: widget.systemId, + quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + ); + + print('Electrical Line Model: ${electricalLineModel.toJson()}'); + + bool electricalLineUpdateSuccess = await electricalLineService + .updateEletricalLine(widget.electricalLineId!, electricalLineModel); + + if (electricalLineUpdateSuccess) { + await Future.wait(_images + .where((imageData) => imageData.toDelete) + .map((imageData) async { + await equipmentPhotoService.deletePhoto(imageData.id); + })); + + await Future.wait(_images + .where((imageData) => !imageData.toDelete) + .map((imageData) async { + if (imageData.id == -1) { + // Verifique se o ID é -1 (não atribuído) + await equipmentPhotoService.createPhoto( + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } else { + await equipmentPhotoService.updatePhoto( + imageData.id, + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } + })); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Detalhes do equipamento atualizados com sucesso.'), + backgroundColor: Colors.green, + ), + ); + Navigator.pushReplacementNamed( + context, + '/electricalLineList', + arguments: { + 'areaName': widget.areaName, + 'systemId': widget.systemId, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + setState(() { + _quantity.clear(); + _selectedType = null; + _selectedPersonalEquipmentCategoryId = null; + _selectedGenericEquipmentCategoryId = null; + _images.clear(); + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar os detalhes do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar o tipo do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } + Future _registerPersonalEquipmentType() async { int systemId = widget.systemId; PersonalEquipmentCategoryRequestModel personalEquipmentTypeRequestModel = @@ -228,7 +435,8 @@ class _AddEquipmentScreenState extends State { if (id != -1) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento registrado com sucesso.'), + content: + Text('Equipamento de linha elétrica registrado com sucesso.'), backgroundColor: Colors.green, ), ); @@ -243,14 +451,14 @@ class _AddEquipmentScreenState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao registrar o equipamento.'), + content: Text('Falha ao registrar o equipamento de linha elétrica.'), backgroundColor: Colors.red, ), ); } } - void _deleteElectricalType() async { + void _deleteEquipmentType() async { if (personalEquipmentTypes.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -271,7 +479,7 @@ class _AddEquipmentScreenState extends State { return; } - int equipmentId = personalEquipmentMap[_selectedTypeToDelete]!; + int personalCategoryId = personalEquipmentMap[_selectedTypeToDelete]!; showDialog( context: context, @@ -292,13 +500,14 @@ class _AddEquipmentScreenState extends State { onPressed: () async { Navigator.of(context).pop(); bool success = await personalEquipmentCategoryService - .deletePersonalEquipmentCategory(equipmentId); + .deletePersonalEquipmentCategory(personalCategoryId); if (success) { setState(() { personalEquipmentTypes.removeWhere( (element) => element['name'] == _selectedTypeToDelete); _selectedTypeToDelete = null; + _selectedType = null; _fetchEquipmentCategory(); }); ScaffoldMessenger.of(context).showSnackBar( @@ -324,7 +533,8 @@ class _AddEquipmentScreenState extends State { } void _showConfirmationDialog() { - if (_equipmentQuantityController.text.isEmpty || _selectedType == null) { + if (_quantity.text.isEmpty || + (_selectedType == null && _newEquipmentTypeName == null)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Por favor, preencha todos os campos.'), @@ -343,16 +553,22 @@ class _AddEquipmentScreenState extends State { children: [ const Text('Tipo:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_selectedType ?? ''), + Text(_selectedType ?? _newEquipmentTypeName ?? ''), const SizedBox(height: 10), const Text('Quantidade:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentQuantityController.text), + Text(_quantity.text), + const SizedBox(height: 10), + const Text('Observações:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_observationsController.text), const SizedBox(height: 10), const Text('Imagens:', style: TextStyle(fontWeight: FontWeight.bold)), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Padding( padding: const EdgeInsets.all(4.0), child: GestureDetector( @@ -384,9 +600,16 @@ class _AddEquipmentScreenState extends State { }, ), TextButton( - child: const Text('Adicionar'), + child: widget.isEdit + ? const Text('Atualizar') + : const Text('Adicionar'), onPressed: () { - _registerEquipment(); + if (widget.isEdit) { + _updateEquipment(); + } else { + _registerEquipment(); + } + Navigator.of(context).pop(); }, ), ], @@ -411,6 +634,10 @@ class _AddEquipmentScreenState extends State { EletricalLineRequestModel( area: widget.areaId, system: widget.systemId, + quantity: int.tryParse(_quantity.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); final EletricalLineEquipmentRequestModel electricalLineEquipmentDetail = @@ -420,16 +647,22 @@ class _AddEquipmentScreenState extends State { eletricalLineRequestModel: electricalLineModel, ); - int? equipmentId = await equipmentService + int? id = await equipmentService .createElectricalLine(electricalLineEquipmentDetail); + setState(() { + equipmentId = id; + }); - if (equipmentId != null) { + if (equipmentId != null && equipmentId != 0) { + print('Registering photos for equipment ID: $equipmentId'); await Future.wait(_images.map((imageData) async { + print('Creating photo with description: "${imageData.description}"'); await equipmentPhotoService.createPhoto( EquipmentPhotoRequestModel( photo: imageData.imageFile, - description: imageData.description, - equipment: equipmentId, + description: + imageData.description.isEmpty ? null : imageData.description, + equipment: equipmentId!, ), ); })); @@ -452,11 +685,12 @@ class _AddEquipmentScreenState extends State { }, ); setState(() { - _equipmentQuantityController.clear(); + _quantity.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -468,6 +702,13 @@ class _AddEquipmentScreenState extends State { } } + Future _deletePhoto(int photoId) async { + setState(() { + _images.firstWhere((imageData) => imageData.id == photoId).toDelete = + true; + }); + } + @override Widget build(BuildContext context) { List> combinedTypes = [ @@ -483,11 +724,12 @@ class _AddEquipmentScreenState extends State { icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () { setState(() { - _equipmentQuantityController.clear(); + _quantity.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); Navigator.pushReplacementNamed( context, @@ -515,9 +757,12 @@ class _AddEquipmentScreenState extends State { borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)), ), - child: const Center( - child: Text('Adicionar equipamento', - style: TextStyle( + child: Center( + child: Text( + widget.electricalLineId == null + ? 'Adicionar Equipamento' + : 'Editar Equipamento', + style: const TextStyle( fontSize: 26, fontWeight: FontWeight.bold, color: Colors.white)), @@ -528,7 +773,7 @@ class _AddEquipmentScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Tipos de instalações elétricas', + const Text('Tipos de instalação elétrica', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -549,7 +794,7 @@ class _AddEquipmentScreenState extends State { value: _selectedType, onChanged: (newValue) { if (newValue != - 'Selecione o tipo de linha elétrica') { + 'Selecione o tipo de instalação elétrica') { setState(() { _selectedType = newValue; Map selected = @@ -578,7 +823,7 @@ class _AddEquipmentScreenState extends State { children: [ IconButton( icon: const Icon(Icons.add), - onPressed: _addNewElectricalType, + onPressed: _addNewEquipmentType, ), IconButton( icon: const Icon(Icons.delete), @@ -603,7 +848,6 @@ class _AddEquipmentScreenState extends State { ), ], ), - const SizedBox(height: 8), TextButton( onPressed: () { setState(() { @@ -612,7 +856,6 @@ class _AddEquipmentScreenState extends State { }, child: const Text('Limpar seleção'), ), - const SizedBox(height: 30), const Text('Quantidade', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), @@ -623,7 +866,7 @@ class _AddEquipmentScreenState extends State { borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _equipmentQuantityController, + controller: _quantity, keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly @@ -633,6 +876,46 @@ class _AddEquipmentScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 15), ), + onChanged: (value) { + print('Quantity changed: $value'); + }, + ), + ), + const SizedBox(height: 15), + const Text('Observações', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _observationsController, + maxLines: 3, + maxLength: 500, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: + 'Digite suas observações aqui (máx 500 caracteres)', + ), + buildCounter: (BuildContext context, + {required int currentLength, + required bool isFocused, + required int? maxLength}) { + return Text( + '$currentLength / $maxLength', + style: TextStyle( + fontSize: 12, + color: currentLength > maxLength! + ? Colors.red + : Colors.grey, + ), + ); + }, ), ), const SizedBox(height: 15), @@ -641,7 +924,9 @@ class _AddEquipmentScreenState extends State { onPressed: _pickImage, ), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Stack( alignment: Alignment.topRight, children: [ @@ -662,10 +947,7 @@ class _AddEquipmentScreenState extends State { icon: const Icon(Icons.remove_circle, color: AppColors.warn), onPressed: () { - setState(() { - _images.removeWhere( - (element) => element.id == imageData.id); - }); + _deletePhoto(imageData.id); }, ), ], @@ -687,9 +969,11 @@ class _AddEquipmentScreenState extends State { borderRadius: BorderRadius.circular(10), ))), onPressed: _showConfirmationDialog, - child: const Text( - 'ADICIONAR EQUIPAMENTO', - style: TextStyle( + child: Text( + widget.isEdit + ? 'ATUALIZAR EQUIPAMENTO' + : 'ADICIONAR EQUIPAMENTO', + style: const TextStyle( fontSize: 17, fontWeight: FontWeight.bold), ), ), @@ -708,12 +992,12 @@ class _AddEquipmentScreenState extends State { context: context, builder: (BuildContext context) { return AlertDialog( - title: const Text('Excluir tipo de linha elétrica'), + title: const Text('Excluir tipo de equipamento'), content: Column( mainAxisSize: MainAxisSize.min, children: [ const Text( - 'Selecione um tipo de linha elétrica para excluir:', + 'Selecione um equipamento para excluir:', textAlign: TextAlign.center, ), StatefulBuilder( @@ -753,7 +1037,7 @@ class _AddEquipmentScreenState extends State { onPressed: () { if (_selectedTypeToDelete != null) { Navigator.of(context).pop(); - _deleteElectricalType(); + _deleteEquipmentType(); } }, ), @@ -785,13 +1069,14 @@ class _AddEquipmentScreenState extends State { items: items.map>((Map value) { return DropdownMenuItem( value: value['name'] as String, - enabled: value['name'] != 'Selecione o tipo de linha elétrica', + enabled: value['name'] != 'Selecione o tipo de instalação elétrica', child: Text( value['name'] as String, style: TextStyle( - color: value['name'] == 'Selecione o tipo de linha elétrica' - ? Colors.grey - : Colors.black, + color: + value['name'] == 'Selecione o tipo de instalação elétrica' + ? Colors.grey + : Colors.black, ), ), ); diff --git a/frontend/sige_ie/lib/equipments/feature/electrical_line/electrical_line_list.dart b/frontend/sige_ie/lib/equipments/feature/electrical_line/electrical_line_list.dart index 12c57e90..ea4d7ae1 100644 --- a/frontend/sige_ie/lib/equipments/feature/electrical_line/electrical_line_list.dart +++ b/frontend/sige_ie/lib/equipments/feature/electrical_line/electrical_line_list.dart @@ -27,39 +27,38 @@ class ListElectricalLineEquipment extends StatefulWidget { class _ListElectricalLineEquipmentState extends State { - List equipmentList = []; - bool isLoading = true; - final EletricalLineEquipmentService _service = + late Future> + _electricalLineList; + final EletricalLineEquipmentService _electricalLineService = EletricalLineEquipmentService(); + final GlobalKey _scaffoldMessengerKey = + GlobalKey(); + @override void initState() { super.initState(); - fetchEquipmentList(); + _electricalLineList = + _electricalLineService.getEletricalLineListByArea(widget.areaId); } - Future fetchEquipmentList() async { - try { - final List equipmentList = - await _service.getEletricalLineListByArea(widget.areaId); - if (mounted) { - setState(() { -/* this.equipmentList = equipmentList; - */ - isLoading = false; - }); - } - } catch (e) { - print('Error fetching equipment list: $e'); - if (mounted) { - setState(() { - isLoading = false; - }); - } - } + void navigateToAddElectricalLine(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddElectricalLineScreen( + areaName: widget.areaName, + systemId: widget.systemId, + localName: widget.localName, + localId: widget.localId, + areaId: widget.areaId, + electricalLineId: null, + ), + ), + ); } - void navigateToAddEquipment(BuildContext context) { + void _editElectricalLine(BuildContext context, int electricalLineId) { Navigator.push( context, MaterialPageRoute( @@ -69,141 +68,190 @@ class _ListElectricalLineEquipmentState localName: widget.localName, localId: widget.localId, areaId: widget.areaId, + electricalLineId: electricalLineId, + isEdit: true, ), ), ); } - void _editEquipment(BuildContext context, String equipment) { - // Implement the logic to edit the equipment + Future _deleteElectricalLine( + BuildContext context, int electricalLineId) async { + try { + await _electricalLineService.deleteEletricalLine(electricalLineId); + setState(() { + _electricalLineList = + _electricalLineService.getEletricalLineListByArea(widget.areaId); + }); + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Equipamento deletado com sucesso'), + backgroundColor: Colors.green, + ), + ); + } catch (e) { + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Falha ao deletar o equipamento'), + backgroundColor: Colors.red, + ), + ); + } } - void _deleteEquipment(BuildContext context, String equipment) { - // Implement the logic to delete the equipment + void _confirmDelete(BuildContext context, int electricalLineId) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirmar Exclusão'), + content: const Text( + 'Você tem certeza que deseja excluir este equipamento?'), + actions: [ + TextButton( + child: const Text('Cancelar'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Excluir'), + onPressed: () { + Navigator.of(context).pop(); + _deleteElectricalLine(context, electricalLineId); + }, + ), + ], + ); + }, + ); } @override Widget build(BuildContext context) { String systemTitle = 'Instalações Elétricas'; - return Scaffold( - appBar: AppBar( - backgroundColor: AppColors.sigeIeBlue, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pushReplacementNamed( - context, - '/systemLocation', - arguments: { - 'areaName': widget.areaName, - 'localName': widget.localName, - 'localId': widget.localId, - 'areaId': widget.areaId, - }, - ); - }, + return ScaffoldMessenger( + key: _scaffoldMessengerKey, + child: Scaffold( + appBar: AppBar( + backgroundColor: AppColors.sigeIeBlue, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () { + Navigator.pushReplacementNamed( + context, + '/systemLocation', + arguments: { + 'areaName': widget.areaName, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + }, + ), ), - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), - decoration: const BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: - BorderRadius.vertical(bottom: Radius.circular(20)), - ), - child: Center( - child: Text( - '${widget.areaName} - $systemTitle', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: AppColors.lightText, + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), + decoration: const BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: + BorderRadius.vertical(bottom: Radius.circular(20)), + ), + child: Center( + child: Text( + '${widget.areaName} - $systemTitle', + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: AppColors.lightText, + ), ), ), ), - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : equipmentList.isNotEmpty - ? Column( - children: equipmentList.map((equipment) { - return Container( - margin: - const EdgeInsets.symmetric(vertical: 5), - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - children: [ - Expanded( - child: Padding( - padding: - const EdgeInsets.only(left: 10), - child: Text( - equipment, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ), - ), - IconButton( - icon: const Icon(Icons.edit, - color: Colors.blue), - onPressed: () => _editEquipment( - context, equipment), - ), - IconButton( - icon: const Icon(Icons.delete, - color: Colors.red), - onPressed: () => _deleteEquipment( - context, equipment), - ), - ], - ), - ), - ); - }).toList(), - ) - : const Center( - child: Text( - 'Você ainda não tem equipamentos', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black54, + const SizedBox(height: 20), + FutureBuilder>( + future: _electricalLineList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Erro: ${snapshot.error}')); + } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var electricalLineEquipment = snapshot.data![index]; + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + title: Text( + electricalLineEquipment.equipmentCategory, + style: const TextStyle( + color: AppColors.lightText, + fontWeight: FontWeight.bold), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit, + color: Colors.blue), + onPressed: () => _editElectricalLine( + context, electricalLineEquipment.id), + ), + IconButton( + icon: const Icon(Icons.delete, + color: Colors.red), + onPressed: () => _confirmDelete( + context, electricalLineEquipment.id), ), - ), + ], ), - const SizedBox(height: 40), - ], + ), + ); + }, + ); + } else { + return const Padding( + padding: EdgeInsets.all(10.0), + child: Center( + child: Text( + 'Nenhum equipamento encontrado.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black54), + ), + ), + ); + } + }, ), - ), - ], + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => navigateToAddElectricalLine(context), + backgroundColor: AppColors.sigeIeYellow, + child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), - ), - floatingActionButton: FloatingActionButton( - onPressed: () => navigateToAddEquipment(context), - backgroundColor: AppColors.sigeIeYellow, - child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), ); } From 9c05a9f6e5589f67057b137d55e350487d50d891 Mon Sep 17 00:00:00 2001 From: EngDann Date: Sat, 17 Aug 2024 16:48:52 -0300 Subject: [PATCH 09/15] Distribution Board completo e integrado --- .../distribution_equipment_request_model.dart | 2 +- .../distribution_request_model.dart | 13 +- .../distribution_response_by_area_model.dart | 45 +- .../distribution_response_model.dart | 58 ++- .../distribution/distribution_service.dart | 2 + .../add_distribuition_board.dart | 493 +++++++++++++++--- .../distribuition_board_equipment_list.dart | 321 +++++++----- 7 files changed, 650 insertions(+), 284 deletions(-) diff --git a/frontend/sige_ie/lib/equipments/data/distribution/distribution_equipment_request_model.dart b/frontend/sige_ie/lib/equipments/data/distribution/distribution_equipment_request_model.dart index dc3aae08..fbbaf814 100644 --- a/frontend/sige_ie/lib/equipments/data/distribution/distribution_equipment_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/distribution/distribution_equipment_request_model.dart @@ -15,7 +15,7 @@ class DistributionEquipmentRequestModel { return { 'generic_equipment_category': genericEquipmentCategory, 'personal_equipment_category': personalEquipmentCategory, - 'structured_cabling_equipment': distributionRequestModel?.toJson(), + 'distribution_board_equipment': distributionRequestModel?.toJson(), }; } } diff --git a/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart b/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart index 09e313da..52cc4eac 100644 --- a/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/distribution/distribution_request_model.dart @@ -1,14 +1,14 @@ class DistributionRequestModel { - /* Obs e power = int? */ int? area; int? system; int? quantity; int? power; - bool dr; - bool dps; - bool grounding; + bool? dr; + bool? dps; + bool? grounding; String? typeMaterial; String? methodInstallation; + String? observation; DistributionRequestModel( {required this.area, @@ -19,7 +19,9 @@ class DistributionRequestModel { required this.dps, required this.grounding, required this.typeMaterial, - required this.methodInstallation}); + required this.methodInstallation, + required this.observation}); + Map toJson() { return { 'area': area, @@ -31,6 +33,7 @@ class DistributionRequestModel { 'grounding': grounding, 'type_material': typeMaterial, 'method_installation': methodInstallation, + 'observation': observation }; } } diff --git a/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_by_area_model.dart index cac85dd7..0bfe0e03 100644 --- a/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_by_area_model.dart @@ -4,39 +4,26 @@ class DistributionEquipmentResponseByAreaModel { String equipmentCategory; int system; int quantity; - String? power; - bool dr; - bool dps; - bool grounding; - String? typeMaterial; - String? methodInstallation; + String? observation; - DistributionEquipmentResponseByAreaModel( - {required this.id, - required this.area, - required this.equipmentCategory, - required this.system, - required this.quantity, - required this.power, - required this.dr, - required this.dps, - required this.grounding, - required this.typeMaterial, - required this.methodInstallation}); + DistributionEquipmentResponseByAreaModel({ + required this.id, + required this.area, + required this.equipmentCategory, + required this.system, + required this.quantity, + required this.observation, + }); factory DistributionEquipmentResponseByAreaModel.fromJson( Map json) { return DistributionEquipmentResponseByAreaModel( - id: json['id'], - area: json['area'], - equipmentCategory: json['equipment_category'], - system: json['system'], - quantity: json['quantity'], - power: json['power'], - dr: json['dr'], - dps: json['dps'], - grounding: json['grounding'], - typeMaterial: json['typeMaterial'], - methodInstallation: json['methodInstallation']); + id: json['id'], + area: json['area'], + equipmentCategory: json['equipment_category'], + system: json['system'], + quantity: json['quantity'], + observation: json['observation'], + ); } } diff --git a/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_model.dart b/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_model.dart index 06e5cea5..5d6c58d5 100644 --- a/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/distribution/distribution_response_model.dart @@ -4,38 +4,44 @@ class DistributionResponseModel { int equipment; int system; int quantity; - String? power; + int power; bool dr; bool dps; bool grounding; - String? typeMaterial; - String? methodInstallation; + String typeMaterial; + String methodInstallation; + String? observation; - DistributionResponseModel( - {required this.id, - required this.area, - required this.equipment, - required this.system, - required this.quantity, - required this.power, - required this.dr, - required this.dps, - required this.grounding, - required this.typeMaterial, - required this.methodInstallation}); + DistributionResponseModel({ + required this.id, + required this.area, + required this.equipment, + required this.system, + required this.quantity, + required this.power, + required this.dr, + required this.dps, + required this.grounding, + required this.typeMaterial, + required this.methodInstallation, + this.observation, + }); factory DistributionResponseModel.fromJson(Map json) { return DistributionResponseModel( - id: json['id'], - area: json['area'], - equipment: json['equipment'], - system: json['system'], - quantity: json['quantity'], - power: json['power'], - dr: json['dr'], - dps: json['dps'], - grounding: json['grounding'], - typeMaterial: json['typeMaterial'], - methodInstallation: json['methodInstallation']); + id: json['id'] ?? 0, + area: json['area'] ?? 0, + equipment: json['equipment'] ?? 0, + system: json['system'] ?? 0, + quantity: json['quantity'] ?? 0, + power: json['power'] ?? 0, + dr: json['dr'] ?? false, + dps: json['dps'] ?? false, + grounding: json['grounding'] ?? false, + typeMaterial: json['type_material'] ?? '', // Corrigido para type_material + methodInstallation: json['method_installation'] ?? + '', // Corrigido para method_installation + observation: json['observation'], // Nullable, sem valor padrão + ); } } diff --git a/frontend/sige_ie/lib/equipments/data/distribution/distribution_service.dart b/frontend/sige_ie/lib/equipments/data/distribution/distribution_service.dart index 40a4ff8c..3fa83eaf 100644 --- a/frontend/sige_ie/lib/equipments/data/distribution/distribution_service.dart +++ b/frontend/sige_ie/lib/equipments/data/distribution/distribution_service.dart @@ -21,6 +21,8 @@ class DistributionEquipmentService { var url = Uri.parse('${baseUrl}distribution-boards/by-area/$areaId/'); try { var response = await client.get(url); + print('Status Code: ${response.statusCode}'); + print('Response Body: ${response.body}'); if (response.statusCode == 200) { List dataList = jsonDecode(response.body); diff --git a/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart b/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart index ff28b84f..7511062f 100644 --- a/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart +++ b/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart @@ -1,13 +1,15 @@ import 'dart:io'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:sige_ie/config/app_styles.dart'; import 'package:sige_ie/equipments/data/distribution/distribution_equipment_request_model.dart'; import 'package:sige_ie/equipments/data/distribution/distribution_request_model.dart'; +import 'package:sige_ie/equipments/data/distribution/distribution_response_model.dart'; +import 'package:sige_ie/equipments/data/distribution/distribution_service.dart'; import 'package:sige_ie/equipments/data/equipment_service.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_request_model.dart'; +import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_response_model.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_service.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_response_model.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_service.dart'; @@ -18,12 +20,17 @@ class ImageData { int id; File imageFile; String description; + bool toDelete; - ImageData(this.imageFile, this.description) : id = Random().nextInt(1000000); + ImageData({ + int? id, + required this.imageFile, + required this.description, + this.toDelete = false, + }) : id = id ?? -1; // Valor padrão indicando ID inexistente } List _images = []; -Map> categoryImagesMap = {}; class AddDistribuitionBoard extends StatefulWidget { final String areaName; @@ -31,6 +38,8 @@ class AddDistribuitionBoard extends StatefulWidget { final int localId; final int systemId; final int areaId; + final int? distributionBoardId; + final bool isEdit; const AddDistribuitionBoard({ super.key, @@ -39,6 +48,8 @@ class AddDistribuitionBoard extends StatefulWidget { required this.localName, required this.localId, required this.areaId, + this.distributionBoardId, + this.isEdit = false, }); @override @@ -47,17 +58,21 @@ class AddDistribuitionBoard extends StatefulWidget { class _AddDistribuitionBoardState extends State { EquipmentService equipmentService = EquipmentService(); + DistributionEquipmentService distributionService = + DistributionEquipmentService(); EquipmentPhotoService equipmentPhotoService = EquipmentPhotoService(); PersonalEquipmentCategoryService personalEquipmentCategoryService = PersonalEquipmentCategoryService(); GenericEquipmentCategoryService genericEquipmentCategoryService = GenericEquipmentCategoryService(); - final _equipmentChargeController = TextEditingController(); - final _equipmentQuantityController = TextEditingController(); - final _powerController = TextEditingController(); - final _typeMaterialController = TextEditingController(); - final _methodInstallationController = TextEditingController(); + final TextEditingController _equipmentQuantityController = + TextEditingController(); + final TextEditingController _powerController = TextEditingController(); + final TextEditingController _typeMaterialController = TextEditingController(); + final TextEditingController _methodInstallationController = + TextEditingController(); + final TextEditingController _observationsController = TextEditingController(); bool _dr = false; bool _dps = false; bool _grounding = false; @@ -67,21 +82,114 @@ class _AddDistribuitionBoardState extends State { int? _selectedGenericEquipmentCategoryId; int? _selectedPersonalEquipmentCategoryId; bool _isPersonalEquipmentCategorySelected = false; - + int? equipmentId; List> genericEquipmentTypes = []; List> personalEquipmentTypes = []; Map personalEquipmentMap = {}; + DistributionResponseModel? distributionResponseModel; @override void initState() { super.initState(); _fetchEquipmentCategory(); + if (widget.isEdit && widget.distributionBoardId != null) { + _initializeData(widget.distributionBoardId!); + } } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _fetchEquipmentCategory(); + Future _initializeData(int distributionBoardId) async { + try { + await _fetchDistributionBoardEquipment(distributionBoardId); + + if (distributionResponseModel != null) { + setState(() { + equipmentId = distributionResponseModel!.equipment; + _equipmentQuantityController.text = + distributionResponseModel!.quantity.toString(); + _powerController.text = distributionResponseModel!.power.toString(); + _typeMaterialController.text = + distributionResponseModel!.typeMaterial; + _methodInstallationController.text = + distributionResponseModel!.methodInstallation; + _observationsController.text = + distributionResponseModel!.observation ?? ''; + _dr = distributionResponseModel!.dr; + _dps = distributionResponseModel!.dps; + _grounding = distributionResponseModel!.grounding; + }); + + _fetchEquipmentDetails(distributionResponseModel!.equipment); + _fetchExistingPhotos(distributionResponseModel!.equipment); + } + } catch (error) { + print('Error: $error'); + } + } + + Future _fetchDistributionBoardEquipment(int distributionBoardId) async { + print(distributionBoardId); + try { + distributionResponseModel = + await distributionService.getDistributionById(distributionBoardId); + if (distributionResponseModel == null) { + throw Exception('Distribuição não encontrada'); + } + } catch (e) { + print('Error: $e'); + throw Exception('Failed to load distribution boards'); + } + } + + Future _fetchEquipmentDetails(int equipmentId) async { + try { + final equipmentDetails = + await equipmentService.getEquipmentById(equipmentId); + + setState(() { + _isPersonalEquipmentCategorySelected = + equipmentDetails['personal_equipment_category'] != null; + + if (_isPersonalEquipmentCategorySelected) { + _selectedPersonalEquipmentCategoryId = + equipmentDetails['personal_equipment_category']; + _selectedType = personalEquipmentTypes.firstWhere((element) => + element['id'] == _selectedPersonalEquipmentCategoryId)['name'] + as String; + } else { + _selectedGenericEquipmentCategoryId = + equipmentDetails['generic_equipment_category']; + _selectedType = genericEquipmentTypes.firstWhere((element) => + element['id'] == _selectedGenericEquipmentCategoryId)['name'] + as String; + } + }); + } catch (e) { + print('Erro ao buscar detalhes do equipamento: $e'); + } + } + + void _fetchExistingPhotos(int equipmentId) async { + try { + List photos = + await equipmentPhotoService.getPhotosByEquipmentId(equipmentId); + + List imageList = []; + + for (var photo in photos) { + File imageFile = await photo.toFile(); + imageList.add(ImageData( + id: photo.id, + imageFile: imageFile, + description: photo.description ?? '', + )); + } + + setState(() { + _images = imageList; + }); + } catch (e) { + print('Erro ao buscar fotos existentes: $e'); + } } Future _fetchEquipmentCategory() async { @@ -111,12 +219,12 @@ class _AddDistribuitionBoardState extends State { @override void dispose() { - _equipmentChargeController.dispose(); _equipmentQuantityController.dispose(); _powerController.dispose(); _typeMaterialController.dispose(); _methodInstallationController.dispose(); - categoryImagesMap[widget.systemId]?.clear(); + _observationsController.dispose(); + _images.clear(); super.dispose(); } @@ -133,8 +241,9 @@ class _AddDistribuitionBoardState extends State { } void _showImageDialog(File imageFile, {ImageData? existingImage}) { - TextEditingController descriptionController = - TextEditingController(text: existingImage?.description ?? ''); + TextEditingController descriptionController = TextEditingController( + text: existingImage?.description ?? '', + ); showDialog( context: context, builder: (BuildContext context) { @@ -162,17 +271,17 @@ class _AddDistribuitionBoardState extends State { child: const Text('Salvar'), onPressed: () { setState(() { + String? description = descriptionController.text.isEmpty + ? null + : descriptionController.text; if (existingImage != null) { - existingImage.description = descriptionController.text; + existingImage.description = description ?? ''; } else { - final imageData = - ImageData(imageFile, descriptionController.text); - final systemId = widget.systemId; - if (!categoryImagesMap.containsKey(systemId)) { - categoryImagesMap[systemId] = []; - } - categoryImagesMap[systemId]!.add(imageData); - _images = categoryImagesMap[systemId]!; + final imageData = ImageData( + imageFile: imageFile, + description: description ?? '', + ); + _images.add(imageData); } }); Navigator.of(context).pop(); @@ -227,6 +336,125 @@ class _AddDistribuitionBoardState extends State { ); } + void _updateDistributionBoard() async { + int? genericEquipmentCategory; + int? personalEquipmentCategory; + + if (_isPersonalEquipmentCategorySelected) { + genericEquipmentCategory = null; + personalEquipmentCategory = _selectedPersonalEquipmentCategoryId; + } else { + genericEquipmentCategory = _selectedGenericEquipmentCategoryId; + personalEquipmentCategory = null; + } + + final Map equipmentTypeUpdate = { + "generic_equipment_category": genericEquipmentCategory, + "personal_equipment_category": personalEquipmentCategory, + }; + + bool typeUpdateSuccess = await equipmentService.updateEquipment( + equipmentId!, equipmentTypeUpdate); + + if (typeUpdateSuccess) { + final DistributionRequestModel distributionModel = + DistributionRequestModel( + area: widget.areaId, + system: widget.systemId, + quantity: int.tryParse(_equipmentQuantityController.text), + power: int.tryParse(_powerController.text), + dr: _dr, + dps: _dps, + grounding: _grounding, + typeMaterial: _typeMaterialController.text, + methodInstallation: _methodInstallationController.text, + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + ); + + bool distributionUpdateSuccess = await distributionService + .updateDistribution(widget.distributionBoardId!, distributionModel); + + if (distributionUpdateSuccess) { + await Future.wait(_images + .where((imageData) => imageData.toDelete) + .map((imageData) async { + await equipmentPhotoService.deletePhoto(imageData.id); + })); + + await Future.wait(_images + .where((imageData) => !imageData.toDelete) + .map((imageData) async { + if (imageData.id == -1) { + await equipmentPhotoService.createPhoto( + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } else { + await equipmentPhotoService.updatePhoto( + imageData.id, + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } + })); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Detalhes do quadro de distribuição atualizados com sucesso.'), + backgroundColor: Colors.green, + ), + ); + Navigator.pushReplacementNamed( + context, + '/listDistribuitionBoard', + arguments: { + 'areaName': widget.areaName, + 'systemId': widget.systemId, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + setState(() { + _equipmentQuantityController.clear(); + _powerController.clear(); + _typeMaterialController.clear(); + _methodInstallationController.clear(); + _observationsController.clear(); + _images.clear(); + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Falha ao atualizar os detalhes do quadro de distribuição.'), + backgroundColor: Colors.red, + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar o tipo do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } + Future _registerPersonalEquipmentType() async { int systemId = widget.systemId; PersonalEquipmentCategoryRequestModel personalEquipmentTypeRequestModel = @@ -239,7 +467,7 @@ class _AddDistribuitionBoardState extends State { if (id != -1) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento registrado com sucesso.'), + content: Text('Tipo de equipamento registrado com sucesso.'), backgroundColor: Colors.green, ), ); @@ -254,7 +482,7 @@ class _AddDistribuitionBoardState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao registrar o equipamento.'), + content: Text('Falha ao registrar o tipo de equipamento.'), backgroundColor: Colors.red, ), ); @@ -282,15 +510,15 @@ class _AddDistribuitionBoardState extends State { return; } - int equipmentId = personalEquipmentMap[_selectedTypeToDelete]!; + int personalCategoryId = personalEquipmentMap[_selectedTypeToDelete]!; showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Confirmar Exclusão'), - content: - const Text('Tem certeza de que deseja excluir este equipamento?'), + content: const Text( + 'Tem certeza de que deseja excluir este tipo de equipamento?'), actions: [ TextButton( child: const Text('Cancelar'), @@ -303,25 +531,27 @@ class _AddDistribuitionBoardState extends State { onPressed: () async { Navigator.of(context).pop(); bool success = await personalEquipmentCategoryService - .deletePersonalEquipmentCategory(equipmentId); + .deletePersonalEquipmentCategory(personalCategoryId); if (success) { setState(() { personalEquipmentTypes.removeWhere( (element) => element['name'] == _selectedTypeToDelete); _selectedTypeToDelete = null; + _selectedType = null; _fetchEquipmentCategory(); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento excluído com sucesso.'), + content: + Text('Tipo de equipamento excluído com sucesso.'), backgroundColor: Colors.green, ), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao excluir o equipamento.'), + content: Text('Falha ao excluir o tipo de equipamento.'), backgroundColor: Colors.red, ), ); @@ -335,12 +565,11 @@ class _AddDistribuitionBoardState extends State { } void _showConfirmationDialog() { - if (_equipmentChargeController.text.isEmpty || - _equipmentQuantityController.text.isEmpty || + if (_equipmentQuantityController.text.isEmpty || (_selectedType == null && _newEquipmentTypeName == null)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Por favor, preencha todos os campos.'), + content: Text('Por favor, preencha todos os campos obrigatórios.'), ), ); return; @@ -358,18 +587,44 @@ class _AddDistribuitionBoardState extends State { style: TextStyle(fontWeight: FontWeight.bold)), Text(_selectedType ?? _newEquipmentTypeName ?? ''), const SizedBox(height: 10), - const Text('Especificação:', - style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentChargeController.text), - const SizedBox(height: 10), const Text('Quantidade:', style: TextStyle(fontWeight: FontWeight.bold)), Text(_equipmentQuantityController.text), const SizedBox(height: 10), + const Text('Potência:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_powerController.text), + const SizedBox(height: 10), + const Text('Material:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_typeMaterialController.text), + const SizedBox(height: 10), + const Text('Método de Instalação:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_methodInstallationController.text), + const SizedBox(height: 10), + const Text('DR:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_dr ? 'Sim' : 'Não'), + const SizedBox(height: 10), + const Text('DPS:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_dps ? 'Sim' : 'Não'), + const SizedBox(height: 10), + const Text('Aterramento:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_grounding ? 'Sim' : 'Não'), + const SizedBox(height: 10), + const Text('Observações:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_observationsController.text), + const SizedBox(height: 10), const Text('Imagens:', style: TextStyle(fontWeight: FontWeight.bold)), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Padding( padding: const EdgeInsets.all(4.0), child: GestureDetector( @@ -401,9 +656,16 @@ class _AddDistribuitionBoardState extends State { }, ), TextButton( - child: const Text('Adicionar'), + child: widget.isEdit + ? const Text('Atualizar') + : const Text('Adicionar'), onPressed: () { - _registerEquipment(); + if (widget.isEdit) { + _updateDistributionBoard(); + } else { + _registerDistributionBoard(); + } + Navigator.of(context).pop(); }, ), ], @@ -412,7 +674,7 @@ class _AddDistribuitionBoardState extends State { ); } - void _registerEquipment() async { + void _registerDistributionBoard() async { int? genericEquipmentCategory; int? personalEquipmentCategory; @@ -427,13 +689,16 @@ class _AddDistribuitionBoardState extends State { final DistributionRequestModel distributionModel = DistributionRequestModel( area: widget.areaId, system: widget.systemId, - quantity: null, - power: null, - dr: true, - dps: true, - grounding: true, - typeMaterial: '', - methodInstallation: '', + quantity: int.tryParse(_equipmentQuantityController.text), + power: int.tryParse(_powerController.text), + dr: _dr, + dps: _dps, + grounding: _grounding, + typeMaterial: _typeMaterialController.text, + methodInstallation: _methodInstallationController.text, + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); final DistributionEquipmentRequestModel distributionEquipmentDetail = @@ -443,23 +708,32 @@ class _AddDistribuitionBoardState extends State { distributionRequestModel: distributionModel, ); - int? equipmentId = + int? id = await equipmentService.createDistribution(distributionEquipmentDetail); + setState(() { + equipmentId = id; + }); + + print('Response ID: $id'); - if (equipmentId != null) { + if (equipmentId != null && equipmentId != 0) { + print('Registering photos for equipment ID: $equipmentId'); await Future.wait(_images.map((imageData) async { + print('Creating photo with description: "${imageData.description}"'); await equipmentPhotoService.createPhoto( EquipmentPhotoRequestModel( photo: imageData.imageFile, - description: imageData.description, - equipment: equipmentId, + description: + imageData.description.isEmpty ? null : imageData.description, + equipment: equipmentId!, ), ); })); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Detalhes do equipamento registrados com sucesso.'), + content: Text( + 'Detalhes do quadro de distribuição registrados com sucesso.'), backgroundColor: Colors.green, ), ); @@ -475,29 +749,31 @@ class _AddDistribuitionBoardState extends State { }, ); setState(() { - _equipmentChargeController.clear(); _equipmentQuantityController.clear(); _powerController.clear(); _typeMaterialController.clear(); _methodInstallationController.clear(); - _dr = false; - _dps = false; - _grounding = false; - _selectedType = null; - _selectedPersonalEquipmentCategoryId = null; - _selectedGenericEquipmentCategoryId = null; + _observationsController.clear(); _images.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao registrar os detalhes do equipamento.'), + content: + Text('Falha ao registrar os detalhes do quadro de distribuição.'), backgroundColor: Colors.red, ), ); } } + Future _deletePhoto(int photoId) async { + setState(() { + _images.firstWhere((imageData) => imageData.id == photoId).toDelete = + true; + }); + } + @override Widget build(BuildContext context) { List> combinedTypes = [ @@ -513,14 +789,11 @@ class _AddDistribuitionBoardState extends State { icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () { setState(() { - _equipmentChargeController.clear(); _equipmentQuantityController.clear(); _powerController.clear(); _typeMaterialController.clear(); _methodInstallationController.clear(); - _dr = false; - _dps = false; - _grounding = false; + _observationsController.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; @@ -552,9 +825,12 @@ class _AddDistribuitionBoardState extends State { borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)), ), - child: const Center( - child: Text('Adicionar equipamento', - style: TextStyle( + child: Center( + child: Text( + widget.distributionBoardId == null + ? 'Adicionar Equipamento' + : 'Editar Equipamento', + style: const TextStyle( fontSize: 26, fontWeight: FontWeight.bold, color: Colors.white)), @@ -565,7 +841,7 @@ class _AddDistribuitionBoardState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Tipos de quadros de distribuição', + const Text('Tipos de Quadro de Distribuição', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -577,7 +853,7 @@ class _AddDistribuitionBoardState extends State { items: [ { 'name': - 'Selecione o tipo de quadro de distribuição', + 'Selecione o tipo de Quadro de Distribuição', 'id': -1, 'type': -1 } @@ -585,7 +861,8 @@ class _AddDistribuitionBoardState extends State { combinedTypes, value: _selectedType, onChanged: (newValue) { - if (newValue != 'Selecione o tipo de equipamento') { + if (newValue != + 'Selecione o tipo de Quadro de Distribuição') { setState(() { _selectedType = newValue; Map selected = @@ -647,7 +924,7 @@ class _AddDistribuitionBoardState extends State { }, child: const Text('Limpar seleção'), ), - const SizedBox(height: 8), + const SizedBox(height: 15), const Text('Quantidade', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), @@ -668,6 +945,9 @@ class _AddDistribuitionBoardState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 15), ), + onChanged: (value) { + print('Quantidade modificada: $value'); + }, ), ), const SizedBox(height: 15), @@ -682,6 +962,7 @@ class _AddDistribuitionBoardState extends State { ), child: TextField( controller: _powerController, + keyboardType: TextInputType.number, decoration: const InputDecoration( border: InputBorder.none, contentPadding: @@ -776,12 +1057,51 @@ class _AddDistribuitionBoardState extends State { ), ), const SizedBox(height: 15), + const Text('Observações', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _observationsController, + maxLines: 3, + maxLength: 500, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: + 'Digite suas observações aqui (máx 500 caracteres)', + ), + buildCounter: (BuildContext context, + {required int currentLength, + required bool isFocused, + required int? maxLength}) { + return Text( + '$currentLength / $maxLength', + style: TextStyle( + fontSize: 12, + color: currentLength > maxLength! + ? Colors.red + : Colors.grey, + ), + ); + }, + ), + ), + const SizedBox(height: 15), IconButton( icon: const Icon(Icons.camera_alt), onPressed: _pickImage, ), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Stack( alignment: Alignment.topRight, children: [ @@ -802,10 +1122,7 @@ class _AddDistribuitionBoardState extends State { icon: const Icon(Icons.remove_circle, color: AppColors.warn), onPressed: () { - setState(() { - _images.removeWhere( - (element) => element.id == imageData.id); - }); + _deletePhoto(imageData.id); }, ), ], @@ -827,9 +1144,11 @@ class _AddDistribuitionBoardState extends State { borderRadius: BorderRadius.circular(10), ))), onPressed: _showConfirmationDialog, - child: const Text( - 'ADICIONAR EQUIPAMENTO', - style: TextStyle( + child: Text( + widget.isEdit + ? 'ATUALIZAR EQUIPAMENTO' + : 'ADICIONAR EQUIPAMENTO', + style: const TextStyle( fontSize: 17, fontWeight: FontWeight.bold), ), ), @@ -925,11 +1244,13 @@ class _AddDistribuitionBoardState extends State { items: items.map>((Map value) { return DropdownMenuItem( value: value['name'] as String, - enabled: value['name'] != 'Selecione o tipo de equipamento', + enabled: + value['name'] != 'Selecione o tipo de Quadro de Distribuição', child: Text( value['name'] as String, style: TextStyle( - color: value['name'] == 'Selecione o tipo de equipamento' + color: value['name'] == + 'Selecione o tipo de Quadro de Distribuição' ? Colors.grey : Colors.black, ), diff --git a/frontend/sige_ie/lib/equipments/feature/distribuition_board/distribuition_board_equipment_list.dart b/frontend/sige_ie/lib/equipments/feature/distribuition_board/distribuition_board_equipment_list.dart index 5a638828..8354e5de 100644 --- a/frontend/sige_ie/lib/equipments/feature/distribuition_board/distribuition_board_equipment_list.dart +++ b/frontend/sige_ie/lib/equipments/feature/distribuition_board/distribuition_board_equipment_list.dart @@ -25,38 +25,36 @@ class ListDistributionBoard extends StatefulWidget { } class _ListDistributionBoardState extends State { - List equipmentList = []; - bool isLoading = true; + late Future> + _distributionBoardList; final DistributionEquipmentService _service = DistributionEquipmentService(); + final GlobalKey _scaffoldMessengerKey = + GlobalKey(); + @override void initState() { super.initState(); - fetchEquipmentList(); + _distributionBoardList = _service.getDistributionListByArea(widget.areaId); } - Future fetchEquipmentList() async { - try { - final List equipmentList = - await _service.getDistributionListByArea(widget.areaId); - if (mounted) { - setState(() { -/* this.equipmentList = equipmentList; - */ - isLoading = false; - }); - } - } catch (e) { - print('Error fetching equipment list: $e'); - if (mounted) { - setState(() { - isLoading = false; - }); - } - } + void navigateToAddDistributionBoard(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddDistribuitionBoard( + areaName: widget.areaName, + systemId: widget.systemId, + localName: widget.localName, + localId: widget.localId, + areaId: widget.areaId, + distributionBoardId: null, + ), + ), + ); } - void navigateToAddEquipment(BuildContext context) { + void _editDistributionBoard(BuildContext context, int distributionBoardId) { Navigator.push( context, MaterialPageRoute( @@ -66,141 +64,190 @@ class _ListDistributionBoardState extends State { localName: widget.localName, localId: widget.localId, areaId: widget.areaId, + distributionBoardId: distributionBoardId, + isEdit: true, ), ), ); } - void _editEquipment(BuildContext context, String equipment) { - // Implement the logic to edit the equipment + Future _deleteDistributionBoard( + BuildContext context, int distributionBoardId) async { + try { + await _service.deleteDistribution(distributionBoardId); + setState(() { + _distributionBoardList = + _service.getDistributionListByArea(widget.areaId); + }); + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Equipamento deletado com sucesso'), + backgroundColor: Colors.green, + ), + ); + } catch (e) { + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Falha ao deletar o equipamento'), + backgroundColor: Colors.red, + ), + ); + } } - void _deleteEquipment(BuildContext context, String equipment) { - // Implement the logic to delete the equipment + void _confirmDelete(BuildContext context, int distributionBoardId) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirmar Exclusão'), + content: const Text( + 'Você tem certeza que deseja excluir este equipamento?'), + actions: [ + TextButton( + child: const Text('Cancelar'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Excluir'), + onPressed: () { + Navigator.of(context).pop(); + _deleteDistributionBoard(context, distributionBoardId); + }, + ), + ], + ); + }, + ); } @override Widget build(BuildContext context) { String systemTitle = 'Quadro de Distribuição'; - return Scaffold( - appBar: AppBar( - backgroundColor: AppColors.sigeIeBlue, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pushReplacementNamed( - context, - '/systemLocation', - arguments: { - 'areaName': widget.areaName, - 'localName': widget.localName, - 'localId': widget.localId, - 'areaId': widget.areaId, - }, - ); - }, + return ScaffoldMessenger( + key: _scaffoldMessengerKey, + child: Scaffold( + appBar: AppBar( + backgroundColor: AppColors.sigeIeBlue, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () { + Navigator.pushReplacementNamed( + context, + '/systemLocation', + arguments: { + 'areaName': widget.areaName, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + }, + ), ), - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), - decoration: const BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: - BorderRadius.vertical(bottom: Radius.circular(20)), - ), - child: Center( - child: Text( - '${widget.areaName} - $systemTitle', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: AppColors.lightText, + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), + decoration: const BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: + BorderRadius.vertical(bottom: Radius.circular(20)), + ), + child: Center( + child: Text( + '${widget.areaName} - $systemTitle', + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: AppColors.lightText, + ), ), ), ), - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : equipmentList.isNotEmpty - ? Column( - children: equipmentList.map((equipment) { - return Container( - margin: - const EdgeInsets.symmetric(vertical: 5), - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - children: [ - Expanded( - child: Padding( - padding: - const EdgeInsets.only(left: 10), - child: Text( - equipment, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ), - ), - IconButton( - icon: const Icon(Icons.edit, - color: Colors.blue), - onPressed: () => _editEquipment( - context, equipment), - ), - IconButton( - icon: const Icon(Icons.delete, - color: Colors.red), - onPressed: () => _deleteEquipment( - context, equipment), - ), - ], - ), - ), - ); - }).toList(), - ) - : const Center( - child: Text( - 'Você ainda não tem equipamentos', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black54, + const SizedBox(height: 20), + FutureBuilder>( + future: _distributionBoardList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Erro: ${snapshot.error}')); + } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var distributionBoardEquipment = snapshot.data![index]; + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + title: Text( + distributionBoardEquipment.equipmentCategory, + style: const TextStyle( + color: AppColors.lightText, + fontWeight: FontWeight.bold), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit, + color: Colors.blue), + onPressed: () => _editDistributionBoard( + context, distributionBoardEquipment.id), + ), + IconButton( + icon: const Icon(Icons.delete, + color: Colors.red), + onPressed: () => _confirmDelete( + context, distributionBoardEquipment.id), ), - ), + ], ), - const SizedBox(height: 40), - ], + ), + ); + }, + ); + } else { + return const Padding( + padding: EdgeInsets.all(10.0), + child: Center( + child: Text( + 'Nenhum equipamento encontrado.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black54), + ), + ), + ); + } + }, ), - ), - ], + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => navigateToAddDistributionBoard(context), + backgroundColor: AppColors.sigeIeYellow, + child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), - ), - floatingActionButton: FloatingActionButton( - onPressed: () => navigateToAddEquipment(context), - backgroundColor: AppColors.sigeIeYellow, - child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), ); } From f4fda711a761153f36a247e537240b7c62efd4ba Mon Sep 17 00:00:00 2001 From: EngDann Date: Sat, 17 Aug 2024 17:10:51 -0300 Subject: [PATCH 10/15] Refrigeration completo e integrado --- .../refrigerations_request_model.dart | 9 +- ...refrigerations_response_by_area_model.dart | 16 +- .../refrigerations_response_model.dart | 7 +- .../refrigerations_service.dart | 2 +- .../add_distribuition_board.dart | 4 - .../refrigerations/add_refrigeration.dart | 457 ++++++++++++++---- .../refrigeration_equipment_list.dart | 324 +++++++------ 7 files changed, 577 insertions(+), 242 deletions(-) diff --git a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart index cdb88474..bd742dc0 100644 --- a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart +++ b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_request_model.dart @@ -1,22 +1,23 @@ class RefrigerationsRequestModel { - /* obs */ int? area; int? system; int? quantity; int? power; - + String? observation; RefrigerationsRequestModel( {required this.area, required this.system, required this.quantity, - required this.power}); + required this.power, + required this.observation}); Map toJson() { return { 'area': area, 'system': system, 'quantity': quantity, - 'power': power + 'power': power, + 'observation': observation }; } } diff --git a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_by_area_model.dart b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_by_area_model.dart index b9d5b351..942b2e50 100644 --- a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_by_area_model.dart +++ b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_by_area_model.dart @@ -1,10 +1,10 @@ class RefrigerationsEquipmentResponseByAreaModel { - int id; - int area; - String equipmentCategory; - int system; - int quantity; - int power; + final int id; + final int area; + final String equipmentCategory; + final int system; + final int quantity; + final String observation; RefrigerationsEquipmentResponseByAreaModel( {required this.id, @@ -12,7 +12,7 @@ class RefrigerationsEquipmentResponseByAreaModel { required this.equipmentCategory, required this.system, required this.quantity, - required this.power}); + required this.observation}); factory RefrigerationsEquipmentResponseByAreaModel.fromJson( Map json) { @@ -22,6 +22,6 @@ class RefrigerationsEquipmentResponseByAreaModel { equipmentCategory: json['equipment_category'], system: json['system'], quantity: json['quantity'], - power: json['power']); + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_model.dart b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_model.dart index 70277af5..cc0fded6 100644 --- a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_model.dart +++ b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_response_model.dart @@ -5,6 +5,7 @@ class RefrigerationsResponseModel { int system; int quantity; int power; + String? observation; RefrigerationsResponseModel( {required this.id, @@ -12,7 +13,8 @@ class RefrigerationsResponseModel { required this.equipment, required this.system, required this.quantity, - required this.power}); + required this.power, + required this.observation}); factory RefrigerationsResponseModel.fromJson(Map json) { return RefrigerationsResponseModel( @@ -21,6 +23,7 @@ class RefrigerationsResponseModel { equipment: json['equipment'], system: json['system'], quantity: json['quantity'], - power: json['power']); + power: json['power'], + observation: json['observation']); } } diff --git a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_service.dart b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_service.dart index e8175606..72f269bf 100644 --- a/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_service.dart +++ b/frontend/sige_ie/lib/equipments/data/refrigerations/refrigerations_service.dart @@ -18,7 +18,7 @@ class RefrigerationsEquipmentService { Future> getRefrigerationsListByArea(int areaId) async { - var url = Uri.parse('${baseUrl}refrigerations/by-area/$areaId/'); + var url = Uri.parse('${baseUrl}refrigeration/by-area/$areaId/'); try { var response = await client.get(url); diff --git a/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart b/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart index 7511062f..b47271eb 100644 --- a/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart +++ b/frontend/sige_ie/lib/equipments/feature/distribuition_board/add_distribuition_board.dart @@ -714,12 +714,8 @@ class _AddDistribuitionBoardState extends State { equipmentId = id; }); - print('Response ID: $id'); - if (equipmentId != null && equipmentId != 0) { - print('Registering photos for equipment ID: $equipmentId'); await Future.wait(_images.map((imageData) async { - print('Creating photo with description: "${imageData.description}"'); await equipmentPhotoService.createPhoto( EquipmentPhotoRequestModel( photo: imageData.imageFile, diff --git a/frontend/sige_ie/lib/equipments/feature/refrigerations/add_refrigeration.dart b/frontend/sige_ie/lib/equipments/feature/refrigerations/add_refrigeration.dart index 0d31c45d..675d75de 100644 --- a/frontend/sige_ie/lib/equipments/feature/refrigerations/add_refrigeration.dart +++ b/frontend/sige_ie/lib/equipments/feature/refrigerations/add_refrigeration.dart @@ -1,12 +1,14 @@ import 'dart:io'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:sige_ie/config/app_styles.dart'; import 'package:sige_ie/equipments/data/refrigerations/refrigerations_equipment_request_model.dart'; import 'package:sige_ie/equipments/data/refrigerations/refrigerations_request_model.dart'; +import 'package:sige_ie/equipments/data/refrigerations/refrigerations_response_model.dart'; +import 'package:sige_ie/equipments/data/refrigerations/refrigerations_service.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_request_model.dart'; +import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_response_model.dart'; import 'package:sige_ie/shared/data/equipment-photo/equipment_photo_service.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_response_model.dart'; import 'package:sige_ie/shared/data/generic-equipment-category/generic_equipment_category_service.dart'; @@ -18,12 +20,17 @@ class ImageData { int id; File imageFile; String description; + bool toDelete; - ImageData(this.imageFile, this.description) : id = Random().nextInt(1000000); + ImageData({ + int? id, + required this.imageFile, + required this.description, + this.toDelete = false, + }) : id = id ?? -1; // Valor padrão indicando ID inexistente } List _images = []; -Map> categoryImagesMap = {}; class AddRefrigeration extends StatefulWidget { final String areaName; @@ -31,6 +38,8 @@ class AddRefrigeration extends StatefulWidget { final int localId; final int systemId; final int areaId; + final int? refrigerationId; + final bool isEdit; const AddRefrigeration({ super.key, @@ -39,6 +48,8 @@ class AddRefrigeration extends StatefulWidget { required this.localName, required this.localId, required this.areaId, + this.refrigerationId, + this.isEdit = false, }); @override @@ -47,35 +58,114 @@ class AddRefrigeration extends StatefulWidget { class _AddRefrigerationState extends State { EquipmentService equipmentService = EquipmentService(); + RefrigerationsEquipmentService refrigerationService = + RefrigerationsEquipmentService(); EquipmentPhotoService equipmentPhotoService = EquipmentPhotoService(); PersonalEquipmentCategoryService personalEquipmentCategoryService = PersonalEquipmentCategoryService(); GenericEquipmentCategoryService genericEquipmentCategoryService = GenericEquipmentCategoryService(); - - final _equipmentQuantityController = TextEditingController(); - final _equipmentPowerController = TextEditingController(); + final TextEditingController _quantityController = TextEditingController(); + final TextEditingController _powerController = TextEditingController(); + final TextEditingController _observationsController = TextEditingController(); String? _selectedType; - String? _selectedTypeToDelete; - String? _newEquipmentTypeName; int? _selectedGenericEquipmentCategoryId; int? _selectedPersonalEquipmentCategoryId; bool _isPersonalEquipmentCategorySelected = false; - + String? _newEquipmentTypeName; + String? _selectedTypeToDelete; + int? equipmentId; List> genericEquipmentTypes = []; List> personalEquipmentTypes = []; Map personalEquipmentMap = {}; + RefrigerationsResponseModel? refrigerationResponseModel; @override void initState() { super.initState(); _fetchEquipmentCategory(); + if (widget.isEdit && widget.refrigerationId != null) { + _initializeData(widget.refrigerationId!); + } } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _fetchEquipmentCategory(); + Future _initializeData(int refrigerationId) async { + try { + await _fetchRefrigerationEquipment(refrigerationId); + + if (refrigerationResponseModel != null) { + setState(() { + equipmentId = refrigerationResponseModel!.equipment; + _quantityController.text = + refrigerationResponseModel!.quantity.toString(); + _powerController.text = refrigerationResponseModel!.power.toString(); + _observationsController.text = + refrigerationResponseModel!.observation ?? ''; + }); + + _fetchEquipmentDetails(refrigerationResponseModel!.equipment); + _fetchExistingPhotos(refrigerationResponseModel!.equipment); + } + } catch (error) { + print('Error: $error'); + } + } + + Future _fetchRefrigerationEquipment(int refrigerationId) async { + refrigerationResponseModel = + await refrigerationService.getRefrigerationsById(refrigerationId); + } + + Future _fetchEquipmentDetails(int equipmentId) async { + try { + final equipmentDetails = + await equipmentService.getEquipmentById(equipmentId); + + setState(() { + _isPersonalEquipmentCategorySelected = + equipmentDetails['personal_equipment_category'] != null; + + if (_isPersonalEquipmentCategorySelected) { + _selectedPersonalEquipmentCategoryId = + equipmentDetails['personal_equipment_category']; + _selectedType = personalEquipmentTypes.firstWhere((element) => + element['id'] == _selectedPersonalEquipmentCategoryId)['name'] + as String; + } else { + _selectedGenericEquipmentCategoryId = + equipmentDetails['generic_equipment_category']; + _selectedType = genericEquipmentTypes.firstWhere((element) => + element['id'] == _selectedGenericEquipmentCategoryId)['name'] + as String; + } + }); + } catch (e) { + print('Erro ao buscar detalhes do equipamento: $e'); + } + } + + void _fetchExistingPhotos(int equipmentId) async { + try { + List photos = + await equipmentPhotoService.getPhotosByEquipmentId(equipmentId); + + List imageList = []; + + for (var photo in photos) { + File imageFile = await photo.toFile(); + imageList.add(ImageData( + id: photo.id, + imageFile: imageFile, + description: photo.description ?? '', + )); + } + + setState(() { + _images = imageList; + }); + } catch (e) { + print('Erro ao buscar fotos existentes: $e'); + } } Future _fetchEquipmentCategory() async { @@ -105,9 +195,10 @@ class _AddRefrigerationState extends State { @override void dispose() { - _equipmentQuantityController.dispose(); - _equipmentPowerController.dispose(); - categoryImagesMap[widget.systemId]?.clear(); + _quantityController.dispose(); + _powerController.dispose(); + _images.clear(); + _observationsController.dispose(); super.dispose(); } @@ -124,8 +215,9 @@ class _AddRefrigerationState extends State { } void _showImageDialog(File imageFile, {ImageData? existingImage}) { - TextEditingController descriptionController = - TextEditingController(text: existingImage?.description ?? ''); + TextEditingController descriptionController = TextEditingController( + text: existingImage?.description ?? '', + ); showDialog( context: context, builder: (BuildContext context) { @@ -153,17 +245,17 @@ class _AddRefrigerationState extends State { child: const Text('Salvar'), onPressed: () { setState(() { + String? description = descriptionController.text.isEmpty + ? null + : descriptionController.text; if (existingImage != null) { - existingImage.description = descriptionController.text; + existingImage.description = description ?? ''; } else { - final imageData = - ImageData(imageFile, descriptionController.text); - final systemId = widget.systemId; - if (!categoryImagesMap.containsKey(systemId)) { - categoryImagesMap[systemId] = []; - } - categoryImagesMap[systemId]!.add(imageData); - _images = categoryImagesMap[systemId]!; + final imageData = ImageData( + imageFile: imageFile, + description: description ?? '', + ); + _images.add(imageData); } }); Navigator.of(context).pop(); @@ -218,6 +310,120 @@ class _AddRefrigerationState extends State { ); } + void _updateRefrigeration() async { + int? genericEquipmentCategory; + int? personalEquipmentCategory; + + if (_isPersonalEquipmentCategorySelected) { + genericEquipmentCategory = null; + personalEquipmentCategory = _selectedPersonalEquipmentCategoryId; + } else { + genericEquipmentCategory = _selectedGenericEquipmentCategoryId; + personalEquipmentCategory = null; + } + + final Map equipmentTypeUpdate = { + "generic_equipment_category": genericEquipmentCategory, + "personal_equipment_category": personalEquipmentCategory, + }; + + bool typeUpdateSuccess = await equipmentService.updateEquipment( + equipmentId!, equipmentTypeUpdate); + + if (typeUpdateSuccess) { + final RefrigerationsRequestModel refrigerationModel = + RefrigerationsRequestModel( + area: widget.areaId, + system: widget.systemId, + quantity: int.tryParse(_quantityController.text), + power: int.tryParse(_powerController.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, + ); + + bool refrigerationUpdateSuccess = await refrigerationService + .updateRefrigerations(widget.refrigerationId!, refrigerationModel); + + if (refrigerationUpdateSuccess) { + await Future.wait(_images + .where((imageData) => imageData.toDelete) + .map((imageData) async { + await equipmentPhotoService.deletePhoto(imageData.id); + })); + + await Future.wait(_images + .where((imageData) => !imageData.toDelete) + .map((imageData) async { + if (imageData.id == -1) { + await equipmentPhotoService.createPhoto( + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } else { + await equipmentPhotoService.updatePhoto( + imageData.id, + EquipmentPhotoRequestModel( + photo: imageData.imageFile, + description: imageData.description.isEmpty + ? null + : imageData.description, + equipment: equipmentId!, + ), + ); + } + })); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Detalhes do equipamento de refrigeração atualizados com sucesso.'), + backgroundColor: Colors.green, + ), + ); + Navigator.pushReplacementNamed( + context, + '/listRefrigerationEquipment', + arguments: { + 'areaName': widget.areaName, + 'systemId': widget.systemId, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + setState(() { + _quantityController.clear(); + _powerController.clear(); + _selectedType = null; + _selectedPersonalEquipmentCategoryId = null; + _selectedGenericEquipmentCategoryId = null; + _images.clear(); + _observationsController.clear(); + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar os detalhes do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao atualizar o tipo do equipamento.'), + backgroundColor: Colors.red, + ), + ); + } + } + Future _registerPersonalEquipmentType() async { int systemId = widget.systemId; PersonalEquipmentCategoryRequestModel personalEquipmentTypeRequestModel = @@ -230,7 +436,7 @@ class _AddRefrigerationState extends State { if (id != -1) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento registrado com sucesso.'), + content: Text('Equipamento de refrigeração registrado com sucesso.'), backgroundColor: Colors.green, ), ); @@ -245,7 +451,7 @@ class _AddRefrigerationState extends State { } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao registrar o equipamento.'), + content: Text('Falha ao registrar o equipamento de refrigeração.'), backgroundColor: Colors.red, ), ); @@ -273,15 +479,15 @@ class _AddRefrigerationState extends State { return; } - int equipmentId = personalEquipmentMap[_selectedTypeToDelete]!; + int personalCategoryId = personalEquipmentMap[_selectedTypeToDelete]!; showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Confirmar Exclusão'), - content: - const Text('Tem certeza de que deseja excluir este equipamento?'), + content: const Text( + 'Tem certeza de que deseja excluir este tipo de equipamento?'), actions: [ TextButton( child: const Text('Cancelar'), @@ -294,25 +500,27 @@ class _AddRefrigerationState extends State { onPressed: () async { Navigator.of(context).pop(); bool success = await personalEquipmentCategoryService - .deletePersonalEquipmentCategory(equipmentId); + .deletePersonalEquipmentCategory(personalCategoryId); if (success) { setState(() { personalEquipmentTypes.removeWhere( (element) => element['name'] == _selectedTypeToDelete); _selectedTypeToDelete = null; + _selectedType = null; _fetchEquipmentCategory(); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Equipamento excluído com sucesso.'), + content: + Text('Tipo de equipamento excluído com sucesso.'), backgroundColor: Colors.green, ), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Falha ao excluir o equipamento.'), + content: Text('Falha ao excluir o tipo de equipamento.'), backgroundColor: Colors.red, ), ); @@ -326,8 +534,8 @@ class _AddRefrigerationState extends State { } void _showConfirmationDialog() { - if (_equipmentQuantityController.text.isEmpty || - _equipmentPowerController.text.isEmpty || + if (_quantityController.text.isEmpty || + _powerController.text.isEmpty || (_selectedType == null && _newEquipmentTypeName == null)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -351,16 +559,22 @@ class _AddRefrigerationState extends State { const SizedBox(height: 10), const Text('Quantidade:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentQuantityController.text), + Text(_quantityController.text), + const SizedBox(height: 10), + const Text('Potência:', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(_powerController.text), const SizedBox(height: 10), - const Text('Potência (KW):', + const Text('Observações:', style: TextStyle(fontWeight: FontWeight.bold)), - Text(_equipmentPowerController.text), + Text(_observationsController.text), const SizedBox(height: 10), const Text('Imagens:', style: TextStyle(fontWeight: FontWeight.bold)), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Padding( padding: const EdgeInsets.all(4.0), child: GestureDetector( @@ -392,9 +606,16 @@ class _AddRefrigerationState extends State { }, ), TextButton( - child: const Text('Adicionar'), + child: widget.isEdit + ? const Text('Atualizar') + : const Text('Adicionar'), onPressed: () { - _registerEquipment(); + if (widget.isEdit) { + _updateRefrigeration(); + } else { + _registerRefrigeration(); + } + Navigator.of(context).pop(); }, ), ], @@ -403,7 +624,7 @@ class _AddRefrigerationState extends State { ); } - void _registerEquipment() async { + void _registerRefrigeration() async { int? genericEquipmentCategory; int? personalEquipmentCategory; @@ -415,38 +636,48 @@ class _AddRefrigerationState extends State { personalEquipmentCategory = null; } - final RefrigerationsRequestModel refrigerationsModel = + final RefrigerationsRequestModel refrigerationModel = RefrigerationsRequestModel( area: widget.areaId, system: widget.systemId, - quantity: null, - power: null, + quantity: int.tryParse(_quantityController.text), + power: int.tryParse(_powerController.text), + observation: _observationsController.text.isNotEmpty + ? _observationsController.text + : null, ); - final RefrigerationsEquipmentRequestModel refrigerationsEquipmentDetail = + final RefrigerationsEquipmentRequestModel refrigerationEquipmentDetail = RefrigerationsEquipmentRequestModel( genericEquipmentCategory: genericEquipmentCategory, personalEquipmentCategory: personalEquipmentCategory, - refrigerationsRequestModel: refrigerationsModel, + refrigerationsRequestModel: refrigerationModel, ); - int? equipmentId = await equipmentService - .createRefrigerations(refrigerationsEquipmentDetail); + int? id = await equipmentService + .createRefrigerations(refrigerationEquipmentDetail); + setState(() { + equipmentId = id; + }); - if (equipmentId != null) { + if (equipmentId != null && equipmentId != 0) { + print('Registering photos for equipment ID: $equipmentId'); await Future.wait(_images.map((imageData) async { + print('Creating photo with description: "${imageData.description}"'); await equipmentPhotoService.createPhoto( EquipmentPhotoRequestModel( photo: imageData.imageFile, - description: imageData.description, - equipment: equipmentId, + description: + imageData.description.isEmpty ? null : imageData.description, + equipment: equipmentId!, ), ); })); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Detalhes do equipamento registrados com sucesso.'), + content: Text( + 'Detalhes do equipamento de refrigeração registrados com sucesso.'), backgroundColor: Colors.green, ), ); @@ -462,12 +693,13 @@ class _AddRefrigerationState extends State { }, ); setState(() { - _equipmentQuantityController.clear(); - _equipmentPowerController.clear(); + _quantityController.clear(); + _powerController.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( @@ -479,6 +711,13 @@ class _AddRefrigerationState extends State { } } + Future _deletePhoto(int photoId) async { + setState(() { + _images.firstWhere((imageData) => imageData.id == photoId).toDelete = + true; + }); + } + @override Widget build(BuildContext context) { List> combinedTypes = [ @@ -494,12 +733,13 @@ class _AddRefrigerationState extends State { icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () { setState(() { - _equipmentQuantityController.clear(); - _equipmentPowerController.clear(); + _quantityController.clear(); + _powerController.clear(); _selectedType = null; _selectedPersonalEquipmentCategoryId = null; _selectedGenericEquipmentCategoryId = null; _images.clear(); + _observationsController.clear(); }); Navigator.pushReplacementNamed( context, @@ -527,9 +767,12 @@ class _AddRefrigerationState extends State { borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)), ), - child: const Center( - child: Text('Adicionar equipamento', - style: TextStyle( + child: Center( + child: Text( + widget.refrigerationId == null + ? 'Adicionar Equipamento' + : 'Editar Equipamento', + style: const TextStyle( fontSize: 26, fontWeight: FontWeight.bold, color: Colors.white)), @@ -540,7 +783,7 @@ class _AddRefrigerationState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Tipos de refrigerações', + const Text('Tipos de Equipamento de Refrigeração', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -551,7 +794,8 @@ class _AddRefrigerationState extends State { child: _buildStyledDropdown( items: [ { - 'name': 'Selecione o tipo de refrigeração', + 'name': + 'Selecione o tipo de equipamento de refrigeração', 'id': -1, 'type': -1 } @@ -559,7 +803,8 @@ class _AddRefrigerationState extends State { combinedTypes, value: _selectedType, onChanged: (newValue) { - if (newValue != 'Selecione o tipo de equipamento') { + if (newValue != + 'Selecione o tipo de equipamento de refrigeração') { setState(() { _selectedType = newValue; Map selected = @@ -613,7 +858,6 @@ class _AddRefrigerationState extends State { ), ], ), - const SizedBox(height: 8), TextButton( onPressed: () { setState(() { @@ -622,8 +866,8 @@ class _AddRefrigerationState extends State { }, child: const Text('Limpar seleção'), ), - const SizedBox(height: 30), - const Text('Potência (KW)', + const SizedBox(height: 15), + const Text('Quantidade', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -633,17 +877,23 @@ class _AddRefrigerationState extends State { borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _equipmentPowerController, + controller: _quantityController, keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], decoration: const InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 15), ), + onChanged: (value) { + print('Quantidade modificada: $value'); + }, ), ), - const SizedBox(height: 30), - const Text('Quantidade', + const SizedBox(height: 15), + const Text('Potência (KW)', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 8), @@ -653,11 +903,8 @@ class _AddRefrigerationState extends State { borderRadius: BorderRadius.circular(10), ), child: TextField( - controller: _equipmentQuantityController, + controller: _powerController, keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly - ], decoration: const InputDecoration( border: InputBorder.none, contentPadding: @@ -666,12 +913,51 @@ class _AddRefrigerationState extends State { ), ), const SizedBox(height: 15), + const Text('Observações', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 14)), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + child: TextField( + controller: _observationsController, + maxLines: 3, + maxLength: 500, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: + EdgeInsets.symmetric(horizontal: 10, vertical: 15), + hintText: + 'Digite suas observações aqui (máx 500 caracteres)', + ), + buildCounter: (BuildContext context, + {required int currentLength, + required bool isFocused, + required int? maxLength}) { + return Text( + '$currentLength / $maxLength', + style: TextStyle( + fontSize: 12, + color: currentLength > maxLength! + ? Colors.red + : Colors.grey, + ), + ); + }, + ), + ), + const SizedBox(height: 15), IconButton( icon: const Icon(Icons.camera_alt), onPressed: _pickImage, ), Wrap( - children: _images.map((imageData) { + children: _images + .where((imageData) => !imageData.toDelete) + .map((imageData) { return Stack( alignment: Alignment.topRight, children: [ @@ -692,10 +978,7 @@ class _AddRefrigerationState extends State { icon: const Icon(Icons.remove_circle, color: AppColors.warn), onPressed: () { - setState(() { - _images.removeWhere( - (element) => element.id == imageData.id); - }); + _deletePhoto(imageData.id); }, ), ], @@ -717,9 +1000,11 @@ class _AddRefrigerationState extends State { borderRadius: BorderRadius.circular(10), ))), onPressed: _showConfirmationDialog, - child: const Text( - 'ADICIONAR EQUIPAMENTO', - style: TextStyle( + child: Text( + widget.isEdit + ? 'ATUALIZAR EQUIPAMENTO' + : 'ADICIONAR EQUIPAMENTO', + style: const TextStyle( fontSize: 17, fontWeight: FontWeight.bold), ), ), @@ -815,11 +1100,13 @@ class _AddRefrigerationState extends State { items: items.map>((Map value) { return DropdownMenuItem( value: value['name'] as String, - enabled: value['name'] != 'Selecione o tipo de equipamento', + enabled: value['name'] != + 'Selecione o tipo de equipamento de refrigeração', child: Text( value['name'] as String, style: TextStyle( - color: value['name'] == 'Selecione o tipo de equipamento' + color: value['name'] == + 'Selecione o tipo de equipamento de refrigeração' ? Colors.grey : Colors.black, ), diff --git a/frontend/sige_ie/lib/equipments/feature/refrigerations/refrigeration_equipment_list.dart b/frontend/sige_ie/lib/equipments/feature/refrigerations/refrigeration_equipment_list.dart index 569c88b0..f448fe49 100644 --- a/frontend/sige_ie/lib/equipments/feature/refrigerations/refrigeration_equipment_list.dart +++ b/frontend/sige_ie/lib/equipments/feature/refrigerations/refrigeration_equipment_list.dart @@ -27,39 +27,38 @@ class ListRefrigerationEquipment extends StatefulWidget { class _ListRefrigerationEquipmentState extends State { - List equipmentList = []; - bool isLoading = true; - final RefrigerationsEquipmentService _service = + late Future> + _refrigerationList; + final RefrigerationsEquipmentService _refrigerationService = RefrigerationsEquipmentService(); + final GlobalKey _scaffoldMessengerKey = + GlobalKey(); + @override void initState() { super.initState(); - fetchEquipmentList(); + _refrigerationList = + _refrigerationService.getRefrigerationsListByArea(widget.areaId); } - Future fetchEquipmentList() async { - try { - final List equipmentList = - await _service.getRefrigerationsListByArea(widget.areaId); - if (mounted) { - setState(() { -/* this.equipmentList = equipmentList; - */ - isLoading = false; - }); - } - } catch (e) { - print('Error fetching equipment list: $e'); - if (mounted) { - setState(() { - isLoading = false; - }); - } - } + void navigateToAddRefrigeration(BuildContext context) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddRefrigeration( + areaName: widget.areaName, + systemId: widget.systemId, + localName: widget.localName, + localId: widget.localId, + areaId: widget.areaId, + refrigerationId: null, + ), + ), + ); } - void navigateToAddEquipment(BuildContext context) { + void _editRefrigeration(BuildContext context, int refrigerationId) { Navigator.push( context, MaterialPageRoute( @@ -69,141 +68,190 @@ class _ListRefrigerationEquipmentState localName: widget.localName, localId: widget.localId, areaId: widget.areaId, + refrigerationId: refrigerationId, + isEdit: true, ), ), ); } - void _editEquipment(BuildContext context, String equipment) { - // Implement the logic to edit the equipment + Future _deleteRefrigeration( + BuildContext context, int refrigerationId) async { + try { + await _refrigerationService.deleteRefrigerations(refrigerationId); + setState(() { + _refrigerationList = + _refrigerationService.getRefrigerationsListByArea(widget.areaId); + }); + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Equipamento deletado com sucesso'), + backgroundColor: Colors.green, + ), + ); + } catch (e) { + _scaffoldMessengerKey.currentState?.showSnackBar( + const SnackBar( + content: Text('Falha ao deletar o equipamento'), + backgroundColor: Colors.red, + ), + ); + } } - void _deleteEquipment(BuildContext context, String equipment) { - // Implement the logic to delete the equipment + void _confirmDelete(BuildContext context, int refrigerationId) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Confirmar Exclusão'), + content: const Text( + 'Você tem certeza que deseja excluir este equipamento?'), + actions: [ + TextButton( + child: const Text('Cancelar'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('Excluir'), + onPressed: () { + Navigator.of(context).pop(); + _deleteRefrigeration(context, refrigerationId); + }, + ), + ], + ); + }, + ); } @override Widget build(BuildContext context) { String systemTitle = 'Refrigeração'; - return Scaffold( - appBar: AppBar( - backgroundColor: AppColors.sigeIeBlue, - leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.white), - onPressed: () { - Navigator.pushReplacementNamed( - context, - '/systemLocation', - arguments: { - 'areaName': widget.areaName, - 'localName': widget.localName, - 'localId': widget.localId, - 'areaId': widget.areaId, - }, - ); - }, + return ScaffoldMessenger( + key: _scaffoldMessengerKey, + child: Scaffold( + appBar: AppBar( + backgroundColor: AppColors.sigeIeBlue, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () { + Navigator.pushReplacementNamed( + context, + '/systemLocation', + arguments: { + 'areaName': widget.areaName, + 'localName': widget.localName, + 'localId': widget.localId, + 'areaId': widget.areaId, + }, + ); + }, + ), ), - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), - decoration: const BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: - BorderRadius.vertical(bottom: Radius.circular(20)), - ), - child: Center( - child: Text( - '${widget.areaName} - $systemTitle', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.bold, - color: AppColors.lightText, + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + padding: const EdgeInsets.fromLTRB(10, 10, 10, 35), + decoration: const BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: + BorderRadius.vertical(bottom: Radius.circular(20)), + ), + child: Center( + child: Text( + '${widget.areaName} - $systemTitle', + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: AppColors.lightText, + ), ), ), ), - ), - const SizedBox(height: 20), - Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - isLoading - ? const Center( - child: CircularProgressIndicator(), - ) - : equipmentList.isNotEmpty - ? Column( - children: equipmentList.map((equipment) { - return Container( - margin: - const EdgeInsets.symmetric(vertical: 5), - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: BorderRadius.circular(10), - ), - child: Row( - children: [ - Expanded( - child: Padding( - padding: - const EdgeInsets.only(left: 10), - child: Text( - equipment, - style: const TextStyle( - color: Colors.white, - fontSize: 18, - ), - ), - ), - ), - IconButton( - icon: const Icon(Icons.edit, - color: Colors.blue), - onPressed: () => _editEquipment( - context, equipment), - ), - IconButton( - icon: const Icon(Icons.delete, - color: Colors.red), - onPressed: () => _deleteEquipment( - context, equipment), - ), - ], - ), - ), - ); - }).toList(), - ) - : const Center( - child: Text( - 'Você ainda não tem equipamentos', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black54, + const SizedBox(height: 20), + FutureBuilder>( + future: _refrigerationList, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Erro: ${snapshot.error}')); + } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var refrigerationEquipment = snapshot.data![index]; + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: BorderRadius.circular(10), + ), + child: ListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, vertical: 10), + title: Text( + refrigerationEquipment.equipmentCategory, + style: const TextStyle( + color: AppColors.lightText, + fontWeight: FontWeight.bold), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit, + color: Colors.blue), + onPressed: () => _editRefrigeration( + context, refrigerationEquipment.id), + ), + IconButton( + icon: const Icon(Icons.delete, + color: Colors.red), + onPressed: () => _confirmDelete( + context, refrigerationEquipment.id), ), - ), + ], ), - const SizedBox(height: 40), - ], + ), + ); + }, + ); + } else { + return const Padding( + padding: EdgeInsets.all(10.0), + child: Center( + child: Text( + 'Nenhum equipamento encontrado.', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black54), + ), + ), + ); + } + }, ), - ), - ], + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => navigateToAddRefrigeration(context), + backgroundColor: AppColors.sigeIeYellow, + child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), - ), - floatingActionButton: FloatingActionButton( - onPressed: () => navigateToAddEquipment(context), - backgroundColor: AppColors.sigeIeYellow, - child: const Icon(Icons.add, color: AppColors.sigeIeBlue), ), ); } From b89c5bc2aa5bf659a81da33ac06710fd946ac62b Mon Sep 17 00:00:00 2001 From: EngDann Date: Sat, 17 Aug 2024 20:58:34 -0300 Subject: [PATCH 11/15] Tela de perfil visualmente refeita --- .../sige_ie/lib/users/feature/profile.dart | 465 ++++++++++-------- 1 file changed, 249 insertions(+), 216 deletions(-) diff --git a/frontend/sige_ie/lib/users/feature/profile.dart b/frontend/sige_ie/lib/users/feature/profile.dart index 0057218d..44827a0c 100644 --- a/frontend/sige_ie/lib/users/feature/profile.dart +++ b/frontend/sige_ie/lib/users/feature/profile.dart @@ -20,251 +20,284 @@ class _ProfilePageState extends State { @override void initState() { super.initState(); - userService.fetchProfileData().then((userResponseModel) { + _loadProfileData(); + } + + Future _loadProfileData() async { + try { + UserResponseModel profileData = await userService.fetchProfileData(); setState(() { - this.userResponseModel = userResponseModel; + userResponseModel = profileData; }); - }).catchError((error) { + } catch (error) { print(error); - }); + } } - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - backgroundColor: AppColors.sigeIeBlue, - ), - body: SingleChildScrollView( - child: - Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - padding: const EdgeInsets.fromLTRB(0, 0, 0, 10), - decoration: const BoxDecoration( - color: AppColors.sigeIeBlue, - borderRadius: BorderRadius.vertical(bottom: Radius.circular(20)), - ), - child: const Center( - child: Column( - children: [ - Text( - 'Editar perfil', + void _showEditDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + contentPadding: const EdgeInsets.all(16.0), + content: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: Text( + 'Editar dados', style: TextStyle( - fontSize: 26, + fontSize: 20, fontWeight: FontWeight.bold, - color: Colors.white, ), ), - ], - ), + ), + const SizedBox(height: 20), + Text('Nome'), + TextField( + decoration: InputDecoration( + filled: true, + fillColor: Colors.grey[300], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric( + vertical: 15.0, horizontal: 10.0), + isDense: true, + ), + controller: + TextEditingController(text: userResponseModel.firstname), + onChanged: (value) => userResponseModel.firstname = value, + ), + const SizedBox(height: 15), + Text('Username'), + TextField( + decoration: InputDecoration( + filled: true, + fillColor: Colors.grey[200], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric( + vertical: 15.0, horizontal: 10.0), + isDense: true, + ), + controller: + TextEditingController(text: userResponseModel.username), + enabled: false, + ), + const SizedBox(height: 15), + Text('Email'), + TextField( + decoration: InputDecoration( + filled: true, + fillColor: Colors.grey[300], + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric( + vertical: 15.0, horizontal: 10.0), + isDense: true, + ), + controller: + TextEditingController(text: userResponseModel.email), + onChanged: (value) => userResponseModel.email = value, + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + InkWell( + onTap: () { + // Implementar lógica para mudar senha + }, + child: const Text( + 'Mudar senha', + style: TextStyle( + color: Colors.blue, + ), + ), + ), + InkWell( + onTap: () { + // Implementar lógica para mudar username + }, + child: const Text( + 'Mudar username', + style: TextStyle( + color: Colors.blue, + ), + ), + ), + ], + ), + ], ), ), - const SizedBox(height: 20), - Padding( + actions: [ + Center( + child: ElevatedButton( + onPressed: () async { + await userService.update( + userResponseModel.id, + userResponseModel.firstname, + userResponseModel.email, + ); + Navigator.of(context).pop(); + _loadProfileData(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.sigeIeYellow, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: const Text( + 'Salvar', + style: TextStyle( + color: AppColors.sigeIeBlue, + fontSize: 15, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + backgroundColor: AppColors.sigeIeBlue, + ), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 10), + decoration: const BoxDecoration( + color: AppColors.sigeIeBlue, + borderRadius: + BorderRadius.vertical(bottom: Radius.circular(20)), + ), + child: const Center( + child: Column( + children: [ + Text( + 'Perfil', + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + ], + ), + ), + ), + const SizedBox(height: 20), + Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Email', + 'Olá, seja bem vindo', style: TextStyle( - color: Colors.grey[600], fontWeight: FontWeight.bold), - ), - const SizedBox(height: 5), - TextField( - decoration: InputDecoration( - filled: true, - fillColor: Colors.grey[300], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8.0), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - vertical: 15.0, horizontal: 10.0), - isDense: true, + color: Colors.black, + fontWeight: FontWeight.bold, ), - controller: - TextEditingController(text: userResponseModel.email), - onChanged: (value) => userResponseModel.email = value, ), - const SizedBox(height: 15), Text( - 'Nome', + userResponseModel.firstname, style: TextStyle( - color: Colors.grey[600], fontWeight: FontWeight.bold), - ), - const SizedBox(height: 5), - TextField( - decoration: InputDecoration( - filled: true, - fillColor: Colors.grey[300], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8.0), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - vertical: 15.0, horizontal: 10.0), - isDense: true, + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, ), - controller: TextEditingController( - text: userResponseModel.firstname), - onChanged: (value) => userResponseModel.firstname = value, - ), - const SizedBox(height: 15), - Text( - 'Username', - style: TextStyle( - color: Colors.grey[600], fontWeight: FontWeight.bold), ), - const SizedBox(height: 5), - TextField( - decoration: InputDecoration( - filled: true, - fillColor: Colors.grey[200], - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8.0), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.symmetric( - vertical: 15.0, horizontal: 10.0), - isDense: true, + const SizedBox(height: 20), + ListTile( + leading: Icon(Icons.edit, color: Colors.black), + title: Text( + 'Editar dados', + style: TextStyle(color: Colors.black), ), - controller: - TextEditingController(text: userResponseModel.username), - enabled: false, + onTap: _showEditDialog, + trailing: + Icon(Icons.arrow_forward_ios, color: Colors.black), ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - InkWell( - onTap: () {}, - child: const Text( - 'Mudar senha', - style: TextStyle(color: Colors.blue), - ), - ), - InkWell( - onTap: () {}, - child: const Text( - 'Mudar username', - style: TextStyle(color: Colors.blue), - ), - ), - ], + Divider(color: Colors.grey), + ListTile( + leading: Icon(Icons.exit_to_app, color: AppColors.accent), + title: Text('Sair', style: TextStyle(color: Colors.black)), + onTap: () async { + await authService.logout(); + Navigator.pushReplacementNamed(context, '/loginScreen'); + }, + trailing: + Icon(Icons.arrow_forward_ios, color: Colors.black), ), - const SizedBox(height: 60), - Center( - child: Column( - children: [ - SizedBox( - width: 150, - height: 50, - child: ElevatedButton( - onPressed: () async { - await userService.update( - userResponseModel.id, - userResponseModel.firstname, - userResponseModel.email); - }, - style: ElevatedButton.styleFrom( - backgroundColor: AppColors.sigeIeYellow, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + Divider(color: Colors.grey), + ListTile( + leading: Icon(Icons.delete, color: AppColors.warn), + title: Text( + 'Excluir conta', + style: TextStyle(color: Colors.black), + ), + onTap: () async { + bool deleteConfirmed = await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('EXCLUIR'), + content: const Text( + 'Tem certeza que deseja excluir sua conta?', ), - minimumSize: const Size(150, 50), - ), - child: const Text('Salvar', - style: TextStyle( - color: AppColors.sigeIeBlue, - fontSize: 15, - fontWeight: FontWeight.w900)), - ), - ), - const SizedBox(height: 50), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - SizedBox( - width: 150, - height: 50, - child: ElevatedButton( - onPressed: () async { - await authService.logout(); - Navigator.pushReplacementNamed( - context, '/loginScreen'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: - const Color.fromARGB(207, 231, 27, 27), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - minimumSize: const Size(150, 50), + actions: [ + TextButton( + onPressed: () => + Navigator.of(context).pop(false), + child: const Text('Cancelar'), ), - child: const Text('Sair da Conta', - style: TextStyle( - color: AppColors.lightText, - fontSize: 15, - fontWeight: FontWeight.w900)), - ), - ), - const SizedBox(width: 20), - SizedBox( - width: 150, - height: 50, - child: ElevatedButton( - onPressed: () async { - bool deleteConfirmed = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('EXCLUIR'), - content: const Text( - 'Tem certeza que deseja excluir sua conta?'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context) - .pop( - false), // Retorna falso para indicar que a exclusão não foi confirmada - child: const Text('Cancelar'), - ), - TextButton( - onPressed: () => Navigator.of(context) - .pop( - true), // Retorna verdadeiro para indicar que a exclusão foi confirmada - child: const Text('Confirmar'), - ), - ], - ); - }, - ); + TextButton( + onPressed: () => + Navigator.of(context).pop(true), + child: const Text('Confirmar'), + ), + ], + ); + }, + ); - // Se a exclusão for confirmada, exclua a conta - if (deleteConfirmed) { - await userService - .delete(userResponseModel.id); - Navigator.pushReplacementNamed( - context, '/loginScreen'); - } - }, - style: AppButtonStyles.warnButton.copyWith( - minimumSize: MaterialStateProperty.all( - const Size(150, 50))), - child: const Text('Excluir Conta', - style: TextStyle( - color: AppColors.lightText, - fontSize: 15, - fontWeight: FontWeight.w900)), - ), - ), - ], - ), - ], - )) + if (deleteConfirmed) { + await userService.delete(userResponseModel.id); + Navigator.pushReplacementNamed(context, '/loginScreen'); + } + }, + trailing: + Icon(Icons.arrow_forward_ios, color: Colors.black), + ), ], - )), - ]))); + ), + ), + ], + ), + ), + ); } } From 43969e46ea301d76f6eeea5e349fce4aed36403d Mon Sep 17 00:00:00 2001 From: EngDann Date: Sat, 17 Aug 2024 20:59:10 -0300 Subject: [PATCH 12/15] Atmospheric Discharges completo e integrado Co-authored-by: Ramires rocha Co-authored-by: Kauan Jose Co-authored-by: OscarDeBrito Co-authored-by: Pedro Lucas --- api/equipments/views.py | 2 +- .../atmospheric_discharges_list.dart | 38 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/api/equipments/views.py b/api/equipments/views.py index 57858caf..47f05ee1 100644 --- a/api/equipments/views.py +++ b/api/equipments/views.py @@ -220,7 +220,7 @@ def create(self, request, *args, **kwargs): class AtmosphericDischargeEquipmentByAreaList(generics.ListAPIView): serializer_class = AtmosphericDischargeEquipmentResponseSerializer - permission_classes = [IsAuthenticated, IsEquipmentOwner, IsSpecificEquipmentEditor] + permission_classes = [IsAuthenticated, IsEquipmentOwner | IsSpecificEquipmentEditor] def get_queryset(self): area_id = self.kwargs['area_id'] diff --git a/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart b/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart index 1570b885..f0b0f049 100644 --- a/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart +++ b/frontend/sige_ie/lib/equipments/feature/atmospheric-discharges/atmospheric_discharges_list.dart @@ -74,23 +74,29 @@ class _ListAtmosphericEquipmentState extends State { BuildContext context, int dischargeId) async { try { await _atmosphericService.deleteAtmospheric(dischargeId); - setState(() { - _atmosphericList = - _atmosphericService.getAtmosphericListByArea(widget.areaId); - }); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Equipamento deletado com sucesso'), - backgroundColor: Colors.green, - ), - ); + if (mounted) { + // Verifica se o widget ainda está montado + setState(() { + _atmosphericList = + _atmosphericService.getAtmosphericListByArea(widget.areaId); + }); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Equipamento deletado com sucesso'), + backgroundColor: Colors.green, + ), + ); + } } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Falha ao deletar o equipamento'), - backgroundColor: Colors.red, - ), - ); + if (mounted) { + // Verifica se o widget ainda está montado + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Falha ao deletar o equipamento'), + backgroundColor: Colors.red, + ), + ); + } } } From 5ed5c09a660542096dcb161097523976ee3ed6c5 Mon Sep 17 00:00:00 2001 From: Pedro Lucas Garcia <190115548@aluno.unb.br> Date: Mon, 19 Aug 2024 11:48:59 -0300 Subject: [PATCH 13/15] =?UTF-8?q?Corrige=20conflitos=20do=20relat=C3=B3rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/places/views.py | 119 +++++++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 29 deletions(-) diff --git a/api/places/views.py b/api/places/views.py index ae8401cf..0f6f758a 100644 --- a/api/places/views.py +++ b/api/places/views.py @@ -5,6 +5,7 @@ from rest_framework.views import APIView from django.db.models import Q from django.contrib.auth.models import User +from weasyprint import HTML from users.models import PlaceOwner, PlaceEditor from rest_framework.generics import get_object_or_404 from rest_framework.permissions import IsAuthenticated @@ -18,6 +19,7 @@ from reportlab.pdfgen import canvas from .models import Place, Area from .serializers import PlaceSerializer, AreaSerializer +from django.template.loader import render_to_string def get_place_owner_or_create(user): @@ -251,7 +253,6 @@ class GeneratePDFView(APIView): def get(self, request, pk=None): place = get_object_or_404(Place, pk=pk) - self.check_object_permissions(request, place) timezone = pytz.timezone('America/Sao_Paulo') @@ -263,32 +264,69 @@ def get(self, request, pk=None): } for area in place.areas.all(): - p.setFont('Helvetica-Bold', 14) - p.drawString(120, alt.get_alt(p), f"Relatório da Área: {area.name}") - for system in area.fire_alarm_equipment.all(): - if (system == None): - break - p.setFont('Helvetica', 12) - p.drawString(140, alt.get_alt(p), f"Sistema: {system.system} - Tipo: {genericOrPersonal(system)}") + photos = [] + for image in system.equipment.ephoto.all(): + photos.append({'image_url': image.photo.url, 'description': image.description}) + report_data['installations'].append({ + 'area': f'{area.name}', + 'system': f'{system.system}', + 'type': f'{genericOrPersonal(system)}', + 'photos': photos, + 'quantity': f'{system.quantity}', + 'observation': f'{system.observation}', + }) for system in area.atmospheric_discharge_equipment.all(): - if (system == None): - break - p.setFont('Helvetica', 12) - p.drawString(140, alt.get_alt(p), f"Sistema: {system.system} - Tipo: {genericOrPersonal(system)}") + photos = [] + for image in system.atmospheric_discharge_equipment.ephoto.all(): + photos.append({ + 'image_url': image.photo.url, + 'description': image.description + }) + report_data['installations'].append({ + 'area': f'{area.name}', + 'system': f'{system.system}', + 'type': f'{genericOrPersonal(system)}', + 'photos': photos + }) for system in area.structured_cabling_equipment.all(): - if (system == None): - break - p.setFont('Helvetica', 12) - p.drawString(140, alt.get_alt(p), f"Sistema: {system.system} - Tipo: {genericOrPersonal(system)}") + photos = [] + for image in system.equipment.ephoto.all(): + photos.append({ + 'image_url': image.photo.url, + 'description': image.description + }) + report_data['installations'].append({ + 'area': f'{area.name}', + 'system': f'{system.system}', + 'type': f'{genericOrPersonal(system)}', + 'photos': photos, + 'quantity': f'{system.quantity}' + }) for system in area.distribution_board_equipment.all(): - if (system == None): - break - p.setFont('Helvetica', 12) - p.drawString(140, alt.get_alt(p), f"Sistema: {system.system} - Tipo: {genericOrPersonal(system)}") + photos = [] + for image in system.equipment.ephoto.all(): + photos.append({ + 'image_url': image.photo.url, + 'description': image.description + }) + report_data['installations'].append({ + 'area': f'{area.name}', + 'system': f'{system.system}', + 'type': f'{genericOrPersonal(system)}', + 'photos': photos, + 'power': f'{system.power}', + 'dr': f'{"Sim" if system.dr else "Não"}', + 'dps': f'{"Sim" if system.dps else "Não"}', + 'grounding': f'{"Sim" if system.grounding else "Não"}', + 'type_material': f'{system.type_material}', + 'method_installation': f'{system.method_installation}', + 'quantity': f'{system.quantity}', + 'observation': f'{system.observation}' + }) for system in area.electrical_circuit_equipment.all(): photos = [] @@ -308,10 +346,20 @@ def get(self, request, pk=None): }) for system in area.electrical_line_equipment.all(): - if (system == None): - break - p.setFont('Helvetica', 12) - p.drawString(140, alt.get_alt(p), f"Sistema: {system.system} - Tipo: {genericOrPersonal(system)}") + photos = [] + for image in system.equipment.ephoto.all(): + photos.append({ + 'image_url': image.photo.url, + 'description': image.description + }) + report_data['installations'].append({ + 'area': f'{area.name}', + 'system': f'{system.system}', + 'type': f'{genericOrPersonal(system)}', + 'photos': photos, + 'quantity': f'{system.quantity}', + 'observation': f'{system.observation}' + }) for system in area.electrical_load_equipment.all(): photos = [] @@ -333,10 +381,23 @@ def get(self, request, pk=None): }) for system in area.ilumination_equipment.all(): - if (system == None): - break - p.setFont('Helvetica', 12) - p.drawString(140, alt.get_alt(p), f"Sistema: {system.system} - Tipo: {genericOrPersonal(system)}") + photos = [] + for image in system.equipment.ephoto.all(): + photos.append({ + 'image_url': image.photo.url, + 'description': image.description + }) + report_data['installations'].append({ + 'area': f'{area.name}', + 'sistema': f'{system.system}', + 'tipo': f'{genericOrPersonal(system)}', + 'photos': photos, + 'quantity': f'{system.quantity}', + 'power': f'{system.power}', + 'tecnology': f'{system.tecnology}', + 'format': f'{system.format}', + 'observation': f'{system.observation}' + }) for system in area.refrigeration_equipment.all(): photos = [] @@ -514,4 +575,4 @@ def get(self, request, pk=None): content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = 'attachment; filename="equipamentos_relatorio.xlsx"' - return response + return response \ No newline at end of file From 736fd7ee1537020918bffc390b62aca3b17aae99 Mon Sep 17 00:00:00 2001 From: Pedro Lucas Garcia <190115548@aluno.unb.br> Date: Mon, 19 Aug 2024 13:34:28 -0300 Subject: [PATCH 14/15] Corrige conflito do template do relatorio --- api/docker-compose.yml | 1 + api/places/templates/html/index.html | 251 +++++++++++++++++++++++++++ api/places/urls.py | 2 +- api/sigeie/settings.py | 2 + api/sigeie/urls.py | 4 + 5 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 api/places/templates/html/index.html diff --git a/api/docker-compose.yml b/api/docker-compose.yml index 3dd18ff6..ce255832 100644 --- a/api/docker-compose.yml +++ b/api/docker-compose.yml @@ -31,6 +31,7 @@ services: container_name: sigeie_web command: > sh -c " + pip install -r requirements.txt python manage.py makemigrations && python manage.py migrate && python manage.py loaddata sigeie/fixtures/initial_data.json && diff --git a/api/places/templates/html/index.html b/api/places/templates/html/index.html new file mode 100644 index 00000000..65148209 --- /dev/null +++ b/api/places/templates/html/index.html @@ -0,0 +1,251 @@ + + + + + + Relatório + + + +
+

Relatório de Instalações Elétricas

+

Data: {{ report_date }}

+
+ +
+

Resumo

+

{{ summary }}

+
+ +
+

Detalhes das Instalações

+ {% for installation in installations %} +
+
+

{{ installation.area }}

+

{{ installation.system }}

+
+
+
+ Tipo: +

{{ installation.type }}

+
+ {% if installation.system == 'Sistema de Iluminação' %} +
+ Potência: +

{{ installation.power }} KW

+
+ +
+ Tecnologia +

{{ installation.tecnology }}

+
+ +
+ Formato +

{{ installation.format }}

+
+ {% endif %} + + {% if installation.system == 'Cargas Elétricas' %} +
+ Marca: +

{{ installation.brand }}

+
+ +
+ Modelo: +

{{ installation.model }}

+
+ +
+ Potência: +

{{ installation.power }} KW

+
+ + {% endif %} + + {% if installation.system == 'Circuitos Elétricos' %} + +
+ Tamanho: +

{{ installation.size }} m

+
+ +
+ Tipo de fio: +

{{ installation.type_wire }}

+
+ +
+ Tipo do disjuntor: +

{{ installation.type_circuit_breaker }}

+
+ + {% endif %} + + {% if installation.system == 'Quadros de Distribuição' %} +
+ Potência: +

{{ installation.power }} KW

+
+ +
+ DR: +

{{ installation.dr }}

+
+ +
+ DP: +

{{ installation.dp }}

+
+ +
+ DPS: +

{{ installation.dp }}

+
+ +
+ Aterramento: +

{{ installation.grounding }}

+
+ +
+ Tipo de Material: +

{{ installation.type_material }}

+
+ +
+ Método de Instalação: +

{{ installation.method_installation }}

+
+ + {% endif %} + + {% if installation.system == 'Sistema de Refrigeração' %} +
+ Potência: +

{{ installation.power}}

+
+ {% endif %} + +
+ Quantidade: +

{{ installation.quantity }}

+
+ +
+ Observações: +

{{ installation.observation }}

+
+ + + +
+
+ {% for image in installation.photos %} +
+ Imagem do Equipamento +

{{ image.description }}

+
+ {% endfor %} +
+
+ {% endfor %} +
+ +
+

Relatório gerado por: {{ generated_by }}

+
+ + \ No newline at end of file diff --git a/api/places/urls.py b/api/places/urls.py index 7b9a051f..46b30160 100644 --- a/api/places/urls.py +++ b/api/places/urls.py @@ -11,5 +11,5 @@ urlpatterns = [ path('places//report-pdf/', GeneratePDFView.as_view(), name='place-report-pdf'), - path('places//report-csv', CSVView.as_view(), name='place-report-csv') + path('places//report-csv/', CSVView.as_view(), name='place-report-csv') ] \ No newline at end of file diff --git a/api/sigeie/settings.py b/api/sigeie/settings.py index c3da8211..b0443a41 100644 --- a/api/sigeie/settings.py +++ b/api/sigeie/settings.py @@ -8,6 +8,8 @@ ALLOWED_HOSTS = ['127.0.0.1', '10.0.2.2', '192.168.1.142', '192.168.15.36'] +MEDIA_URL = '/media/' + INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', diff --git a/api/sigeie/urls.py b/api/sigeie/urls.py index 877cd7b7..235303fe 100644 --- a/api/sigeie/urls.py +++ b/api/sigeie/urls.py @@ -17,6 +17,8 @@ from django.contrib import admin from django.urls import path, include from places.urls import router +from django.conf import settings +from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), @@ -27,3 +29,5 @@ path('api/', include('equipments.urls')), path('api/', include('places.urls')) ] + +static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file From b4d830c68646108973e9ddd50be45af269b553ed Mon Sep 17 00:00:00 2001 From: Pedro Lucas Garcia <190115548@aluno.unb.br> Date: Mon, 19 Aug 2024 15:31:34 -0300 Subject: [PATCH 15/15] Corrige conflito do nome da view --- api/places/urls.py | 3 +-- api/places/views.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api/places/urls.py b/api/places/urls.py index 46b30160..6438e6ea 100644 --- a/api/places/urls.py +++ b/api/places/urls.py @@ -1,7 +1,6 @@ from django.urls import path, include from .views import * from rest_framework.routers import SimpleRouter -from .views import GeneratePDFView router = SimpleRouter() router.register(r'places',PlaceViewSet) @@ -10,6 +9,6 @@ router.register(r'refuse_access', RefuseAccessViewSet, basename = 'refuse_access') urlpatterns = [ - path('places//report-pdf/', GeneratePDFView.as_view(), name='place-report-pdf'), + path('places//report-pdf/', PDFView.as_view(), name='place-report-pdf'), path('places//report-csv/', CSVView.as_view(), name='place-report-csv') ] \ No newline at end of file diff --git a/api/places/views.py b/api/places/views.py index 0f6f758a..7e8f0fbc 100644 --- a/api/places/views.py +++ b/api/places/views.py @@ -248,7 +248,7 @@ def genericOrPersonal(system): return system.equipment.personal_equipment_category -class GeneratePDFView(APIView): +class PDFView(APIView): permission_classes = [IsAuthenticated, IsPlaceOwner or IsPlaceEditor] def get(self, request, pk=None):