706 lines
26 KiB
Dart
706 lines
26 KiB
Dart
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:firebase_storage/firebase_storage.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'package:prosappco/src/authentication/authentication_repository.dart';
|
|
import 'package:prosappco/src/components/photo_view_web.dart';
|
|
import 'package:prosappco/src/components/pop_appbar.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'dart:io';
|
|
|
|
class ProfessionalProfileWebScreen extends StatefulWidget {
|
|
const ProfessionalProfileWebScreen({super.key});
|
|
|
|
@override
|
|
State<ProfessionalProfileWebScreen> createState() =>
|
|
_ProfessionalProfileWebScreenState();
|
|
}
|
|
|
|
class _ProfessionalProfileWebScreenState
|
|
extends State<ProfessionalProfileWebScreen> {
|
|
final uid = AuthenticationRepository.instance.getCurrentUserUid();
|
|
final FirebaseStorage storage = FirebaseStorage.instance;
|
|
|
|
// variables imagen
|
|
String selectedImage = '';
|
|
String selectedCedulaImage = '';
|
|
String selectedCertificadoImage = '';
|
|
|
|
List selectedEspecializacionImages = [];
|
|
List<Uint8List> imagesEspecializacionsBytes = [];
|
|
|
|
XFile? file;
|
|
Uint8List? selectedImagInBytes;
|
|
XFile? image_cedula;
|
|
Uint8List? imageCedulaBytes;
|
|
XFile? image_certificado;
|
|
Uint8List? imageCertificadoBytes;
|
|
XFile? image_especializaciones;
|
|
Uint8List? imageEspecializacionesBytes;
|
|
|
|
// controllers
|
|
final _formKey = GlobalKey<FormState>();
|
|
final TextEditingController _cedulaController = TextEditingController();
|
|
final TextEditingController _especializacionController =
|
|
TextEditingController();
|
|
|
|
// variables
|
|
bool _isLoading = false;
|
|
String _profession = '...';
|
|
String photoTemp = '';
|
|
String photoCedulaTemp = '';
|
|
String photoCertificadoTemp = '';
|
|
String _photo = '...';
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
if (_photo == '...') {
|
|
AuthenticationRepository.instance.getPhoto(uid.toString()).then(
|
|
(String s) => setState(() {
|
|
_photo = s;
|
|
}),
|
|
);
|
|
}
|
|
|
|
if (_profession == '...') {
|
|
AuthenticationRepository.instance.getProfession(uid.toString()).then(
|
|
(String s) => setState(() {
|
|
_profession = s;
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
_selectFile(bool imageFrom) async {
|
|
FilePickerResult? fileResult = await FilePicker.platform.pickFiles();
|
|
|
|
if (fileResult != null) {
|
|
setState(() {
|
|
selectedImage = fileResult.files.first.name;
|
|
selectedImagInBytes = fileResult.files.first.bytes;
|
|
});
|
|
}
|
|
}
|
|
|
|
_selectFileCedula(bool imageFrom) async {
|
|
FilePickerResult? fileResult = await FilePicker.platform.pickFiles();
|
|
|
|
if (fileResult != null) {
|
|
setState(() {
|
|
selectedCedulaImage = fileResult.files.first.name;
|
|
imageCedulaBytes = fileResult.files.first.bytes;
|
|
});
|
|
}
|
|
}
|
|
|
|
_selectFileCertificado(bool imageFrom) async {
|
|
FilePickerResult? fileResult = await FilePicker.platform.pickFiles();
|
|
|
|
if (fileResult != null) {
|
|
setState(() {
|
|
selectedCertificadoImage = fileResult.files.first.name;
|
|
imageCertificadoBytes = fileResult.files.first.bytes;
|
|
});
|
|
}
|
|
}
|
|
|
|
_selectFilesEspecializaciones(bool imageFrom) async {
|
|
FilePickerResult? fileResult =
|
|
await FilePicker.platform.pickFiles(allowMultiple: true);
|
|
try {
|
|
if (fileResult != null) {
|
|
List<Uint8List> selectedFileBytes = [];
|
|
|
|
for (var file in fileResult.files) {
|
|
Uint8List? bytes = file.bytes;
|
|
if (bytes != null) {
|
|
selectedFileBytes.add(bytes);
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
imagesEspecializacionsBytes = selectedFileBytes;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
print('$e');
|
|
}
|
|
}
|
|
|
|
Future<void> sendInfo() async {
|
|
setState(() {
|
|
_isLoading = true;
|
|
});
|
|
|
|
final String cedula = _cedulaController.text.trim();
|
|
final String especializacion = _especializacionController.text.trim();
|
|
final List<String> especializaciones =
|
|
especializacion.split(',').map((e) => e.trim()).toList();
|
|
|
|
if (cedula.isEmpty) {
|
|
showSnackBar('Cedula invalida', 'Ingrese una cedula válida');
|
|
return;
|
|
}
|
|
|
|
if (imageCedulaBytes == null) {
|
|
showSnackBar('Cedula', 'Ingrese una imagen de su cedula');
|
|
return;
|
|
}
|
|
|
|
if (imageCertificadoBytes == null) {
|
|
showSnackBar('Certificado', 'Ingrese una imagen de su certificado');
|
|
return;
|
|
}
|
|
|
|
// Actualiza los datos del usuario en Firestore
|
|
await FirebaseFirestore.instance.collection('users').doc(uid).update({
|
|
'cedula': cedula,
|
|
'estado': 'revision',
|
|
'especializaciones': especializaciones
|
|
});
|
|
|
|
// Sube las imágenes al storage de Firebase
|
|
await uploadCedula();
|
|
await uploadCertificado();
|
|
List<String> uploadedPhotoPaths =
|
|
await uploadEspecializaciones(imagesEspecializacionsBytes);
|
|
|
|
if (uploadedPhotoPaths.isNotEmpty) {
|
|
// Las imágenes se cargaron correctamente
|
|
// Actualiza las imágenes en Firestore
|
|
await updateFilesEspecializaciones(uploadedPhotoPaths);
|
|
|
|
// Navega a la siguiente pantalla
|
|
Navigator.pushReplacementNamed(context, '/solicitudEnviada');
|
|
} else {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: const Text('Error'),
|
|
content: const Text('Ocurrió un error al cargar las imágenes.'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
},
|
|
child: const Text('Aceptar'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
// Actualiza la imagen de perfil si hay cambios
|
|
if (selectedImagInBytes != null) {
|
|
await uploadFile();
|
|
await updateImage(photoTemp);
|
|
}
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
|
|
// Navega a la siguiente pantalla
|
|
Navigator.pushReplacementNamed(context, '/solicitudEnviada');
|
|
}
|
|
|
|
uploadFile() async {
|
|
try {
|
|
final now = DateTime.now();
|
|
final formattedDate = DateFormat('HHmmssddMMyyyy').format(now);
|
|
final milliseconds = (now.microsecondsSinceEpoch / 1000).round();
|
|
final random = '$formattedDate$milliseconds';
|
|
|
|
final Reference ref = FirebaseStorage.instance
|
|
.ref()
|
|
.child('users')
|
|
.child(uid!)
|
|
.child('profile')
|
|
.child(random);
|
|
|
|
final metaData = SettableMetadata(contentType: 'image/jpeg');
|
|
|
|
final UploadTask uploadTask = ref.putData(selectedImagInBytes!, metaData);
|
|
|
|
final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true);
|
|
|
|
photoTemp = ref.fullPath;
|
|
|
|
if (snapshot.state == TaskState.success) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
print('web image error - $e');
|
|
}
|
|
}
|
|
|
|
uploadCedula() async {
|
|
try {
|
|
final now = DateTime.now();
|
|
final formattedDate = DateFormat('HHmmssddMMyyyy').format(now);
|
|
final milliseconds = (now.microsecondsSinceEpoch / 1000).round();
|
|
final random = 'c$formattedDate$milliseconds';
|
|
|
|
final Reference ref = FirebaseStorage.instance
|
|
.ref()
|
|
.child('users')
|
|
.child(uid!)
|
|
.child('cedula')
|
|
.child(random);
|
|
|
|
final metaData = SettableMetadata(contentType: 'application/pdf');
|
|
|
|
final UploadTask uploadTask = ref.putData(imageCedulaBytes!, metaData);
|
|
|
|
final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true);
|
|
|
|
photoCedulaTemp = ref.fullPath;
|
|
|
|
if (snapshot.state == TaskState.success) {
|
|
updateImageCedula(photoCedulaTemp);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
print('web image cedula error - $e');
|
|
}
|
|
}
|
|
|
|
uploadCertificado() async {
|
|
try {
|
|
final now = DateTime.now();
|
|
final formattedDate = DateFormat('HHmmssddMMyyyy').format(now);
|
|
final milliseconds = (now.microsecondsSinceEpoch / 1000).round();
|
|
final random = 'f$formattedDate$milliseconds';
|
|
|
|
final Reference ref = FirebaseStorage.instance
|
|
.ref()
|
|
.child('users')
|
|
.child(uid!)
|
|
.child('certificado_profesional')
|
|
.child(random);
|
|
|
|
final metaData = SettableMetadata(contentType: 'application/pdf');
|
|
|
|
final UploadTask uploadTask =
|
|
ref.putData(imageCertificadoBytes!, metaData);
|
|
|
|
final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true);
|
|
|
|
photoCertificadoTemp = ref.fullPath;
|
|
|
|
if (snapshot.state == TaskState.success) {
|
|
updateImageCertificado(photoCertificadoTemp);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
print('web image certificado error - $e');
|
|
}
|
|
}
|
|
|
|
Future<List<String>> uploadEspecializaciones(List<Uint8List> files) async {
|
|
List<String> filePaths = [];
|
|
|
|
for (Uint8List fileBytes in files) {
|
|
final Reference ref = FirebaseStorage.instance
|
|
.ref()
|
|
.child('users')
|
|
.child(uid!)
|
|
.child('especializaciones')
|
|
.child('e${DateTime.now().millisecondsSinceEpoch}.pdf');
|
|
|
|
final SettableMetadata metaData =
|
|
SettableMetadata(contentType: 'application/pdf');
|
|
|
|
final UploadTask uploadTask = ref.putData(fileBytes, metaData);
|
|
|
|
final TaskSnapshot snapshot = await uploadTask.whenComplete(() => true);
|
|
|
|
if (snapshot.state == TaskState.success) {
|
|
filePaths.add(ref.fullPath);
|
|
} else {
|
|
await updateFilesEspecializaciones(filePaths);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
await updateFilesEspecializaciones(filePaths);
|
|
return filePaths;
|
|
}
|
|
|
|
Future<void> updateImageCedula(image) async {
|
|
try {
|
|
final userRef = FirebaseFirestore.instance.collection('users').doc(uid);
|
|
final userSnapshot = await userRef.get();
|
|
|
|
if (userSnapshot.exists) {
|
|
await userRef.update({'imgCedula': image});
|
|
} else {
|
|
await userRef.set({'imgCedula': image});
|
|
}
|
|
} catch (e) {
|
|
print('Error al agregar o actualizar la imagen de cédula: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> updateImageCertificado(image) async {
|
|
try {
|
|
final userRef = FirebaseFirestore.instance.collection('users').doc(uid);
|
|
final userSnapshot = await userRef.get();
|
|
|
|
if (userSnapshot.exists) {
|
|
await userRef.update({'imgCertificado': image});
|
|
} else {
|
|
await userRef.set({'imgCertificado': image});
|
|
}
|
|
} catch (e) {
|
|
print('Error al agregar o actualizar la imagen de certificado: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> updateImage(image) async {
|
|
try {
|
|
await FirebaseFirestore.instance
|
|
.collection('users')
|
|
.doc(uid)
|
|
.update({'photo': image});
|
|
} catch (e) {
|
|
try {
|
|
await FirebaseFirestore.instance
|
|
.collection('users')
|
|
.doc(uid)
|
|
.set({'photo': image});
|
|
} catch (e) {
|
|
print('Error al agregar la imagen de perfil: $e');
|
|
}
|
|
|
|
print('Error al actualizar la imagen de perfil: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> updateFilesEspecializaciones(List<String> filePaths) async {
|
|
try {
|
|
final userRef = FirebaseFirestore.instance.collection('users').doc(uid);
|
|
final userSnapshot = await userRef.get();
|
|
|
|
if (userSnapshot.exists) {
|
|
await userRef.update({'imgEspecializaciones': filePaths});
|
|
} else {
|
|
await userRef.set({'imgEspecializaciones': filePaths});
|
|
}
|
|
} catch (e) {
|
|
print('Error al actualizar los archivos de especializaciones: $e');
|
|
}
|
|
}
|
|
|
|
void showSnackBar(String title, String message) {
|
|
Get.snackbar(
|
|
title,
|
|
message,
|
|
snackPosition: SnackPosition.TOP,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
String profession = _profession.toString();
|
|
double _space = 10;
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFD6F4FF),
|
|
appBar: PopAppbar(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
},
|
|
label: 'Perfil profesional'),
|
|
body: SingleChildScrollView(
|
|
child: Center(
|
|
child: SizedBox(
|
|
width: 350,
|
|
child: Card(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
child: _isLoading
|
|
? const Padding(
|
|
padding: EdgeInsets.symmetric(vertical: 30),
|
|
child: CircularProgressIndicator(),
|
|
)
|
|
: Column(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.only(top: 20),
|
|
child: (selectedImagInBytes != null)
|
|
? LocalPhotoWeb(file: selectedImagInBytes)
|
|
: ReferencePhotoWeb(
|
|
ref: storage.ref().child(_photo)),
|
|
),
|
|
SizedBox(
|
|
width: 300,
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
children: [
|
|
TextFormField(
|
|
keyboardType: TextInputType.number,
|
|
controller: _cedulaController,
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter
|
|
.digitsOnly // Solo permite caracteres numéricos
|
|
],
|
|
validator: (String? value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Ingrese una cedula válida';
|
|
}
|
|
return null;
|
|
},
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.person_outline),
|
|
hintText: 'Cedula (Obligatorio)',
|
|
),
|
|
),
|
|
SizedBox(height: _space),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
_selectFileCedula(true);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFD6F4FF),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(50),
|
|
),
|
|
elevation: 0,
|
|
minimumSize: const Size(250, 50),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'Cedula',
|
|
style: TextStyle(
|
|
color: Color(0xFF2BA4EC),
|
|
fontSize: 17,
|
|
),
|
|
),
|
|
const SizedBox(width: 15),
|
|
Icon(
|
|
imageCedulaBytes != null
|
|
? Icons.check
|
|
: Icons.file_upload_outlined,
|
|
color: const Color(0xFF2BA4EC),
|
|
size: 30,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: _space),
|
|
TextFormField(
|
|
readOnly: true,
|
|
onTap: () async {
|
|
final String? profesion =
|
|
(await Navigator.pushNamed(
|
|
context, '/profession'))
|
|
as String?;
|
|
|
|
if (profesion != null) {
|
|
setState(() {
|
|
_profession = profesion;
|
|
});
|
|
}
|
|
},
|
|
decoration: InputDecoration(
|
|
prefixIcon: const Icon(
|
|
Icons.assignment_ind_rounded),
|
|
suffixIcon:
|
|
const Icon(Icons.arrow_drop_down),
|
|
hintStyle: profession == ''
|
|
? const TextStyle()
|
|
: const TextStyle(
|
|
color: Colors.black87),
|
|
hintText: profession == ''
|
|
? 'Profesión (Obligatorio)'
|
|
: profession),
|
|
),
|
|
SizedBox(height: _space),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
_selectFileCertificado(true);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFD6F4FF),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(50),
|
|
),
|
|
elevation: 0,
|
|
minimumSize: const Size(250, 50),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'Certificado profesional',
|
|
style: TextStyle(
|
|
color: Color(0xFF2BA4EC),
|
|
fontSize: 17,
|
|
),
|
|
),
|
|
const SizedBox(width: 15),
|
|
Icon(
|
|
imageCertificadoBytes != null
|
|
? Icons.check
|
|
: Icons.file_upload_outlined,
|
|
color: const Color(0xFF2BA4EC),
|
|
size: 30,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: _space),
|
|
TextFormField(
|
|
controller: _especializacionController,
|
|
decoration: const InputDecoration(
|
|
prefixIcon:
|
|
Icon(Icons.assignment_ind_rounded),
|
|
hintText: 'Especialización'),
|
|
),
|
|
SizedBox(height: _space),
|
|
ElevatedButton(
|
|
onPressed: () async {
|
|
_selectFilesEspecializaciones(true);
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFD6F4FF),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(50),
|
|
),
|
|
elevation: 0,
|
|
minimumSize: const Size(250, 50),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'Especialización',
|
|
style: TextStyle(
|
|
color: Color(0xFF2BA4EC),
|
|
fontSize: 17,
|
|
),
|
|
),
|
|
const SizedBox(width: 15),
|
|
Icon(
|
|
imagesEspecializacionsBytes.isEmpty
|
|
? Icons.file_upload_outlined
|
|
: Icons.check,
|
|
color: const Color(0xFF2BA4EC),
|
|
size: 30,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
margin: const EdgeInsets.only(top: 40),
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 20, vertical: 15),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFD6F4FF),
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.5),
|
|
spreadRadius: 1,
|
|
blurRadius: 5,
|
|
offset: const Offset(1, 3),
|
|
),
|
|
],
|
|
),
|
|
child: const Row(
|
|
children: [
|
|
Icon(
|
|
Icons.error_outline,
|
|
size: 27,
|
|
color: Colors.black54,
|
|
),
|
|
SizedBox(width: 15),
|
|
Expanded(
|
|
child: Text(
|
|
'Si tienes más de una especialidad, por favor, adjunta un archivo con el diploma de respaldo para cada una de ellas y sepáralos por comas. ¡Gracias!',
|
|
style: TextStyle(
|
|
color: Colors.black, fontSize: 14),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
alignment: Alignment.bottomCenter,
|
|
margin: const EdgeInsets.only(
|
|
top: 30, right: 20, left: 20, bottom: 30),
|
|
child: ElevatedButton(
|
|
onPressed: () {
|
|
if (_formKey.currentState!.validate()) {
|
|
sendInfo();
|
|
}
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF2BA4EC),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(50),
|
|
),
|
|
elevation: 0,
|
|
minimumSize: const Size(250, 50),
|
|
maximumSize: const Size(350, 50),
|
|
),
|
|
child: const Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
'Enviar información',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 17,
|
|
),
|
|
),
|
|
SizedBox(width: 15),
|
|
Icon(
|
|
Icons.send,
|
|
color: Colors.white,
|
|
size: 20,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|