feat: implement core domain models, authentication service, and UI components for the attendance system
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Asistencia.Models;
|
||||
using Asistencia.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Asistencia.ViewModels;
|
||||
|
||||
public partial class AsistenciaViewModel : ObservableObject
|
||||
{
|
||||
private readonly AsistenciaService _asistenciaService;
|
||||
private readonly AuthService _authService;
|
||||
private readonly MainWindowViewModel _mainVm;
|
||||
private readonly CursoAbierto _curso;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _nombreCurso = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _infoCurso = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _enSesion;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _totalAlumnos;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _presentes;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _tardanzas;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _ausentes;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _mensajeEstado = string.Empty;
|
||||
|
||||
public ObservableCollection<AlumnoAsistenciaViewModel> Alumnos { get; } = new();
|
||||
|
||||
public AsistenciaViewModel(AsistenciaService asistenciaService, AuthService authService, CursoAbierto curso, MainWindowViewModel mainVm)
|
||||
{
|
||||
_asistenciaService = asistenciaService;
|
||||
_authService = authService;
|
||||
_mainVm = mainVm;
|
||||
_curso = curso;
|
||||
|
||||
NombreCurso = curso.Curso?.NombreCurso ?? "Curso";
|
||||
InfoCurso = $"{curso.Sala} | {curso.Sede} | {curso.HoraInicio} - {curso.HoraFin}";
|
||||
|
||||
_ = InitAsync();
|
||||
}
|
||||
|
||||
private async Task InitAsync()
|
||||
{
|
||||
var sesion = await _asistenciaService.BuscarSesionActivaAsync(_curso.IdCursoAbierto);
|
||||
EnSesion = sesion != null;
|
||||
|
||||
var alumnos = await _asistenciaService.ObtenerAlumnosAsync(_curso.IdCursoAbierto);
|
||||
var asistencias = await _asistenciaService.ObtenerAsistenciaAlumnosAsync(_curso.IdCursoAbierto);
|
||||
|
||||
Alumnos.Clear();
|
||||
foreach (var alumno in alumnos)
|
||||
{
|
||||
var tipo = asistencias.TryGetValue(alumno.IdDetalleContrato, out var t) ? t : TipoAsistencia.Ausente;
|
||||
Alumnos.Add(new AlumnoAsistenciaViewModel(alumno, tipo));
|
||||
}
|
||||
|
||||
TotalAlumnos = Alumnos.Count;
|
||||
ActualizarContadores();
|
||||
|
||||
MensajeEstado = EnSesion
|
||||
? "Sesion en curso - Puede registrar asistencia"
|
||||
: "Inicie la sesion para registrar asistencia";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task IniciarSesionAsync()
|
||||
{
|
||||
if (_authService.CurrentUser == null) return;
|
||||
|
||||
await _asistenciaService.IniciarSesionAsync(_curso.IdCursoAbierto, _authService.CurrentUser.RutPasaporte);
|
||||
EnSesion = true;
|
||||
MensajeEstado = "Sesion iniciada - Registre la asistencia de los alumnos";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task FinalizarSesionAsync()
|
||||
{
|
||||
var sesion = await _asistenciaService.BuscarSesionActivaAsync(_curso.IdCursoAbierto);
|
||||
if (sesion != null)
|
||||
{
|
||||
await _asistenciaService.FinalizarSesionAsync(sesion.Id);
|
||||
}
|
||||
EnSesion = false;
|
||||
MensajeEstado = "Sesion finalizada";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task MarcarAlumnoAsync(AlumnoAsistenciaViewModel? alumno)
|
||||
{
|
||||
if (alumno == null || !EnSesion) return;
|
||||
|
||||
alumno.CiclarTipoAsistencia();
|
||||
await _asistenciaService.GuardarAsistenciaAlumnoAsync(alumno.IdDetalleContrato, alumno.TipoAsistencia);
|
||||
ActualizarContadores();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Volver()
|
||||
{
|
||||
_mainVm.NavigateToDashboard();
|
||||
}
|
||||
|
||||
private void ActualizarContadores()
|
||||
{
|
||||
Presentes = Alumnos.Count(a => a.TipoAsistencia == Models.TipoAsistencia.Presente);
|
||||
Tardanzas = Alumnos.Count(a => a.TipoAsistencia == Models.TipoAsistencia.Tardanza);
|
||||
Ausentes = Alumnos.Count(a => a.TipoAsistencia == Models.TipoAsistencia.Ausente);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class AlumnoAsistenciaViewModel : ObservableObject
|
||||
{
|
||||
public int IdDetalleContrato { get; }
|
||||
public string NombreCompleto { get; }
|
||||
|
||||
[ObservableProperty]
|
||||
private int _tipoAsistencia;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _tipoAsistenciaTexto;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _tipoAsistenciaColor;
|
||||
|
||||
public AlumnoAsistenciaViewModel(DetalleContrato alumno, int tipoInicial)
|
||||
{
|
||||
IdDetalleContrato = alumno.IdDetalleContrato;
|
||||
NombreCompleto = alumno.NombreCompleto;
|
||||
TipoAsistencia = tipoInicial;
|
||||
ActualizarDisplay();
|
||||
}
|
||||
|
||||
public void CiclarTipoAsistencia()
|
||||
{
|
||||
TipoAsistencia = TipoAsistencia switch
|
||||
{
|
||||
Models.TipoAsistencia.Presente => Models.TipoAsistencia.Tardanza,
|
||||
Models.TipoAsistencia.Tardanza => Models.TipoAsistencia.Ausente,
|
||||
_ => Models.TipoAsistencia.Presente
|
||||
};
|
||||
ActualizarDisplay();
|
||||
}
|
||||
|
||||
private void ActualizarDisplay()
|
||||
{
|
||||
TipoAsistenciaTexto = Models.TipoAsistencia.Nombre(TipoAsistencia);
|
||||
TipoAsistenciaColor = TipoAsistencia switch
|
||||
{
|
||||
Models.TipoAsistencia.Presente => "#28A745",
|
||||
Models.TipoAsistencia.Tardanza => "#FFC107",
|
||||
_ => "#DC3545"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Asistencia.Models;
|
||||
using Asistencia.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Asistencia.ViewModels;
|
||||
|
||||
public partial class DashboardViewModel : ObservableObject
|
||||
{
|
||||
private readonly AuthService _authService;
|
||||
private readonly AsistenciaService _asistenciaService;
|
||||
private readonly MainWindowViewModel _mainVm;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _nombreProfesor = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _totalCursos;
|
||||
|
||||
public ObservableCollection<CursoAbierto> Cursos { get; } = new();
|
||||
|
||||
public DashboardViewModel(AuthService authService, AsistenciaService asistenciaService, MainWindowViewModel mainVm)
|
||||
{
|
||||
_authService = authService;
|
||||
_asistenciaService = asistenciaService;
|
||||
_mainVm = mainVm;
|
||||
NombreProfesor = authService.CurrentUser?.Nombre ?? "";
|
||||
_ = LoadCursosAsync();
|
||||
}
|
||||
|
||||
private async Task LoadCursosAsync()
|
||||
{
|
||||
if (_authService.CurrentUser == null) return;
|
||||
|
||||
var cursos = await _asistenciaService.ObtenerCursosProfesorAsync(_authService.CurrentUser.RutPasaporte);
|
||||
Cursos.Clear();
|
||||
foreach (var c in cursos) Cursos.Add(c);
|
||||
TotalCursos = Cursos.Count;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void AbrirAsistencia(CursoAbierto? curso)
|
||||
{
|
||||
if (curso != null)
|
||||
_mainVm.NavigateToAsistencia(curso);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void AbrirPerfil()
|
||||
{
|
||||
_mainVm.NavigateToPerfil();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CerrarSesion()
|
||||
{
|
||||
_authService.Logout();
|
||||
_mainVm.NavigateToLogin();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Asistencia.Services;
|
||||
|
||||
namespace Asistencia.ViewModels;
|
||||
|
||||
public partial class LoginViewModel : ObservableObject
|
||||
{
|
||||
private readonly AuthService _authService;
|
||||
private readonly MainWindowViewModel _mainVm;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _rut = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _password = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
public LoginViewModel(AuthService authService, MainWindowViewModel mainVm)
|
||||
{
|
||||
_authService = authService;
|
||||
_mainVm = mainVm;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task LoginAsync()
|
||||
{
|
||||
IsLoading = true;
|
||||
HasError = false;
|
||||
|
||||
var (success, message) = await _authService.LoginAsync(Rut, Password);
|
||||
|
||||
if (success)
|
||||
{
|
||||
_mainVm.NavigateToDashboard();
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage = message;
|
||||
HasError = true;
|
||||
}
|
||||
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Asistencia.Models;
|
||||
using Asistencia.Services;
|
||||
using Avalonia.Controls;
|
||||
using Asistencia.Views;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Asistencia.ViewModels;
|
||||
|
||||
public partial class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
private readonly AuthService _authService;
|
||||
private readonly AsistenciaService _asistenciaService;
|
||||
private object? _currentView;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _currentViewTitle = "Login";
|
||||
|
||||
public object? CurrentView
|
||||
{
|
||||
get => _currentView;
|
||||
set => SetProperty(ref _currentView, value);
|
||||
}
|
||||
|
||||
public MainWindowViewModel(AuthService authService, AsistenciaService asistenciaService)
|
||||
{
|
||||
_authService = authService;
|
||||
_asistenciaService = asistenciaService;
|
||||
CurrentView = new LoginViewModel(authService, this);
|
||||
}
|
||||
|
||||
public void NavigateToLogin()
|
||||
{
|
||||
CurrentViewTitle = "Login";
|
||||
CurrentView = new LoginViewModel(_authService, this);
|
||||
}
|
||||
|
||||
public void NavigateToDashboard()
|
||||
{
|
||||
CurrentViewTitle = "Panel Principal";
|
||||
CurrentView = new DashboardViewModel(_authService, _asistenciaService, this);
|
||||
}
|
||||
|
||||
public void NavigateToAsistencia(CursoAbierto curso)
|
||||
{
|
||||
CurrentViewTitle = "Asistencia";
|
||||
CurrentView = new AsistenciaViewModel(_asistenciaService, _authService, curso, this);
|
||||
}
|
||||
|
||||
public void NavigateToPerfil()
|
||||
{
|
||||
CurrentViewTitle = "Mi Perfil";
|
||||
CurrentView = new PerfilViewModel(_authService, this);
|
||||
}
|
||||
|
||||
public string UsuarioActual => _authService.CurrentUser?.NombreCompleto ?? "";
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Asistencia.Services;
|
||||
|
||||
namespace Asistencia.ViewModels;
|
||||
|
||||
public partial class PerfilViewModel : ObservableObject
|
||||
{
|
||||
private readonly AuthService _authService;
|
||||
private readonly MainWindowViewModel _mainVm;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _rut = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _nombreCompleto = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _email = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _fono = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _direccion = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _mensajeExito = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _tieneExito;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _mensajeError = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _tieneError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _mostrarCambioClave;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _claveActual = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _nuevaClave = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _confirmarClave = string.Empty;
|
||||
|
||||
public PerfilViewModel(AuthService authService, MainWindowViewModel mainVm)
|
||||
{
|
||||
_authService = authService;
|
||||
_mainVm = mainVm;
|
||||
|
||||
var user = authService.CurrentUser;
|
||||
if (user != null)
|
||||
{
|
||||
Rut = user.RutPasaporte;
|
||||
NombreCompleto = user.NombreCompleto;
|
||||
Email = user.Email;
|
||||
Fono = user.Fono;
|
||||
Direccion = user.Direccion;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task GuardarEmailAsync()
|
||||
{
|
||||
TieneExito = false;
|
||||
TieneError = false;
|
||||
|
||||
var (success, message) = await _authService.ActualizarEmailAsync(Email);
|
||||
if (success)
|
||||
{
|
||||
MensajeExito = message;
|
||||
TieneExito = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MensajeError = message;
|
||||
TieneError = true;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CambiarClaveAsync()
|
||||
{
|
||||
TieneExito = false;
|
||||
TieneError = false;
|
||||
|
||||
var (success, message) = await _authService.CambiarClaveAsync(ClaveActual, NuevaClave, ConfirmarClave);
|
||||
if (success)
|
||||
{
|
||||
MensajeExito = message;
|
||||
TieneExito = true;
|
||||
MostrarCambioClave = false;
|
||||
ClaveActual = string.Empty;
|
||||
NuevaClave = string.Empty;
|
||||
ConfirmarClave = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
MensajeError = message;
|
||||
TieneError = true;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleCambioClave()
|
||||
{
|
||||
MostrarCambioClave = !MostrarCambioClave;
|
||||
TieneExito = false;
|
||||
TieneError = false;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Volver()
|
||||
{
|
||||
_mainVm.NavigateToDashboard();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Asistencia.ViewModels;
|
||||
|
||||
public abstract class ViewModelBase : ObservableObject
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user