commit 359f0caf8500b7994937af050266bb353828a3c2 Author: Nurfog Date: Thu Sep 10 10:20:41 2026 -0300 Initial commit: HelpDesk ICHN - .NET 10 backend + Next.js 16 frontend diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17b17e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Build results +bin/ +obj/ +*.user +*.suo +*.cache +*.dll +*.exe +*.pdb + +# NuGet +packages/ +*.nupkg + +# Visual Studio +.vs/ +*.sln.docstates + +# Node +node_modules/ +.next/ +out/ + +# Docker +.env + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log + +# Database +*.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..57da6da --- /dev/null +++ b/README.md @@ -0,0 +1,176 @@ +# HelpDesk ICHN + +Sistema de HelpDesk inspirado en Freshdesk/Zammad, construido con .NET 10 + Next.js 15 + PostgreSQL. + +## Arquitectura + +``` +helpdesk/ +├── backend/ # .NET 10 API +│ ├── HelpDesk.API/ # Web API (controllers, services, middleware) +│ ├── HelpDesk.Data/ # EF Core DbContext + Repositories +│ ├── HelpDesk.Models/ # Entities, Enums, DTOs +│ └── HelpDesk.sln +├── frontend/ # Next.js 15 + Bootstrap 5 +│ └── src/ +│ ├── app/ # App Router pages +│ │ ├── (auth)/ # Login, Register +│ │ └── (app)/ # Dashboard, Tickets, KB, Users +│ ├── components/ # Reusable UI components +│ └── lib/ # API client, auth context +├── gateway/ # Caddy reverse proxy +├── scripts/ # DB init scripts +├── docker-compose.yml # Full stack orchestration +└── README.md +``` + +## Stack + +| Componente | Tecnología | +|--------------------|--------------------------| +| Backend API | .NET 10, C# 13 | +| ORM | Entity Framework Core 10 | +| Database | PostgreSQL 16 | +| Cache | Redis 7 | +| Frontend | Next.js 15 (App Router) | +| UI Framework | Bootstrap 5 + React-Bootstrap | +| Authentication | JWT Bearer | +| Reverse Proxy | Caddy 2 | +| Icons | Bootstrap Icons | +| Font | Fira Sans (matching SAM) | + +## Paleta de Colores (SAM) + +| Variable | Valor | Uso | +|----------------|-----------|------------------------------| +| Primary | #00285d | Navbar, sidebar, headings | +| Primary Light | #1a3f7a | Hover states, gradients | +| Gradient | #00285d → #1a4a8a | Sidebar, buttons, headers | +| Background | #f0f2f5 | Page background | +| Card Shadow | rgba(0,0,0,0.08) | Card elevation | + +## Desarrollo Local + +### Prerequisitos +- .NET 10 SDK +- Node.js 24+ +- PostgreSQL 16 (o usar Docker) +- Redis (o usar Docker) + +### Backend +```bash +cd backend +dotnet restore +dotnet run --project HelpDesk.API +# API en http://localhost:5000 +# Swagger en http://localhost:5000/swagger +``` + +### Frontend +```bash +cd frontend +npm install +npm run dev +# Frontend en http://localhost:3000 +``` + +### Docker (todo junto) +```bash +docker compose up -d +# Caddy: http://localhost:8080 +# API: http://localhost:5000 +# Frontend: http://localhost:3000 +# PostgreSQL: localhost:5433 +# Redis: localhost:6381 +``` + +## Datos por Defecto + +### Usuario Admin +- Email: admin@helpdesk.local +- Password: Admin123! +- Rol: SuperAdmin + +### Grupos +- Soporte General +- Ventas +- Desarrollo + +### Políticas SLA +- Estándar: Primera respuesta 4h, Resolución 24h +- Premium: Primera respuesta 1h, Resolución 8h + +## Endpoints API Principales + +### Auth +- `POST /api/auth/login` - Iniciar sesión +- `POST /api/auth/register` - Registrar usuario +- `GET /api/auth/me` - Usuario actual + +### Tickets +- `GET /api/tickets` - Listar tickets (filtros: status, priority, assigneeId, search) +- `GET /api/tickets/{id}` - Detalle de ticket +- `POST /api/tickets` - Crear ticket +- `PUT /api/tickets/{id}` - Actualizar ticket +- `DELETE /api/tickets/{id}` - Eliminar ticket +- `PUT /api/tickets/{id}/status` - Cambiar estado +- `PUT /api/tickets/{id}/assign` - Asignar agente +- `POST /api/tickets/{id}/comments` - Agregar comentario +- `GET /api/tickets/stats` - Estadísticas + +### Knowledge Base +- `GET /api/kb/articles/published` - Artículos publicados +- `GET /api/kb/search?q=termo` - Buscar artículos + +### Dashboard +- `GET /api/dashboard/stats` - Estadísticas del dashboard + +### Users +- `GET /api/users` - Listar usuarios +- `GET /api/users/agents` - Listar agentes + +### Groups +- `GET /api/groups` - Listar grupos + +### SLA +- `GET /api/sla` - Listar políticas SLA + +## Funcionalidades + +### Gestión de Tickets +- Crear, editar, eliminar tickets +- Estados: Abierto → En Progreso → Pendiente (Cliente/Agente) → Resuelto → Cerrado +- Prioridades: Baja, Media, Alta, Urgente +- Asignación a agentes y grupos +- Comentarios públicos e internos +- Historial de actividad +- Número de ticket automático (#XXXX) + +### Dashboard +- Resumen de tickets por estado +- Distribución por prioridad +- Tiempo promedio de respuesta +- Carga de trabajo por agente +- Tickets recientes + +### Knowledge Base +- Artículos organizados por categorías +- Búsqueda de artículos +- Contador de vistas +- Publicar/ocultar artículos + +### Autenticación +- JWT Bearer tokens +- Roles: SuperAdmin, Admin, Agente, Cliente +- Registro de nuevos usuarios +- Políticas de autorización por rol + +## Diseño + +Inspirado en la interfaz de SAM (sam.norteamericano.cl): +- Sidebar azul oscuro con gradiente +- Tarjetas blancas con sombras suaves +- Fondo gris claro +- Tipografía Fira Sans +- Badges de color para estados y prioridades +- Layout responsive (mobile-first) diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..e0c08cb --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,22 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src + +# Copy solution and project files for layer caching +COPY backend/HelpDesk.sln backend/HelpDesk.sln +COPY backend/HelpDesk.Models/HelpDesk.Models.csproj backend/HelpDesk.Models/ +COPY backend/HelpDesk.Data/HelpDesk.Data.csproj backend/HelpDesk.Data/ +COPY backend/HelpDesk.API/HelpDesk.API.csproj backend/HelpDesk.API/ +RUN dotnet restore backend/HelpDesk.API/HelpDesk.API.csproj + +# Copy everything and build +COPY backend/ backend/ +WORKDIR /src/backend +RUN dotnet publish HelpDesk.API/HelpDesk.API.csproj -c Release -o /app/publish --no-restore + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime +WORKDIR /app +COPY --from=build /app/publish . +EXPOSE 5000 +ENV ASPNETCORE_URLS=http://+:5000 +ENV ASPNETCORE_ENVIRONMENT=Production +ENTRYPOINT ["dotnet", "HelpDesk.API.dll"] diff --git a/backend/HelpDesk.API/Controllers/AuthController.cs b/backend/HelpDesk.API/Controllers/AuthController.cs new file mode 100644 index 0000000..16b5ff5 --- /dev/null +++ b/backend/HelpDesk.API/Controllers/AuthController.cs @@ -0,0 +1,115 @@ +using System.Security.Claims; +using HelpDesk.API.Services; +using HelpDesk.Data.Repositories; +using HelpDesk.Models.DTOs; +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HelpDesk.API.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class AuthController : ControllerBase +{ + private readonly IUserRepository _userRepository; + private readonly IJwtService _jwtService; + private readonly IPasswordService _passwordService; + + public AuthController(IUserRepository userRepository, IJwtService jwtService, IPasswordService passwordService) + { + _userRepository = userRepository; + _jwtService = jwtService; + _passwordService = passwordService; + } + + [HttpPost("login")] + public async Task> Login([FromBody] LoginRequest request) + { + if (string.IsNullOrWhiteSpace(request.Email) || string.IsNullOrWhiteSpace(request.Password)) + return BadRequest(new { error = "Email and password are required." }); + + var user = await _userRepository.GetByEmailAsync(request.Email); + if (user == null || !_passwordService.VerifyPassword(request.Password, user.PasswordHash)) + return Unauthorized(new { error = "Invalid email or password." }); + + if (!user.IsActive) + return Unauthorized(new { error = "Account is deactivated." }); + + var token = _jwtService.GenerateToken(user.Id, user.Email, user.Role); + + return Ok(new AuthResponse + { + Token = token, + Expiration = DateTime.UtcNow.AddMinutes(60), + User = MapToDto(user) + }); + } + + [HttpPost("register")] + public async Task> Register([FromBody] RegisterRequest request) + { + if (string.IsNullOrWhiteSpace(request.Email) || string.IsNullOrWhiteSpace(request.Password)) + return BadRequest(new { error = "Email and password are required." }); + if (string.IsNullOrWhiteSpace(request.FirstName) || string.IsNullOrWhiteSpace(request.LastName)) + return BadRequest(new { error = "First name and last name are required." }); + if (request.Password.Length < 6) + return BadRequest(new { error = "Password must be at least 6 characters." }); + + if (await _userRepository.ExistsAsync(request.Email)) + return Conflict(new { error = "Email is already registered." }); + + var user = new User + { + Email = request.Email, + PasswordHash = _passwordService.HashPassword(request.Password), + FirstName = request.FirstName, + LastName = request.LastName, + Role = UserRole.Customer, + CreatedAt = DateTime.UtcNow + }; + + var created = await _userRepository.CreateAsync(user); + var token = _jwtService.GenerateToken(created.Id, created.Email, created.Role); + + return CreatedAtAction(nameof(GetMe), new AuthResponse + { + Token = token, + Expiration = DateTime.UtcNow.AddMinutes(60), + User = MapToDto(created) + }); + } + + [Authorize] + [HttpGet("me")] + public async Task> GetMe() + { + var userId = GetUserId(); + if (userId == null) return Unauthorized(); + + var user = await _userRepository.GetByIdAsync(userId.Value); + if (user == null) return NotFound(new { error = "User not found." }); + + return Ok(MapToDto(user)); + } + + private Guid? GetUserId() + { + var claim = User.FindFirst("userId") ?? User.FindFirst(ClaimTypes.NameIdentifier); + return claim != null && Guid.TryParse(claim.Value, out var id) ? id : null; + } + + private static UserDto MapToDto(User user) => new() + { + Id = user.Id, + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName, + Role = user.Role, + IsActive = user.IsActive, + CreatedAt = user.CreatedAt, + PhoneNumber = user.PhoneNumber, + AvatarUrl = user.AvatarUrl + }; +} diff --git a/backend/HelpDesk.API/Controllers/DashboardController.cs b/backend/HelpDesk.API/Controllers/DashboardController.cs new file mode 100644 index 0000000..bd32cae --- /dev/null +++ b/backend/HelpDesk.API/Controllers/DashboardController.cs @@ -0,0 +1,67 @@ +using System.Security.Claims; +using HelpDesk.Data.Repositories; +using HelpDesk.Models.DTOs; +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HelpDesk.API.Controllers; + +[ApiController] +[Route("api/[controller]")] +[Authorize(Policy = "Agent")] +public class DashboardController : ControllerBase +{ + private readonly ITicketRepository _ticketRepository; + private readonly IUserRepository _userRepository; + + public DashboardController(ITicketRepository ticketRepository, IUserRepository userRepository) + { + _ticketRepository = ticketRepository; + _userRepository = userRepository; + } + + [HttpGet("stats")] + public async Task> GetStats() + { + var agents = await _userRepository.GetByRoleAsync(UserRole.Agent); + var agentWorkloads = new List(); + + foreach (var agent in agents) + { + var activeTickets = await _ticketRepository.GetCountAsync( + assigneeId: agent.Id, + status: TicketStatus.Open); + + var inProgressTickets = await _ticketRepository.GetCountAsync( + assigneeId: agent.Id, + status: TicketStatus.InProgress); + + agentWorkloads.Add(new AgentWorkloadDto + { + UserId = agent.Id, + UserName = $"{agent.FirstName} {agent.LastName}", + ActiveTickets = activeTickets + inProgressTickets, + ResolvedThisWeek = 0 + }); + } + + return Ok(new DashboardStatsDto + { + TotalTickets = await _ticketRepository.GetTotalCountAsync(), + OpenTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Open), + InProgressTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.InProgress), + PendingTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Pending), + ResolvedTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Resolved), + ClosedTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Closed), + CriticalTickets = await _ticketRepository.GetCountByPriorityAsync(TicketPriority.Critical), + HighTickets = await _ticketRepository.GetCountByPriorityAsync(TicketPriority.High), + MediumTickets = await _ticketRepository.GetCountByPriorityAsync(TicketPriority.Medium), + LowTickets = await _ticketRepository.GetCountByPriorityAsync(TicketPriority.Low), + AverageResponseTimeMinutes = await _ticketRepository.GetAverageResponseTimeAsync(), + AverageResolutionTimeMinutes = await _ticketRepository.GetAverageResolutionTimeAsync(), + AgentWorkloads = agentWorkloads + }); + } +} diff --git a/backend/HelpDesk.API/Controllers/GroupsController.cs b/backend/HelpDesk.API/Controllers/GroupsController.cs new file mode 100644 index 0000000..a7efdee --- /dev/null +++ b/backend/HelpDesk.API/Controllers/GroupsController.cs @@ -0,0 +1,85 @@ +using HelpDesk.Data.Repositories; +using HelpDesk.Models.DTOs; +using HelpDesk.Models.Entities; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HelpDesk.API.Controllers; + +[ApiController] +[Route("api/[controller]")] +[Authorize(Policy = "Admin")] +public class GroupsController : ControllerBase +{ + private readonly IGroupRepository _groupRepository; + + public GroupsController(IGroupRepository groupRepository) + { + _groupRepository = groupRepository; + } + + [HttpGet] + public async Task>> GetGroups() + { + var groups = await _groupRepository.GetAllAsync(); + return Ok(groups.Select(MapToDto)); + } + + [HttpGet("{id}")] + public async Task> GetGroup(Guid id) + { + var group = await _groupRepository.GetByIdAsync(id); + if (group == null) return NotFound(new { error = "Group not found." }); + return Ok(MapToDto(group)); + } + + [HttpPost] + public async Task> CreateGroup([FromBody] CreateGroupRequest request) + { + if (string.IsNullOrWhiteSpace(request.Name)) + return BadRequest(new { error = "Name is required." }); + + var group = new Group + { + Name = request.Name, + Description = request.Description, + IsActive = true, + CreatedAt = DateTime.UtcNow + }; + + var created = await _groupRepository.CreateAsync(group); + return CreatedAtAction(nameof(GetGroup), new { id = created.Id }, MapToDto(created)); + } + + [HttpPut("{id}")] + public async Task> UpdateGroup(Guid id, [FromBody] CreateGroupRequest request) + { + var group = await _groupRepository.GetByIdAsync(id); + if (group == null) return NotFound(new { error = "Group not found." }); + + group.Name = request.Name; + if (request.Description != null) group.Description = request.Description; + + var updated = await _groupRepository.UpdateAsync(group); + if (updated == null) return StatusCode(500, new { error = "Failed to update group." }); + + return Ok(MapToDto(updated)); + } + + [HttpDelete("{id}")] + public async Task DeleteGroup(Guid id) + { + var deleted = await _groupRepository.DeleteAsync(id); + if (!deleted) return NotFound(new { error = "Group not found." }); + return NoContent(); + } + + private static GroupDto MapToDto(Group g) => new() + { + Id = g.Id, + Name = g.Name, + Description = g.Description, + IsActive = g.IsActive, + CreatedAt = g.CreatedAt + }; +} diff --git a/backend/HelpDesk.API/Controllers/KnowledgeBaseController.cs b/backend/HelpDesk.API/Controllers/KnowledgeBaseController.cs new file mode 100644 index 0000000..5607fa3 --- /dev/null +++ b/backend/HelpDesk.API/Controllers/KnowledgeBaseController.cs @@ -0,0 +1,206 @@ +using System.Security.Claims; +using HelpDesk.Data.Repositories; +using HelpDesk.Models.DTOs; +using HelpDesk.Models.Entities; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HelpDesk.API.Controllers; + +[ApiController] +[Route("api/kb")] +[Authorize] +public class KnowledgeBaseController : ControllerBase +{ + private readonly IKnowledgeBaseRepository _kbRepository; + + public KnowledgeBaseController(IKnowledgeBaseRepository kbRepository) + { + _kbRepository = kbRepository; + } + + // ===== Articles ===== + + [HttpGet("articles")] + [Authorize(Policy = "Agent")] + public async Task>> GetAllArticles() + { + var articles = await _kbRepository.GetAllArticlesAsync(); + return Ok(articles.Select(MapArticleToDto)); + } + + [HttpGet("articles/published")] + [AllowAnonymous] + public async Task>> GetPublishedArticles() + { + var articles = await _kbRepository.GetPublishedArticlesAsync(); + return Ok(articles.Select(MapArticleToDto)); + } + + [HttpGet("search")] + [AllowAnonymous] + public async Task>> SearchArticles([FromQuery] string q) + { + if (string.IsNullOrWhiteSpace(q)) + return BadRequest(new { error = "Search query is required." }); + + var articles = await _kbRepository.SearchArticlesAsync(q); + return Ok(articles.Select(MapArticleToDto)); + } + + [HttpGet("articles/{id}")] + public async Task> GetArticle(Guid id) + { + var article = await _kbRepository.GetArticleByIdAsync(id); + if (article == null) return NotFound(new { error = "Article not found." }); + return Ok(MapArticleToDto(article)); + } + + [HttpPost("articles")] + [Authorize(Policy = "Agent")] + public async Task> CreateArticle([FromBody] CreateArticleRequest request) + { + if (string.IsNullOrWhiteSpace(request.Title)) + return BadRequest(new { error = "Title is required." }); + if (string.IsNullOrWhiteSpace(request.Content)) + return BadRequest(new { error = "Content is required." }); + + var userId = GetUserId(); + if (userId == null) return Unauthorized(); + + var article = new KnowledgeBaseArticle + { + Title = request.Title, + Content = request.Content, + CategoryId = request.CategoryId, + IsPublished = request.IsPublished, + AuthorId = userId.Value, + CreatedAt = DateTime.UtcNow + }; + + var created = await _kbRepository.CreateArticleAsync(article); + var full = await _kbRepository.GetArticleByIdAsync(created.Id); + return CreatedAtAction(nameof(GetArticle), new { id = created.Id }, MapArticleToDto(full!)); + } + + [HttpPut("articles/{id}")] + [Authorize(Policy = "Agent")] + public async Task> UpdateArticle(Guid id, [FromBody] UpdateArticleRequest request) + { + var article = await _kbRepository.GetArticleByIdAsync(id); + if (article == null) return NotFound(new { error = "Article not found." }); + + if (request.Title != null) article.Title = request.Title; + if (request.Content != null) article.Content = request.Content; + if (request.CategoryId.HasValue) article.CategoryId = request.CategoryId.Value; + if (request.IsPublished.HasValue) article.IsPublished = request.IsPublished.Value; + + var updated = await _kbRepository.UpdateArticleAsync(article); + if (updated == null) return StatusCode(500, new { error = "Failed to update article." }); + + var full = await _kbRepository.GetArticleByIdAsync(id); + return Ok(MapArticleToDto(full!)); + } + + [HttpDelete("articles/{id}")] + [Authorize(Policy = "Admin")] + public async Task DeleteArticle(Guid id) + { + var deleted = await _kbRepository.DeleteArticleAsync(id); + if (!deleted) return NotFound(new { error = "Article not found." }); + return NoContent(); + } + + // ===== Categories ===== + + [HttpGet("categories")] + public async Task>> GetCategories() + { + var categories = await _kbRepository.GetAllCategoriesAsync(); + return Ok(categories.Select(MapCategoryToDto)); + } + + [HttpGet("categories/{id}")] + public async Task> GetCategory(Guid id) + { + var category = await _kbRepository.GetCategoryByIdAsync(id); + if (category == null) return NotFound(new { error = "Category not found." }); + return Ok(MapCategoryToDto(category)); + } + + [HttpPost("categories")] + [Authorize(Policy = "Admin")] + public async Task> CreateCategory([FromBody] CreateCategoryRequest request) + { + if (string.IsNullOrWhiteSpace(request.Name)) + return BadRequest(new { error = "Name is required." }); + + var category = new KnowledgeBaseCategory + { + Name = request.Name, + Description = request.Description, + SortOrder = request.SortOrder, + CreatedAt = DateTime.UtcNow + }; + + var created = await _kbRepository.CreateCategoryAsync(category); + return CreatedAtAction(nameof(GetCategory), new { id = created.Id }, MapCategoryToDto(created)); + } + + [HttpPut("categories/{id}")] + [Authorize(Policy = "Admin")] + public async Task> UpdateCategory(Guid id, [FromBody] CreateCategoryRequest request) + { + var category = await _kbRepository.GetCategoryByIdAsync(id); + if (category == null) return NotFound(new { error = "Category not found." }); + + category.Name = request.Name; + category.Description = request.Description; + category.SortOrder = request.SortOrder; + + var updated = await _kbRepository.UpdateCategoryAsync(category); + if (updated == null) return StatusCode(500, new { error = "Failed to update category." }); + + return Ok(MapCategoryToDto(updated)); + } + + [HttpDelete("categories/{id}")] + [Authorize(Policy = "Admin")] + public async Task DeleteCategory(Guid id) + { + var deleted = await _kbRepository.DeleteCategoryAsync(id); + if (!deleted) return NotFound(new { error = "Category not found." }); + return NoContent(); + } + + private Guid? GetUserId() + { + var claim = User.FindFirst("userId") ?? User.FindFirst(ClaimTypes.NameIdentifier); + return claim != null && Guid.TryParse(claim.Value, out var id) ? id : null; + } + + private static ArticleDto MapArticleToDto(KnowledgeBaseArticle a) => new() + { + Id = a.Id, + Title = a.Title, + Content = a.Content, + IsPublished = a.IsPublished, + CategoryId = a.CategoryId, + CategoryName = a.Category?.Name ?? "Unknown", + AuthorId = a.AuthorId, + AuthorName = a.Author != null ? $"{a.Author.FirstName} {a.Author.LastName}" : "Unknown", + ViewCount = a.ViewCount, + CreatedAt = a.CreatedAt, + UpdatedAt = a.UpdatedAt + }; + + private static CategoryDto MapCategoryToDto(KnowledgeBaseCategory c) => new() + { + Id = c.Id, + Name = c.Name, + Description = c.Description, + SortOrder = c.SortOrder, + ArticleCount = c.Articles?.Count ?? 0, + CreatedAt = c.CreatedAt + }; +} diff --git a/backend/HelpDesk.API/Controllers/SLAController.cs b/backend/HelpDesk.API/Controllers/SLAController.cs new file mode 100644 index 0000000..490a757 --- /dev/null +++ b/backend/HelpDesk.API/Controllers/SLAController.cs @@ -0,0 +1,94 @@ +using HelpDesk.Data.Repositories; +using HelpDesk.Models.DTOs; +using HelpDesk.Models.Entities; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HelpDesk.API.Controllers; + +[ApiController] +[Route("api/[controller]")] +[Authorize(Policy = "Admin")] +public class SLAController : ControllerBase +{ + private readonly ISLARepository _slaRepository; + + public SLAController(ISLARepository slaRepository) + { + _slaRepository = slaRepository; + } + + [HttpGet] + public async Task>> GetSLAPolicies() + { + var policies = await _slaRepository.GetAllAsync(); + return Ok(policies.Select(MapToDto)); + } + + [HttpGet("{id}")] + public async Task> GetSLAPolicy(Guid id) + { + var policy = await _slaRepository.GetByIdAsync(id); + if (policy == null) return NotFound(new { error = "SLA policy not found." }); + return Ok(MapToDto(policy)); + } + + [HttpPost] + public async Task> CreateSLAPolicy([FromBody] CreateSLARequest request) + { + if (string.IsNullOrWhiteSpace(request.Name)) + return BadRequest(new { error = "Name is required." }); + if (request.ResponseTimeHours <= 0 || request.ResolutionTimeHours <= 0) + return BadRequest(new { error = "Response and resolution times must be positive." }); + + var policy = new SLAPolicy + { + Name = request.Name, + Description = request.Description, + ResponseTimeHours = request.ResponseTimeHours, + ResolutionTimeHours = request.ResolutionTimeHours, + IsActive = true, + CreatedAt = DateTime.UtcNow + }; + + var created = await _slaRepository.CreateAsync(policy); + return CreatedAtAction(nameof(GetSLAPolicy), new { id = created.Id }, MapToDto(created)); + } + + [HttpPut("{id}")] + public async Task> UpdateSLAPolicy(Guid id, [FromBody] UpdateSLARequest request) + { + var policy = await _slaRepository.GetByIdAsync(id); + if (policy == null) return NotFound(new { error = "SLA policy not found." }); + + if (request.Name != null) policy.Name = request.Name; + if (request.Description != null) policy.Description = request.Description; + if (request.ResponseTimeHours.HasValue) policy.ResponseTimeHours = request.ResponseTimeHours.Value; + if (request.ResolutionTimeHours.HasValue) policy.ResolutionTimeHours = request.ResolutionTimeHours.Value; + if (request.IsActive.HasValue) policy.IsActive = request.IsActive.Value; + + var updated = await _slaRepository.UpdateAsync(policy); + if (updated == null) return StatusCode(500, new { error = "Failed to update SLA policy." }); + + return Ok(MapToDto(updated)); + } + + [HttpDelete("{id}")] + public async Task DeleteSLAPolicy(Guid id) + { + var deleted = await _slaRepository.DeleteAsync(id); + if (!deleted) return NotFound(new { error = "SLA policy not found." }); + return NoContent(); + } + + private static SLADto MapToDto(SLAPolicy s) => new() + { + Id = s.Id, + Name = s.Name, + Description = s.Description, + ResponseTimeHours = s.ResponseTimeHours, + ResolutionTimeHours = s.ResolutionTimeHours, + IsActive = s.IsActive, + CreatedAt = s.CreatedAt + }; +} diff --git a/backend/HelpDesk.API/Controllers/TicketsController.cs b/backend/HelpDesk.API/Controllers/TicketsController.cs new file mode 100644 index 0000000..37f2821 --- /dev/null +++ b/backend/HelpDesk.API/Controllers/TicketsController.cs @@ -0,0 +1,204 @@ +using System.Security.Claims; +using HelpDesk.Data.Repositories; +using HelpDesk.Models.DTOs; +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HelpDesk.API.Controllers; + +[ApiController] +[Route("api/[controller]")] +[Authorize] +public class TicketsController : ControllerBase +{ + private readonly ITicketRepository _ticketRepository; + private readonly IUserRepository _userRepository; + + public TicketsController(ITicketRepository ticketRepository, IUserRepository userRepository) + { + _ticketRepository = ticketRepository; + _userRepository = userRepository; + } + + [HttpGet] + public async Task> GetTickets( + [FromQuery] TicketStatus? status, + [FromQuery] TicketPriority? priority, + [FromQuery] Guid? assigneeId, + [FromQuery] Guid? requesterId, + [FromQuery] int page = 1, + [FromQuery] int pageSize = 20) + { + var userId = GetUserId(); + if (userId == null) return Unauthorized(); + + var user = await _userRepository.GetByIdAsync(userId.Value); + if (user == null) return Unauthorized(); + + if (user.Role == UserRole.Customer) + requesterId = userId.Value; + + var tickets = await _ticketRepository.GetAllAsync(status, priority, assigneeId, requesterId, page, pageSize); + var totalCount = await _ticketRepository.GetCountAsync(status, priority, assigneeId, requesterId); + + return Ok(new TicketListResponse + { + Items = tickets.Select(MapToDto).ToList(), + TotalCount = totalCount, + Page = page, + PageSize = pageSize + }); + } + + [HttpGet("stats")] + public async Task> GetStats() + { + return Ok(new TicketStatsDto + { + TotalTickets = await _ticketRepository.GetTotalCountAsync(), + OpenTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Open), + InProgressTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.InProgress), + PendingTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Pending), + ResolvedTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Resolved), + ClosedTickets = await _ticketRepository.GetCountByStatusAsync(TicketStatus.Closed) + }); + } + + [HttpGet("{id}")] + public async Task> GetTicket(Guid id) + { + var ticket = await _ticketRepository.GetByIdAsync(id); + if (ticket == null) return NotFound(new { error = "Ticket not found." }); + + var userId = GetUserId(); + if (userId == null) return Unauthorized(); + + var user = await _userRepository.GetByIdAsync(userId.Value); + if (user?.Role == UserRole.Customer && ticket.RequesterId != userId.Value) + return Forbid(); + + return Ok(MapToDto(ticket)); + } + + [HttpPost] + public async Task> CreateTicket([FromBody] CreateTicketRequest request) + { + if (string.IsNullOrWhiteSpace(request.Title)) + return BadRequest(new { error = "Title is required." }); + if (string.IsNullOrWhiteSpace(request.Description)) + return BadRequest(new { error = "Description is required." }); + + var userId = GetUserId(); + if (userId == null) return Unauthorized(); + + var ticket = new Ticket + { + Title = request.Title, + Description = request.Description, + Priority = request.Priority, + RequesterId = userId.Value, + GroupId = request.GroupId, + SlaPolicyId = request.SlaPolicyId, + TicketNumber = $"TK-{DateTime.UtcNow:yyyyMMddHHmmss}", + Status = TicketStatus.Open, + CreatedAt = DateTime.UtcNow + }; + + var created = await _ticketRepository.CreateAsync(ticket); + var full = await _ticketRepository.GetByIdAsync(created.Id); + + return CreatedAtAction(nameof(GetTicket), new { id = created.Id }, MapToDto(full!)); + } + + [HttpPut("{id}")] + public async Task> UpdateTicket(Guid id, [FromBody] UpdateTicketRequest request) + { + var ticket = await _ticketRepository.GetByIdAsync(id); + if (ticket == null) return NotFound(new { error = "Ticket not found." }); + + if (!string.IsNullOrWhiteSpace(request.Title)) ticket.Title = request.Title; + if (!string.IsNullOrWhiteSpace(request.Description)) ticket.Description = request.Description; + if (request.Priority.HasValue) ticket.Priority = request.Priority.Value; + + var updated = await _ticketRepository.UpdateAsync(ticket); + if (updated == null) return StatusCode(500, new { error = "Failed to update ticket." }); + + var full = await _ticketRepository.GetByIdAsync(id); + return Ok(MapToDto(full!)); + } + + [HttpDelete("{id}")] + [Authorize(Policy = "Admin")] + public async Task DeleteTicket(Guid id) + { + var deleted = await _ticketRepository.DeleteAsync(id); + if (!deleted) return NotFound(new { error = "Ticket not found." }); + return NoContent(); + } + + [HttpPut("{id}/status")] + [Authorize(Policy = "Agent")] + public async Task UpdateStatus(Guid id, [FromBody] UpdateStatusRequest request) + { + var ticket = await _ticketRepository.GetByIdAsync(id); + if (ticket == null) return NotFound(new { error = "Ticket not found." }); + + ticket.Status = request.Status; + await _ticketRepository.UpdateAsync(ticket); + + return Ok(new { message = "Status updated.", status = request.Status.ToString() }); + } + + [HttpPut("{id}/assign")] + [Authorize(Policy = "Agent")] + public async Task AssignTicket(Guid id, [FromBody] AssignTicketRequest request) + { + var ticket = await _ticketRepository.GetByIdAsync(id); + if (ticket == null) return NotFound(new { error = "Ticket not found." }); + + if (request.AssigneeId.HasValue) + { + var assignee = await _userRepository.GetByIdAsync(request.AssigneeId.Value); + if (assignee == null) return BadRequest(new { error = "Assignee not found." }); + if (assignee.Role != UserRole.Agent && assignee.Role != UserRole.Admin) + return BadRequest(new { error = "Assignee must be an agent or admin." }); + } + + ticket.AssigneeId = request.AssigneeId; + if (request.AssigneeId.HasValue && ticket.Status == TicketStatus.Open) + ticket.Status = TicketStatus.InProgress; + + await _ticketRepository.UpdateAsync(ticket); + return Ok(new { message = "Ticket assigned.", assigneeId = request.AssigneeId }); + } + + private Guid? GetUserId() + { + var claim = User.FindFirst("userId") ?? User.FindFirst(ClaimTypes.NameIdentifier); + return claim != null && Guid.TryParse(claim.Value, out var id) ? id : null; + } + + private static TicketDto MapToDto(Ticket t) => new() + { + Id = t.Id, + Title = t.Title, + Description = t.Description, + Status = t.Status, + Priority = t.Priority, + TicketNumber = t.TicketNumber, + RequesterId = t.RequesterId, + RequesterName = t.Requester != null ? $"{t.Requester.FirstName} {t.Requester.LastName}" : "Unknown", + AssigneeId = t.AssigneeId, + AssigneeName = t.Assignee != null ? $"{t.Assignee.FirstName} {t.Assignee.LastName}" : null, + GroupId = t.GroupId, + GroupName = t.Group?.Name, + SlaPolicyId = t.SlaPolicyId, + SlaPolicyName = t.SlaPolicy?.Name, + CreatedAt = t.CreatedAt, + UpdatedAt = t.UpdatedAt, + ResolvedAt = t.ResolvedAt, + CommentCount = t.Comments?.Count ?? 0 + }; +} diff --git a/backend/HelpDesk.API/Controllers/UsersController.cs b/backend/HelpDesk.API/Controllers/UsersController.cs new file mode 100644 index 0000000..4f7faed --- /dev/null +++ b/backend/HelpDesk.API/Controllers/UsersController.cs @@ -0,0 +1,99 @@ +using System.Security.Claims; +using HelpDesk.Data.Repositories; +using HelpDesk.Models.DTOs; +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace HelpDesk.API.Controllers; + +[ApiController] +[Route("api/[controller]")] +[Authorize] +public class UsersController : ControllerBase +{ + private readonly IUserRepository _userRepository; + + public UsersController(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + [HttpGet] + [Authorize(Policy = "Admin")] + public async Task>> GetUsers() + { + var users = await _userRepository.GetAllAsync(); + return Ok(users.Select(MapToDto)); + } + + [HttpGet("agents")] + public async Task>> GetAgents() + { + var agents = await _userRepository.GetByRoleAsync(UserRole.Agent); + return Ok(agents.Select(MapToDto)); + } + + [HttpGet("{id}")] + public async Task> GetUser(Guid id) + { + var user = await _userRepository.GetByIdAsync(id); + if (user == null) return NotFound(new { error = "User not found." }); + return Ok(MapToDto(user)); + } + + [HttpPut("{id}")] + public async Task> UpdateUser(Guid id, [FromBody] UpdateUserRequest request) + { + var currentUserId = GetUserId(); + if (currentUserId == null) return Unauthorized(); + + var currentUser = await _userRepository.GetByIdAsync(currentUserId.Value); + if (currentUser == null) return Unauthorized(); + + // Users can update themselves; Admins/SuperAdmins can update others + if (currentUserId.Value != id && currentUser.Role < UserRole.Admin) + return Forbid(); + + var user = await _userRepository.GetByIdAsync(id); + if (user == null) return NotFound(new { error = "User not found." }); + + if (request.FirstName != null) user.FirstName = request.FirstName; + if (request.LastName != null) user.LastName = request.LastName; + if (request.Email != null) user.Email = request.Email; + if (request.PhoneNumber != null) user.PhoneNumber = request.PhoneNumber; + if (request.AvatarUrl != null) user.AvatarUrl = request.AvatarUrl; + + // Only admins can change role and active status + if (currentUser.Role >= UserRole.Admin) + { + if (request.Role.HasValue) user.Role = request.Role.Value; + if (request.IsActive.HasValue) user.IsActive = request.IsActive.Value; + } + + var updated = await _userRepository.UpdateAsync(user); + if (updated == null) return StatusCode(500, new { error = "Failed to update user." }); + + return Ok(MapToDto(updated)); + } + + private Guid? GetUserId() + { + var claim = User.FindFirst("userId") ?? User.FindFirst(ClaimTypes.NameIdentifier); + return claim != null && Guid.TryParse(claim.Value, out var id) ? id : null; + } + + private static UserDto MapToDto(User u) => new() + { + Id = u.Id, + Email = u.Email, + FirstName = u.FirstName, + LastName = u.LastName, + Role = u.Role, + IsActive = u.IsActive, + CreatedAt = u.CreatedAt, + PhoneNumber = u.PhoneNumber, + AvatarUrl = u.AvatarUrl + }; +} diff --git a/backend/HelpDesk.API/HelpDesk.API.csproj b/backend/HelpDesk.API/HelpDesk.API.csproj new file mode 100644 index 0000000..0f373aa --- /dev/null +++ b/backend/HelpDesk.API/HelpDesk.API.csproj @@ -0,0 +1,24 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + + + + + + + + + diff --git a/backend/HelpDesk.API/HelpDesk.API.http b/backend/HelpDesk.API/HelpDesk.API.http new file mode 100644 index 0000000..d49407f --- /dev/null +++ b/backend/HelpDesk.API/HelpDesk.API.http @@ -0,0 +1,6 @@ +@HelpDesk.API_HostAddress = http://localhost:5098 + +GET {{HelpDesk.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/backend/HelpDesk.API/Middleware/ExceptionMiddleware.cs b/backend/HelpDesk.API/Middleware/ExceptionMiddleware.cs new file mode 100644 index 0000000..5322d5c --- /dev/null +++ b/backend/HelpDesk.API/Middleware/ExceptionMiddleware.cs @@ -0,0 +1,56 @@ +using System.Net; +using System.Text.Json; + +namespace HelpDesk.API.Middleware; + +public class ExceptionMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public ExceptionMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + _logger.LogError(ex, "An unhandled exception occurred: {Message}", ex.Message); + await HandleExceptionAsync(context, ex); + } + } + + private static async Task HandleExceptionAsync(HttpContext context, Exception exception) + { + context.Response.ContentType = "application/json"; + + var (statusCode, message) = exception switch + { + ArgumentException argEx => (HttpStatusCode.BadRequest, argEx.Message), + KeyNotFoundException keyEx => (HttpStatusCode.NotFound, keyEx.Message), + UnauthorizedAccessException => (HttpStatusCode.Unauthorized, "Unauthorized access."), + InvalidOperationException opEx => (HttpStatusCode.BadRequest, opEx.Message), + _ => (HttpStatusCode.InternalServerError, "An unexpected error occurred.") + }; + + context.Response.StatusCode = (int)statusCode; + + var response = new + { + error = message, + statusCode = (int)statusCode + }; + + await context.Response.WriteAsync(JsonSerializer.Serialize(response, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + })); + } +} diff --git a/backend/HelpDesk.API/Program.cs b/backend/HelpDesk.API/Program.cs new file mode 100644 index 0000000..02faaef --- /dev/null +++ b/backend/HelpDesk.API/Program.cs @@ -0,0 +1,134 @@ +using System.Text; +using HelpDesk.API.Middleware; +using HelpDesk.API.Services; +using HelpDesk.Data; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; + +var builder = WebApplication.CreateBuilder(args); + +// Controllers +builder.Services.AddControllers(); + +// Database +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") + ?? Environment.GetEnvironmentVariable("DB_CONNECTION") + ?? "Host=localhost;Port=5432;Database=helpdesk;Username=postgres;Password=postgres"; + +builder.Services.AddDbContext(options => + options.UseNpgsql(connectionString)); + +// Repositories +builder.Services.AddRepositories(); + +// Services +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +// Authentication +var jwtSecretKey = builder.Configuration["JwtSettings:SecretKey"] ?? "YourSuperSecretKeyHereThatShouldBeAtLeast32CharactersLong!!"; + +builder.Services.AddAuthentication(options => +{ + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) +.AddJwtBearer(options => +{ + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = builder.Configuration["JwtSettings:Issuer"] ?? "HelpDesk", + ValidAudience = builder.Configuration["JwtSettings:Audience"] ?? "HelpDeskAPI", + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecretKey)), + ClockSkew = TimeSpan.Zero + }; +}); + +// Authorization Policies +builder.Services.AddAuthorization(options => +{ + options.AddPolicy("SuperAdmin", policy => policy.RequireRole("SuperAdmin")); + options.AddPolicy("Admin", policy => policy.RequireRole("SuperAdmin", "Admin")); + options.AddPolicy("Agent", policy => policy.RequireRole("SuperAdmin", "Admin", "Agent")); + options.AddPolicy("Customer", policy => policy.RequireRole("SuperAdmin", "Admin", "Agent", "Customer")); +}); + +// CORS +var frontendUrl = builder.Configuration["FrontendUrl"] ?? "http://localhost:3000"; +builder.Services.AddCors(options => +{ + options.AddPolicy("Frontend", policy => + { + policy.WithOrigins(frontendUrl) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials(); + }); +}); + +// Swagger +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new OpenApiInfo + { + Title = "HelpDesk API", + Version = "v1", + Description = "HelpDesk API for ticket management" + }); + + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Name = "Authorization", + Type = SecuritySchemeType.Http, + Scheme = "Bearer", + BearerFormat = "JWT", + In = ParameterLocation.Header, + Description = "Enter your JWT token" + }); + + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + Array.Empty() + } + }); +}); + +var app = builder.Build(); + +// Exception Middleware +app.UseMiddleware(); + +// Swagger +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +// CORS +app.UseCors("Frontend"); + +// Auth +app.UseAuthentication(); +app.UseAuthorization(); + +// Map Controllers +app.MapControllers(); + +app.Run(); diff --git a/backend/HelpDesk.API/Properties/launchSettings.json b/backend/HelpDesk.API/Properties/launchSettings.json new file mode 100644 index 0000000..b224eb3 --- /dev/null +++ b/backend/HelpDesk.API/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5098", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/backend/HelpDesk.API/Services/IJwtService.cs b/backend/HelpDesk.API/Services/IJwtService.cs new file mode 100644 index 0000000..86f95ab --- /dev/null +++ b/backend/HelpDesk.API/Services/IJwtService.cs @@ -0,0 +1,10 @@ +using System.Security.Claims; +using HelpDesk.Models.Enums; + +namespace HelpDesk.API.Services; + +public interface IJwtService +{ + string GenerateToken(Guid userId, string email, UserRole role); + ClaimsPrincipal? ValidateToken(string token); +} diff --git a/backend/HelpDesk.API/Services/IPasswordService.cs b/backend/HelpDesk.API/Services/IPasswordService.cs new file mode 100644 index 0000000..fba2a8a --- /dev/null +++ b/backend/HelpDesk.API/Services/IPasswordService.cs @@ -0,0 +1,7 @@ +namespace HelpDesk.API.Services; + +public interface IPasswordService +{ + string HashPassword(string password); + bool VerifyPassword(string password, string hashedPassword); +} diff --git a/backend/HelpDesk.API/Services/JwtService.cs b/backend/HelpDesk.API/Services/JwtService.cs new file mode 100644 index 0000000..3c4828a --- /dev/null +++ b/backend/HelpDesk.API/Services/JwtService.cs @@ -0,0 +1,75 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using HelpDesk.Models.Enums; +using Microsoft.IdentityModel.Tokens; + +namespace HelpDesk.API.Services; + +public class JwtService : IJwtService +{ + private readonly IConfiguration _configuration; + + public JwtService(IConfiguration configuration) + { + _configuration = configuration; + } + + public string GenerateToken(Guid userId, string email, UserRole role) + { + var secretKey = _configuration["JwtSettings:SecretKey"] ?? throw new InvalidOperationException("JWT SecretKey is not configured."); + var issuer = _configuration["JwtSettings:Issuer"] ?? "HelpDesk"; + var audience = _configuration["JwtSettings:Audience"] ?? "HelpDeskAPI"; + var expiryMinutes = int.TryParse(_configuration["JwtSettings:ExpiryMinutes"], out var minutes) ? minutes : 60; + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); + var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var claims = new[] + { + new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), + new Claim(JwtRegisteredClaimNames.Email, email), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim(ClaimTypes.Role, role.ToString()), + new Claim("userId", userId.ToString()) + }; + + var token = new JwtSecurityToken( + issuer: issuer, + audience: audience, + claims: claims, + expires: DateTime.UtcNow.AddMinutes(expiryMinutes), + signingCredentials: credentials + ); + + return new JwtSecurityTokenHandler().WriteToken(token); + } + + public ClaimsPrincipal? ValidateToken(string token) + { + var secretKey = _configuration["JwtSettings:SecretKey"] ?? throw new InvalidOperationException("JWT SecretKey is not configured."); + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.UTF8.GetBytes(secretKey); + + try + { + var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = _configuration["JwtSettings:Issuer"], + ValidAudience = _configuration["JwtSettings:Audience"], + IssuerSigningKey = new SymmetricSecurityKey(key), + ClockSkew = TimeSpan.Zero + }, out _); + + return principal; + } + catch + { + return null; + } + } +} diff --git a/backend/HelpDesk.API/Services/PasswordService.cs b/backend/HelpDesk.API/Services/PasswordService.cs new file mode 100644 index 0000000..a4129e5 --- /dev/null +++ b/backend/HelpDesk.API/Services/PasswordService.cs @@ -0,0 +1,14 @@ +namespace HelpDesk.API.Services; + +public class PasswordService : IPasswordService +{ + public string HashPassword(string password) + { + return BCrypt.Net.BCrypt.HashPassword(password); + } + + public bool VerifyPassword(string password, string hashedPassword) + { + return BCrypt.Net.BCrypt.Verify(password, hashedPassword); + } +} diff --git a/backend/HelpDesk.API/appsettings.Development.json b/backend/HelpDesk.API/appsettings.Development.json new file mode 100644 index 0000000..4383adf --- /dev/null +++ b/backend/HelpDesk.API/appsettings.Development.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Port=5432;Database=helpdesk;Username=postgres;Password=postgres" + }, + "JwtSettings": { + "SecretKey": "DevSuperSecretKeyForDevelopmentOnly_32Chars!!", + "Issuer": "HelpDesk", + "Audience": "HelpDeskAPI", + "ExpiryMinutes": 120 + }, + "FrontendUrl": "http://localhost:3000" +} diff --git a/backend/HelpDesk.API/appsettings.json b/backend/HelpDesk.API/appsettings.json new file mode 100644 index 0000000..986a13c --- /dev/null +++ b/backend/HelpDesk.API/appsettings.json @@ -0,0 +1,19 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Port=5432;Database=helpdesk;Username=postgres;Password=postgres" + }, + "JwtSettings": { + "SecretKey": "YourSuperSecretKeyHereThatShouldBeAtLeast32CharactersLong!!", + "Issuer": "HelpDesk", + "Audience": "HelpDeskAPI", + "ExpiryMinutes": 60 + }, + "FrontendUrl": "http://localhost:3000" +} diff --git a/backend/HelpDesk.Data/DependencyInjection.cs b/backend/HelpDesk.Data/DependencyInjection.cs new file mode 100644 index 0000000..726986f --- /dev/null +++ b/backend/HelpDesk.Data/DependencyInjection.cs @@ -0,0 +1,18 @@ +using HelpDesk.Data.Repositories; +using Microsoft.Extensions.DependencyInjection; + +namespace HelpDesk.Data; +public static class DependencyInjection +{ + public static IServiceCollection AddRepositories(this IServiceCollection services) + { + services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + return services; + } +} diff --git a/backend/HelpDesk.Data/HelpDesk.Data.csproj b/backend/HelpDesk.Data/HelpDesk.Data.csproj new file mode 100644 index 0000000..ee5f0c2 --- /dev/null +++ b/backend/HelpDesk.Data/HelpDesk.Data.csproj @@ -0,0 +1,21 @@ + + + + net10.0 + enable + enable + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/backend/HelpDesk.Data/HelpDeskDbContext.cs b/backend/HelpDesk.Data/HelpDeskDbContext.cs new file mode 100644 index 0000000..169be6b --- /dev/null +++ b/backend/HelpDesk.Data/HelpDeskDbContext.cs @@ -0,0 +1,306 @@ +using Microsoft.EntityFrameworkCore; +using HelpDesk.Models.Entities; + +namespace HelpDesk.Data; + +public class HelpDeskDbContext : DbContext +{ + public HelpDeskDbContext(DbContextOptions options) : base(options) + { + } + + public DbSet Users => Set(); + public DbSet Tickets => Set(); + public DbSet TicketComments => Set(); + public DbSet Groups => Set(); + public DbSet SLAPolicies => Set(); + public DbSet KnowledgeBaseArticles => Set(); + public DbSet KnowledgeBaseCategories => Set(); + public DbSet ActivityLogs => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // ── User ────────────────────────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("users"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.FirstName).HasColumnName("first_name").HasMaxLength(100).IsRequired(); + entity.Property(e => e.LastName).HasColumnName("last_name").HasMaxLength(100).IsRequired(); + entity.Property(e => e.Email).HasColumnName("email").HasMaxLength(256).IsRequired(); + entity.Property(e => e.PasswordHash).HasColumnName("password_hash").HasMaxLength(500).IsRequired(); + entity.Property(e => e.Role).HasColumnName("role").HasMaxLength(50).IsRequired(); + entity.Property(e => e.IsActive).HasColumnName("is_active"); + entity.Property(e => e.PhoneNumber).HasColumnName("phone_number").HasMaxLength(50); + entity.Property(e => e.AvatarUrl).HasColumnName("avatar_url").HasMaxLength(500); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + entity.Property(e => e.UpdatedAt).HasColumnName("updated_at"); + + entity.HasIndex(e => e.Email).IsUnique().HasDatabaseName("ix_users_email"); + }); + + // ── Ticket ──────────────────────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("tickets"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Title).HasColumnName("title").HasMaxLength(200).IsRequired(); + entity.Property(e => e.Description).HasColumnName("description").IsRequired(); + entity.Property(e => e.Status).HasColumnName("status").HasMaxLength(50).IsRequired(); + entity.Property(e => e.Priority).HasColumnName("priority").HasMaxLength(50).IsRequired(); + entity.Property(e => e.TicketNumber).HasColumnName("ticket_number").HasMaxLength(50).IsRequired(); + entity.Property(e => e.RequesterId).HasColumnName("requester_id"); + entity.Property(e => e.AssigneeId).HasColumnName("assignee_id"); + entity.Property(e => e.GroupId).HasColumnName("group_id"); + entity.Property(e => e.SlaPolicyId).HasColumnName("sla_policy_id"); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + entity.Property(e => e.UpdatedAt).HasColumnName("updated_at"); + entity.Property(e => e.ResolvedAt).HasColumnName("resolved_at"); + entity.Property(e => e.ClosedAt).HasColumnName("closed_at"); + + entity.HasIndex(e => e.TicketNumber).IsUnique().HasDatabaseName("ix_tickets_ticket_number"); + + // Cascade delete: Ticket -> TicketComment + entity.HasMany(e => e.Comments) + .WithOne(c => c.Ticket) + .HasForeignKey(c => c.TicketId) + .OnDelete(DeleteBehavior.Cascade); + + // Restrict: User FK references (Requester) + entity.HasOne(e => e.Requester) + .WithMany(u => u.RequestedTickets) + .HasForeignKey(e => e.RequesterId) + .OnDelete(DeleteBehavior.Restrict); + + // Restrict: User FK references (Assignee) + entity.HasOne(e => e.Assignee) + .WithMany(u => u.AssignedTickets) + .HasForeignKey(e => e.AssigneeId) + .OnDelete(DeleteBehavior.Restrict); + + entity.HasOne(e => e.Group) + .WithMany(g => g.Tickets) + .HasForeignKey(e => e.GroupId) + .OnDelete(DeleteBehavior.SetNull); + + entity.HasOne(e => e.SlaPolicy) + .WithMany(s => s.Tickets) + .HasForeignKey(e => e.SlaPolicyId) + .OnDelete(DeleteBehavior.SetNull); + }); + + // ── TicketComment ───────────────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("ticket_comments"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.TicketId).HasColumnName("ticket_id"); + entity.Property(e => e.UserId).HasColumnName("user_id"); + entity.Property(e => e.Content).HasColumnName("content").IsRequired(); + entity.Property(e => e.IsInternal).HasColumnName("is_internal"); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + + // Restrict: User FK reference + entity.HasOne(e => e.User) + .WithMany(u => u.Comments) + .HasForeignKey(e => e.UserId) + .OnDelete(DeleteBehavior.Restrict); + }); + + // ── Group ───────────────────────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("groups"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Name).HasColumnName("name").HasMaxLength(100).IsRequired(); + entity.Property(e => e.Description).HasColumnName("description").HasMaxLength(500); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + }); + + // ── SLAPolicy ───────────────────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("sla_policies"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Name).HasColumnName("name").HasMaxLength(100).IsRequired(); + entity.Property(e => e.Description).HasColumnName("description").HasMaxLength(500); + entity.Property(e => e.ResponseTimeHours).HasColumnName("response_time_hours"); + entity.Property(e => e.ResolutionTimeHours).HasColumnName("resolution_time_hours"); + entity.Property(e => e.IsActive).HasColumnName("is_active"); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + }); + + // ── KnowledgeBaseArticle ────────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("knowledge_base_articles"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Title).HasColumnName("title").HasMaxLength(200).IsRequired(); + entity.Property(e => e.Content).HasColumnName("content").IsRequired(); + entity.Property(e => e.CategoryId).HasColumnName("category_id"); + entity.Property(e => e.AuthorId).HasColumnName("author_id"); + entity.Property(e => e.IsPublished).HasColumnName("is_published"); + entity.Property(e => e.ViewCount).HasColumnName("view_count"); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + entity.Property(e => e.UpdatedAt).HasColumnName("updated_at"); + + entity.HasOne(e => e.Category) + .WithMany(c => c.Articles) + .HasForeignKey(e => e.CategoryId) + .OnDelete(DeleteBehavior.Restrict); + + entity.HasOne(e => e.Author) + .WithMany(u => u.Articles) + .HasForeignKey(e => e.AuthorId) + .OnDelete(DeleteBehavior.Restrict); + }); + + // ── KnowledgeBaseCategory ───────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("knowledge_base_categories"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Name).HasColumnName("name").HasMaxLength(100).IsRequired(); + entity.Property(e => e.Description).HasColumnName("description").HasMaxLength(500); + entity.Property(e => e.SortOrder).HasColumnName("sort_order"); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + }); + + // ── ActivityLog ─────────────────────────────────────── + modelBuilder.Entity(entity => + { + entity.ToTable("activity_logs"); + entity.HasKey(e => e.Id); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.UserId).HasColumnName("user_id"); + entity.Property(e => e.EntityType).HasColumnName("entity_type").HasMaxLength(100).IsRequired(); + entity.Property(e => e.EntityId).HasColumnName("entity_id"); + entity.Property(e => e.Action).HasColumnName("action").HasMaxLength(100).IsRequired(); + entity.Property(e => e.Details).HasColumnName("details"); + entity.Property(e => e.CreatedAt).HasColumnName("created_at"); + + entity.HasOne(e => e.User) + .WithMany(u => u.ActivityLogs) + .HasForeignKey(e => e.UserId) + .OnDelete(DeleteBehavior.Restrict); + }); + + // ── Seed Data ───────────────────────────────────────── + SeedData(modelBuilder); + } + + private static void SeedData(ModelBuilder modelBuilder) + { + var adminId = Guid.Parse("a0000000-0000-0000-0000-000000000001"); + var generalGroupId = Guid.Parse("b0000000-0000-0000-0000-000000000001"); + var ventasGroupId = Guid.Parse("b0000000-0000-0000-0000-000000000002"); + var desarrolloGroupId = Guid.Parse("b0000000-0000-0000-0000-000000000003"); + var standardSlaId = Guid.Parse("c0000000-0000-0000-0000-000000000001"); + var premiumSlaId = Guid.Parse("c0000000-0000-0000-0000-000000000002"); + var generalCategoryId = Guid.Parse("d0000000-0000-0000-0000-000000000001"); + var faqCategoryId = Guid.Parse("d0000000-0000-0000-0000-000000000002"); + var tutorialsCategoryId = Guid.Parse("d0000000-0000-0000-0000-000000000003"); + + // SuperAdmin user + modelBuilder.Entity().HasData(new User + { + Id = adminId, + FirstName = "Super", + LastName = "Admin", + Email = "admin@helpdesk.local", + // BCrypt hash for "Admin123!" + PasswordHash = "$2a$11$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy", + Role = HelpDesk.Models.Enums.UserRole.SuperAdmin, + IsActive = true, + PhoneNumber = "+1-555-0100", + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + }); + + // Default groups + modelBuilder.Entity().HasData( + new Group + { + Id = generalGroupId, + Name = "Soporte General", + Description = "Grupo de soporte técnico general", + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + }, + new Group + { + Id = ventasGroupId, + Name = "Ventas", + Description = "Grupo de soporte de ventas", + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + }, + new Group + { + Id = desarrolloGroupId, + Name = "Desarrollo", + Description = "Grupo de desarrollo de software", + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + } + ); + + // SLA policies + modelBuilder.Entity().HasData( + new SLAPolicy + { + Id = standardSlaId, + Name = "Estándar", + Description = "Política estándar: respuesta en 4 horas, resolución en 24 horas", + ResponseTimeHours = 4, + ResolutionTimeHours = 24, + IsActive = true, + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + }, + new SLAPolicy + { + Id = premiumSlaId, + Name = "Premium", + Description = "Política premium: respuesta en 1 hora, resolución en 8 horas", + ResponseTimeHours = 1, + ResolutionTimeHours = 8, + IsActive = true, + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + } + ); + + // Knowledge base categories + modelBuilder.Entity().HasData( + new KnowledgeBaseCategory + { + Id = generalCategoryId, + Name = "General", + Description = "Artículos generales de ayuda", + SortOrder = 1, + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + }, + new KnowledgeBaseCategory + { + Id = faqCategoryId, + Name = "Preguntas Frecuentes", + Description = "Preguntas frecuentes y respuestas", + SortOrder = 2, + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + }, + new KnowledgeBaseCategory + { + Id = tutorialsCategoryId, + Name = "Tutoriales", + Description = "Guías y tutoriales paso a paso", + SortOrder = 3, + CreatedAt = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc) + } + ); + } +} diff --git a/backend/HelpDesk.Data/Repositories/IKnowledgeBaseRepository.cs b/backend/HelpDesk.Data/Repositories/IKnowledgeBaseRepository.cs new file mode 100644 index 0000000..fbadeba --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/IKnowledgeBaseRepository.cs @@ -0,0 +1,22 @@ +using HelpDesk.Models.Entities; + +namespace HelpDesk.Data.Repositories; + +public interface IKnowledgeBaseRepository +{ + // Articles + Task GetArticleByIdAsync(Guid id); + Task> GetAllArticlesAsync(); + Task> GetPublishedArticlesAsync(); + Task> SearchArticlesAsync(string query); + Task CreateArticleAsync(KnowledgeBaseArticle article); + Task UpdateArticleAsync(KnowledgeBaseArticle article); + Task DeleteArticleAsync(Guid id); + + // Categories + Task GetCategoryByIdAsync(Guid id); + Task> GetAllCategoriesAsync(); + Task CreateCategoryAsync(KnowledgeBaseCategory category); + Task UpdateCategoryAsync(KnowledgeBaseCategory category); + Task DeleteCategoryAsync(Guid id); +} diff --git a/backend/HelpDesk.Data/Repositories/IRepository.cs b/backend/HelpDesk.Data/Repositories/IRepository.cs new file mode 100644 index 0000000..da7e117 --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/IRepository.cs @@ -0,0 +1,11 @@ +namespace HelpDesk.Data.Repositories; + +public interface IRepository where T : class +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(); + Task AddAsync(T entity); + Task UpdateAsync(T entity); + Task DeleteAsync(T entity); + Task SaveChangesAsync(); +} diff --git a/backend/HelpDesk.Data/Repositories/ITicketRepository.cs b/backend/HelpDesk.Data/Repositories/ITicketRepository.cs new file mode 100644 index 0000000..39f0ade --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/ITicketRepository.cs @@ -0,0 +1,19 @@ +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; + +namespace HelpDesk.Data.Repositories; + +public interface ITicketRepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(TicketStatus? status = null, TicketPriority? priority = null, Guid? assigneeId = null, Guid? requesterId = null, int page = 1, int pageSize = 20); + Task GetCountAsync(TicketStatus? status = null, TicketPriority? priority = null, Guid? assigneeId = null, Guid? requesterId = null); + Task CreateAsync(Ticket ticket); + Task UpdateAsync(Ticket ticket); + Task DeleteAsync(Guid id); + Task GetTotalCountAsync(); + Task GetCountByStatusAsync(TicketStatus status); + Task GetCountByPriorityAsync(TicketPriority priority); + Task GetAverageResponseTimeAsync(); + Task GetAverageResolutionTimeAsync(); +} diff --git a/backend/HelpDesk.Data/Repositories/IUserRepository.cs b/backend/HelpDesk.Data/Repositories/IUserRepository.cs new file mode 100644 index 0000000..68fecfe --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/IUserRepository.cs @@ -0,0 +1,15 @@ +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; + +namespace HelpDesk.Data.Repositories; + +public interface IUserRepository +{ + Task GetByIdAsync(Guid id); + Task GetByEmailAsync(string email); + Task> GetAllAsync(); + Task> GetByRoleAsync(UserRole role); + Task CreateAsync(User user); + Task UpdateAsync(User user); + Task ExistsAsync(string email); +} diff --git a/backend/HelpDesk.Data/Repositories/KnowledgeBaseRepository.cs b/backend/HelpDesk.Data/Repositories/KnowledgeBaseRepository.cs new file mode 100644 index 0000000..8ff2ba2 --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/KnowledgeBaseRepository.cs @@ -0,0 +1,120 @@ +using HelpDesk.Models.Entities; +using Microsoft.EntityFrameworkCore; + +namespace HelpDesk.Data.Repositories; + +public class KnowledgeBaseRepository : IKnowledgeBaseRepository +{ + private readonly HelpDeskDbContext _context; + + public KnowledgeBaseRepository(HelpDeskDbContext context) + { + _context = context; + } + + // Articles + public async Task GetArticleByIdAsync(Guid id) + => await _context.KnowledgeBaseArticles + .Include(a => a.Category) + .Include(a => a.Author) + .FirstOrDefaultAsync(a => a.Id == id); + + public async Task> GetAllArticlesAsync() + => await _context.KnowledgeBaseArticles + .Include(a => a.Category) + .Include(a => a.Author) + .OrderByDescending(a => a.CreatedAt) + .ToListAsync(); + + public async Task> GetPublishedArticlesAsync() + => await _context.KnowledgeBaseArticles + .Include(a => a.Category) + .Include(a => a.Author) + .Where(a => a.IsPublished) + .OrderByDescending(a => a.CreatedAt) + .ToListAsync(); + + public async Task> SearchArticlesAsync(string query) + { + var lowerQuery = query.ToLower(); + return await _context.KnowledgeBaseArticles + .Include(a => a.Category) + .Include(a => a.Author) + .Where(a => a.IsPublished && (a.Title.ToLower().Contains(lowerQuery) || a.Content.ToLower().Contains(lowerQuery))) + .OrderByDescending(a => a.ViewCount) + .ToListAsync(); + } + + public async Task CreateArticleAsync(KnowledgeBaseArticle article) + { + _context.KnowledgeBaseArticles.Add(article); + await _context.SaveChangesAsync(); + return article; + } + + public async Task UpdateArticleAsync(KnowledgeBaseArticle article) + { + var existing = await _context.KnowledgeBaseArticles.FindAsync(article.Id); + if (existing == null) return null; + + existing.Title = article.Title; + existing.Content = article.Content; + existing.CategoryId = article.CategoryId; + existing.IsPublished = article.IsPublished; + existing.UpdatedAt = DateTime.UtcNow; + + await _context.SaveChangesAsync(); + return existing; + } + + public async Task DeleteArticleAsync(Guid id) + { + var article = await _context.KnowledgeBaseArticles.FindAsync(id); + if (article == null) return false; + _context.KnowledgeBaseArticles.Remove(article); + await _context.SaveChangesAsync(); + return true; + } + + // Categories + public async Task GetCategoryByIdAsync(Guid id) + => await _context.KnowledgeBaseCategories + .Include(c => c.Articles) + .FirstOrDefaultAsync(c => c.Id == id); + + public async Task> GetAllCategoriesAsync() + => await _context.KnowledgeBaseCategories + .Include(c => c.Articles) + .OrderBy(c => c.SortOrder) + .ThenBy(c => c.Name) + .ToListAsync(); + + public async Task CreateCategoryAsync(KnowledgeBaseCategory category) + { + _context.KnowledgeBaseCategories.Add(category); + await _context.SaveChangesAsync(); + return category; + } + + public async Task UpdateCategoryAsync(KnowledgeBaseCategory category) + { + var existing = await _context.KnowledgeBaseCategories.FindAsync(category.Id); + if (existing == null) return null; + + existing.Name = category.Name; + existing.Description = category.Description; + existing.SortOrder = category.SortOrder; + + await _context.SaveChangesAsync(); + return existing; + } + + public async Task DeleteCategoryAsync(Guid id) + { + var category = await _context.KnowledgeBaseCategories.FindAsync(id); + if (category == null) return false; + _context.KnowledgeBaseCategories.Remove(category); + await _context.SaveChangesAsync(); + return true; + } +} diff --git a/backend/HelpDesk.Data/Repositories/OtherRepositories.cs b/backend/HelpDesk.Data/Repositories/OtherRepositories.cs new file mode 100644 index 0000000..ac80d76 --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/OtherRepositories.cs @@ -0,0 +1,29 @@ +using HelpDesk.Models.Entities; + +namespace HelpDesk.Data.Repositories; + +public interface ITicketCommentRepository +{ + Task GetByIdAsync(Guid id); + Task> GetByTicketIdAsync(Guid ticketId); + Task CreateAsync(TicketComment comment); + Task DeleteAsync(Guid id); +} + +public interface IGroupRepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(); + Task CreateAsync(Group group); + Task UpdateAsync(Group group); + Task DeleteAsync(Guid id); +} + +public interface ISLARepository +{ + Task GetByIdAsync(Guid id); + Task> GetAllAsync(); + Task CreateAsync(SLAPolicy policy); + Task UpdateAsync(SLAPolicy policy); + Task DeleteAsync(Guid id); +} diff --git a/backend/HelpDesk.Data/Repositories/Repository.cs b/backend/HelpDesk.Data/Repositories/Repository.cs new file mode 100644 index 0000000..31a71f8 --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/Repository.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore; + +namespace HelpDesk.Data.Repositories; + +public class Repository : IRepository where T : class +{ + protected readonly HelpDeskDbContext _context; + protected readonly DbSet _dbSet; + + public Repository(HelpDeskDbContext context) + { + _context = context; + _dbSet = context.Set(); + } + + public virtual async Task GetByIdAsync(Guid id) + { + return await _dbSet.FindAsync(id); + } + + public virtual async Task> GetAllAsync() + { + return await _dbSet.ToListAsync(); + } + + public virtual async Task AddAsync(T entity) + { + await _dbSet.AddAsync(entity); + await SaveChangesAsync(); + return entity; + } + + public virtual async Task UpdateAsync(T entity) + { + _dbSet.Update(entity); + await SaveChangesAsync(); + } + + public virtual async Task DeleteAsync(T entity) + { + _dbSet.Remove(entity); + await SaveChangesAsync(); + } + + public virtual async Task SaveChangesAsync() + { + return await _context.SaveChangesAsync(); + } +} diff --git a/backend/HelpDesk.Data/Repositories/TicketCommentGroupSlaRepositories.cs b/backend/HelpDesk.Data/Repositories/TicketCommentGroupSlaRepositories.cs new file mode 100644 index 0000000..9d5cd23 --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/TicketCommentGroupSlaRepositories.cs @@ -0,0 +1,132 @@ +using HelpDesk.Models.Entities; +using Microsoft.EntityFrameworkCore; + +namespace HelpDesk.Data.Repositories; + +public class TicketCommentRepository : ITicketCommentRepository +{ + private readonly HelpDeskDbContext _context; + + public TicketCommentRepository(HelpDeskDbContext context) + { + _context = context; + } + + public async Task GetByIdAsync(Guid id) + => await _context.TicketComments.FindAsync(id); + + public async Task> GetByTicketIdAsync(Guid ticketId) + => await _context.TicketComments + .Include(c => c.User) + .Where(c => c.TicketId == ticketId) + .OrderBy(c => c.CreatedAt) + .ToListAsync(); + + public async Task CreateAsync(TicketComment comment) + { + _context.TicketComments.Add(comment); + await _context.SaveChangesAsync(); + return comment; + } + + public async Task DeleteAsync(Guid id) + { + var comment = await _context.TicketComments.FindAsync(id); + if (comment == null) return false; + _context.TicketComments.Remove(comment); + await _context.SaveChangesAsync(); + return true; + } +} + +public class GroupRepository : IGroupRepository +{ + private readonly HelpDeskDbContext _context; + + public GroupRepository(HelpDeskDbContext context) + { + _context = context; + } + + public async Task GetByIdAsync(Guid id) + => await _context.Groups.FindAsync(id); + + public async Task> GetAllAsync() + => await _context.Groups.OrderBy(g => g.Name).ToListAsync(); + + public async Task CreateAsync(Group group) + { + _context.Groups.Add(group); + await _context.SaveChangesAsync(); + return group; + } + + public async Task UpdateAsync(Group group) + { + var existing = await _context.Groups.FindAsync(group.Id); + if (existing == null) return null; + + existing.Name = group.Name; + existing.Description = group.Description; + existing.IsActive = group.IsActive; + + await _context.SaveChangesAsync(); + return existing; + } + + public async Task DeleteAsync(Guid id) + { + var group = await _context.Groups.FindAsync(id); + if (group == null) return false; + _context.Groups.Remove(group); + await _context.SaveChangesAsync(); + return true; + } +} + +public class SLARepository : ISLARepository +{ + private readonly HelpDeskDbContext _context; + + public SLARepository(HelpDeskDbContext context) + { + _context = context; + } + + public async Task GetByIdAsync(Guid id) + => await _context.SLAPolicies.FindAsync(id); + + public async Task> GetAllAsync() + => await _context.SLAPolicies.OrderBy(s => s.Name).ToListAsync(); + + public async Task CreateAsync(SLAPolicy policy) + { + _context.SLAPolicies.Add(policy); + await _context.SaveChangesAsync(); + return policy; + } + + public async Task UpdateAsync(SLAPolicy policy) + { + var existing = await _context.SLAPolicies.FindAsync(policy.Id); + if (existing == null) return null; + + existing.Name = policy.Name; + existing.Description = policy.Description; + existing.ResponseTimeHours = policy.ResponseTimeHours; + existing.ResolutionTimeHours = policy.ResolutionTimeHours; + existing.IsActive = policy.IsActive; + + await _context.SaveChangesAsync(); + return existing; + } + + public async Task DeleteAsync(Guid id) + { + var policy = await _context.SLAPolicies.FindAsync(id); + if (policy == null) return false; + _context.SLAPolicies.Remove(policy); + await _context.SaveChangesAsync(); + return true; + } +} diff --git a/backend/HelpDesk.Data/Repositories/TicketRepository.cs b/backend/HelpDesk.Data/Repositories/TicketRepository.cs new file mode 100644 index 0000000..ca50aa1 --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/TicketRepository.cs @@ -0,0 +1,128 @@ +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; +using Microsoft.EntityFrameworkCore; + +namespace HelpDesk.Data.Repositories; + +public class TicketRepository : ITicketRepository +{ + private readonly HelpDeskDbContext _context; + + public TicketRepository(HelpDeskDbContext context) + { + _context = context; + } + + private IQueryable GetQueryable() + => _context.Tickets + .Include(t => t.Requester) + .Include(t => t.Assignee) + .Include(t => t.Group) + .Include(t => t.SlaPolicy) + .Include(t => t.Comments); + + public async Task GetByIdAsync(Guid id) + => await GetQueryable().FirstOrDefaultAsync(t => t.Id == id); + + public async Task> GetAllAsync(TicketStatus? status, TicketPriority? priority, Guid? assigneeId, Guid? requesterId, int page = 1, int pageSize = 20) + { + var query = GetQueryable().AsQueryable(); + + if (status.HasValue) query = query.Where(t => t.Status == status.Value); + if (priority.HasValue) query = query.Where(t => t.Priority == priority.Value); + if (assigneeId.HasValue) query = query.Where(t => t.AssigneeId == assigneeId.Value); + if (requesterId.HasValue) query = query.Where(t => t.RequesterId == requesterId.Value); + + return await query.OrderByDescending(t => t.CreatedAt) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + } + + public async Task GetCountAsync(TicketStatus? status, TicketPriority? priority, Guid? assigneeId, Guid? requesterId) + { + var query = _context.Tickets.AsQueryable(); + + if (status.HasValue) query = query.Where(t => t.Status == status.Value); + if (priority.HasValue) query = query.Where(t => t.Priority == priority.Value); + if (assigneeId.HasValue) query = query.Where(t => t.AssigneeId == assigneeId.Value); + if (requesterId.HasValue) query = query.Where(t => t.RequesterId == requesterId.Value); + + return await query.CountAsync(); + } + + public async Task CreateAsync(Ticket ticket) + { + _context.Tickets.Add(ticket); + await _context.SaveChangesAsync(); + return ticket; + } + + public async Task UpdateAsync(Ticket ticket) + { + var existing = await _context.Tickets.FindAsync(ticket.Id); + if (existing == null) return null; + + existing.Title = ticket.Title; + existing.Description = ticket.Description; + existing.Priority = ticket.Priority; + existing.Status = ticket.Status; + existing.AssigneeId = ticket.AssigneeId; + existing.GroupId = ticket.GroupId; + existing.SlaPolicyId = ticket.SlaPolicyId; + existing.UpdatedAt = DateTime.UtcNow; + + if (ticket.Status == TicketStatus.Resolved && existing.ResolvedAt == null) + existing.ResolvedAt = DateTime.UtcNow; + if (ticket.Status == TicketStatus.Closed && existing.ClosedAt == null) + existing.ClosedAt = DateTime.UtcNow; + + await _context.SaveChangesAsync(); + return existing; + } + + public async Task DeleteAsync(Guid id) + { + var ticket = await _context.Tickets.FindAsync(id); + if (ticket == null) return false; + _context.Tickets.Remove(ticket); + await _context.SaveChangesAsync(); + return true; + } + + public async Task GetTotalCountAsync() + => await _context.Tickets.CountAsync(); + + public async Task GetCountByStatusAsync(TicketStatus status) + => await _context.Tickets.CountAsync(t => t.Status == status); + + public async Task GetCountByPriorityAsync(TicketPriority priority) + => await _context.Tickets.CountAsync(t => t.Priority == priority); + + public async Task GetAverageResponseTimeAsync() + { + var tickets = await _context.Tickets + .Where(t => t.Comments.Any()) + .Select(t => new + { + t.CreatedAt, + FirstCommentAt = t.Comments.OrderBy(c => c.CreatedAt).Select(c => c.CreatedAt).FirstOrDefault() + }) + .Where(t => t.FirstCommentAt != default(DateTime)) + .ToListAsync(); + + if (!tickets.Any()) return 0; + return tickets.Average(t => (t.FirstCommentAt - t.CreatedAt).TotalMinutes); + } + + public async Task GetAverageResolutionTimeAsync() + { + var tickets = await _context.Tickets + .Where(t => t.ResolvedAt.HasValue) + .Select(t => new { t.CreatedAt, t.ResolvedAt }) + .ToListAsync(); + + if (!tickets.Any()) return 0; + return tickets.Average(t => (t.ResolvedAt!.Value - t.CreatedAt).TotalMinutes); + } +} diff --git a/backend/HelpDesk.Data/Repositories/UserRepository.cs b/backend/HelpDesk.Data/Repositories/UserRepository.cs new file mode 100644 index 0000000..33127f6 --- /dev/null +++ b/backend/HelpDesk.Data/Repositories/UserRepository.cs @@ -0,0 +1,55 @@ +using HelpDesk.Models.Entities; +using HelpDesk.Models.Enums; +using Microsoft.EntityFrameworkCore; + +namespace HelpDesk.Data.Repositories; + +public class UserRepository : IUserRepository +{ + private readonly HelpDeskDbContext _context; + + public UserRepository(HelpDeskDbContext context) + { + _context = context; + } + + public async Task GetByIdAsync(Guid id) + => await _context.Users.FirstOrDefaultAsync(u => u.Id == id); + + public async Task GetByEmailAsync(string email) + => await _context.Users.FirstOrDefaultAsync(u => u.Email == email); + + public async Task> GetAllAsync() + => await _context.Users.OrderBy(u => u.LastName).ToListAsync(); + + public async Task> GetByRoleAsync(UserRole role) + => await _context.Users.Where(u => u.Role == role && u.IsActive).OrderBy(u => u.LastName).ToListAsync(); + + public async Task CreateAsync(User user) + { + _context.Users.Add(user); + await _context.SaveChangesAsync(); + return user; + } + + public async Task UpdateAsync(User user) + { + var existing = await _context.Users.FindAsync(user.Id); + if (existing == null) return null; + + existing.FirstName = user.FirstName; + existing.LastName = user.LastName; + existing.Email = user.Email; + existing.PhoneNumber = user.PhoneNumber; + existing.AvatarUrl = user.AvatarUrl; + existing.Role = user.Role; + existing.IsActive = user.IsActive; + existing.UpdatedAt = DateTime.UtcNow; + + await _context.SaveChangesAsync(); + return existing; + } + + public async Task ExistsAsync(string email) + => await _context.Users.AnyAsync(u => u.Email == email); +} diff --git a/backend/HelpDesk.Models/DTOs/AuthDtos.cs b/backend/HelpDesk.Models/DTOs/AuthDtos.cs new file mode 100644 index 0000000..979d8a0 --- /dev/null +++ b/backend/HelpDesk.Models/DTOs/AuthDtos.cs @@ -0,0 +1,48 @@ +using HelpDesk.Models.Enums; + +namespace HelpDesk.Models.DTOs; + +public class LoginRequest +{ + public string Email { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; +} + +public class RegisterRequest +{ + public string Email { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; +} + +public class AuthResponse +{ + public string Token { get; set; } = string.Empty; + public DateTime Expiration { get; set; } + public UserDto User { get; set; } = null!; +} + +public class UserDto +{ + public Guid Id { get; set; } + public string Email { get; set; } = string.Empty; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public UserRole Role { get; set; } + public bool IsActive { get; set; } + public DateTime CreatedAt { get; set; } + public string? PhoneNumber { get; set; } + public string? AvatarUrl { get; set; } +} + +public class UpdateUserRequest +{ + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public string? PhoneNumber { get; set; } + public string? AvatarUrl { get; set; } + public UserRole? Role { get; set; } + public bool? IsActive { get; set; } +} diff --git a/backend/HelpDesk.Models/DTOs/DashboardDtos.cs b/backend/HelpDesk.Models/DTOs/DashboardDtos.cs new file mode 100644 index 0000000..01f916f --- /dev/null +++ b/backend/HelpDesk.Models/DTOs/DashboardDtos.cs @@ -0,0 +1,28 @@ +using HelpDesk.Models.Enums; + +namespace HelpDesk.Models.DTOs; + +public class DashboardStatsDto +{ + public int TotalTickets { get; set; } + public int OpenTickets { get; set; } + public int InProgressTickets { get; set; } + public int PendingTickets { get; set; } + public int ResolvedTickets { get; set; } + public int ClosedTickets { get; set; } + public int CriticalTickets { get; set; } + public int HighTickets { get; set; } + public int MediumTickets { get; set; } + public int LowTickets { get; set; } + public double AverageResponseTimeMinutes { get; set; } + public double AverageResolutionTimeMinutes { get; set; } + public List AgentWorkloads { get; set; } = new(); +} + +public class AgentWorkloadDto +{ + public Guid UserId { get; set; } + public string UserName { get; set; } = string.Empty; + public int ActiveTickets { get; set; } + public int ResolvedThisWeek { get; set; } +} diff --git a/backend/HelpDesk.Models/DTOs/GroupDtos.cs b/backend/HelpDesk.Models/DTOs/GroupDtos.cs new file mode 100644 index 0000000..7328701 --- /dev/null +++ b/backend/HelpDesk.Models/DTOs/GroupDtos.cs @@ -0,0 +1,16 @@ +namespace HelpDesk.Models.DTOs; + +public class GroupDto +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } + public bool IsActive { get; set; } + public DateTime CreatedAt { get; set; } +} + +public class CreateGroupRequest +{ + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } +} diff --git a/backend/HelpDesk.Models/DTOs/KnowledgeBaseDtos.cs b/backend/HelpDesk.Models/DTOs/KnowledgeBaseDtos.cs new file mode 100644 index 0000000..da5c546 --- /dev/null +++ b/backend/HelpDesk.Models/DTOs/KnowledgeBaseDtos.cs @@ -0,0 +1,49 @@ +namespace HelpDesk.Models.DTOs; + +public class ArticleDto +{ + public Guid Id { get; set; } + public string Title { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public bool IsPublished { get; set; } + public Guid CategoryId { get; set; } + public string CategoryName { get; set; } = string.Empty; + public Guid AuthorId { get; set; } + public string AuthorName { get; set; } = string.Empty; + public int ViewCount { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } +} + +public class CreateArticleRequest +{ + public string Title { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public Guid CategoryId { get; set; } + public bool IsPublished { get; set; } +} + +public class UpdateArticleRequest +{ + public string? Title { get; set; } + public string? Content { get; set; } + public Guid? CategoryId { get; set; } + public bool? IsPublished { get; set; } +} + +public class CategoryDto +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } + public int SortOrder { get; set; } + public int ArticleCount { get; set; } + public DateTime CreatedAt { get; set; } +} + +public class CreateCategoryRequest +{ + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } + public int SortOrder { get; set; } +} diff --git a/backend/HelpDesk.Models/DTOs/SLADtos.cs b/backend/HelpDesk.Models/DTOs/SLADtos.cs new file mode 100644 index 0000000..a12f3f2 --- /dev/null +++ b/backend/HelpDesk.Models/DTOs/SLADtos.cs @@ -0,0 +1,29 @@ +namespace HelpDesk.Models.DTOs; + +public class SLADto +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } + public int ResponseTimeHours { get; set; } + public int ResolutionTimeHours { get; set; } + public bool IsActive { get; set; } + public DateTime CreatedAt { get; set; } +} + +public class CreateSLARequest +{ + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } + public int ResponseTimeHours { get; set; } + public int ResolutionTimeHours { get; set; } +} + +public class UpdateSLARequest +{ + public string? Name { get; set; } + public string? Description { get; set; } + public int? ResponseTimeHours { get; set; } + public int? ResolutionTimeHours { get; set; } + public bool? IsActive { get; set; } +} diff --git a/backend/HelpDesk.Models/DTOs/TicketDtos.cs b/backend/HelpDesk.Models/DTOs/TicketDtos.cs new file mode 100644 index 0000000..dde2ab5 --- /dev/null +++ b/backend/HelpDesk.Models/DTOs/TicketDtos.cs @@ -0,0 +1,86 @@ +using HelpDesk.Models.Enums; + +namespace HelpDesk.Models.DTOs; + +public class CreateTicketRequest +{ + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public TicketPriority Priority { get; set; } = TicketPriority.Medium; + public Guid? GroupId { get; set; } + public Guid? SlaPolicyId { get; set; } +} + +public class UpdateTicketRequest +{ + public string? Title { get; set; } + public string? Description { get; set; } + public TicketPriority? Priority { get; set; } +} + +public class TicketDto +{ + public Guid Id { get; set; } + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public TicketStatus Status { get; set; } + public TicketPriority Priority { get; set; } + public string TicketNumber { get; set; } = string.Empty; + public Guid RequesterId { get; set; } + public string RequesterName { get; set; } = string.Empty; + public Guid? AssigneeId { get; set; } + public string? AssigneeName { get; set; } + public Guid? GroupId { get; set; } + public string? GroupName { get; set; } + public Guid? SlaPolicyId { get; set; } + public string? SlaPolicyName { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public DateTime? ResolvedAt { get; set; } + public int CommentCount { get; set; } +} + +public class TicketListResponse +{ + public List Items { get; set; } = new(); + public int TotalCount { get; set; } + public int Page { get; set; } + public int PageSize { get; set; } +} + +public class TicketStatsDto +{ + public int TotalTickets { get; set; } + public int OpenTickets { get; set; } + public int InProgressTickets { get; set; } + public int PendingTickets { get; set; } + public int ResolvedTickets { get; set; } + public int ClosedTickets { get; set; } +} + +public class AddCommentRequest +{ + public string Content { get; set; } = string.Empty; + public bool IsInternal { get; set; } +} + +public class CommentDto +{ + public Guid Id { get; set; } + public Guid TicketId { get; set; } + public Guid UserId { get; set; } + public string UserName { get; set; } = string.Empty; + public string Content { get; set; } = string.Empty; + public bool IsInternal { get; set; } + public DateTime CreatedAt { get; set; } +} + +public class UpdateStatusRequest +{ + public TicketStatus Status { get; set; } +} + +public class AssignTicketRequest +{ + public Guid? AssigneeId { get; set; } +} diff --git a/backend/HelpDesk.Models/Entities/ActivityLog.cs b/backend/HelpDesk.Models/Entities/ActivityLog.cs new file mode 100644 index 0000000..1aab992 --- /dev/null +++ b/backend/HelpDesk.Models/Entities/ActivityLog.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HelpDesk.Models.Entities; + +[Table("activity_logs")] +public class ActivityLog +{ + [Key] + public Guid Id { get; set; } + + public Guid? UserId { get; set; } + + [Required] + [MaxLength(100)] + public string EntityType { get; set; } = string.Empty; + + public int? EntityId { get; set; } + + [Required] + [MaxLength(100)] + public string Action { get; set; } = string.Empty; + + public string? Details { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + // Navigation + public User? User { get; set; } +} diff --git a/backend/HelpDesk.Models/Entities/Group.cs b/backend/HelpDesk.Models/Entities/Group.cs new file mode 100644 index 0000000..a152191 --- /dev/null +++ b/backend/HelpDesk.Models/Entities/Group.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HelpDesk.Models.Entities; + +[Table("groups")] +public class Group +{ + [Key] + public Guid Id { get; set; } + + [Required] + [MaxLength(100)] + public string Name { get; set; } = string.Empty; + + [MaxLength(500)] + public string? Description { get; set; } + + public bool IsActive { get; set; } = true; + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + // Navigation + public ICollection Tickets { get; set; } = new List(); +} diff --git a/backend/HelpDesk.Models/Entities/KnowledgeBaseArticle.cs b/backend/HelpDesk.Models/Entities/KnowledgeBaseArticle.cs new file mode 100644 index 0000000..0e835b5 --- /dev/null +++ b/backend/HelpDesk.Models/Entities/KnowledgeBaseArticle.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HelpDesk.Models.Entities; + +[Table("knowledge_base_articles")] +public class KnowledgeBaseArticle +{ + [Key] + public Guid Id { get; set; } + + [Required] + [MaxLength(200)] + public string Title { get; set; } = string.Empty; + + [Required] + public string Content { get; set; } = string.Empty; + + public Guid CategoryId { get; set; } + public Guid AuthorId { get; set; } + + public bool IsPublished { get; set; } + public int ViewCount { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; + + // Navigation + public KnowledgeBaseCategory Category { get; set; } = null!; + public User Author { get; set; } = null!; +} diff --git a/backend/HelpDesk.Models/Entities/KnowledgeBaseCategory.cs b/backend/HelpDesk.Models/Entities/KnowledgeBaseCategory.cs new file mode 100644 index 0000000..6ce9ac3 --- /dev/null +++ b/backend/HelpDesk.Models/Entities/KnowledgeBaseCategory.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HelpDesk.Models.Entities; + +[Table("knowledge_base_categories")] +public class KnowledgeBaseCategory +{ + [Key] + public Guid Id { get; set; } + + [Required] + [MaxLength(100)] + public string Name { get; set; } = string.Empty; + + [MaxLength(500)] + public string? Description { get; set; } + + public int SortOrder { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + // Navigation + public ICollection Articles { get; set; } = new List(); +} diff --git a/backend/HelpDesk.Models/Entities/SLAPolicy.cs b/backend/HelpDesk.Models/Entities/SLAPolicy.cs new file mode 100644 index 0000000..bcf83de --- /dev/null +++ b/backend/HelpDesk.Models/Entities/SLAPolicy.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HelpDesk.Models.Entities; + +[Table("sla_policies")] +public class SLAPolicy +{ + [Key] + public Guid Id { get; set; } + + [Required] + [MaxLength(100)] + public string Name { get; set; } = string.Empty; + + [MaxLength(500)] + public string? Description { get; set; } + + public int ResponseTimeHours { get; set; } + public int ResolutionTimeHours { get; set; } + + public bool IsActive { get; set; } = true; + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + // Navigation + public ICollection Tickets { get; set; } = new List(); +} diff --git a/backend/HelpDesk.Models/Entities/Ticket.cs b/backend/HelpDesk.Models/Entities/Ticket.cs new file mode 100644 index 0000000..5ae8d02 --- /dev/null +++ b/backend/HelpDesk.Models/Entities/Ticket.cs @@ -0,0 +1,49 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using HelpDesk.Models.Enums; + +namespace HelpDesk.Models.Entities; + +[Table("tickets")] +public class Ticket +{ + [Key] + public Guid Id { get; set; } + + [Required] + [MaxLength(200)] + public string Title { get; set; } = string.Empty; + + [Required] + public string Description { get; set; } = string.Empty; + + [Required] + [MaxLength(50)] + public TicketStatus Status { get; set; } = TicketStatus.Open; + + [Required] + [MaxLength(50)] + public TicketPriority Priority { get; set; } = TicketPriority.Medium; + + [Required] + [MaxLength(50)] + public string TicketNumber { get; set; } = string.Empty; + + public Guid RequesterId { get; set; } + public Guid? AssigneeId { get; set; } + public Guid? GroupId { get; set; } + public Guid? SlaPolicyId { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; + public DateTime? FirstResponseAt { get; set; } + public DateTime? ResolvedAt { get; set; } + public DateTime? ClosedAt { get; set; } + + // Navigation + public User Requester { get; set; } = null!; + public User? Assignee { get; set; } + public Group? Group { get; set; } + public SLAPolicy? SlaPolicy { get; set; } + public ICollection Comments { get; set; } = new List(); +} diff --git a/backend/HelpDesk.Models/Entities/TicketComment.cs b/backend/HelpDesk.Models/Entities/TicketComment.cs new file mode 100644 index 0000000..8aa03c5 --- /dev/null +++ b/backend/HelpDesk.Models/Entities/TicketComment.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace HelpDesk.Models.Entities; + +[Table("ticket_comments")] +public class TicketComment +{ + [Key] + public Guid Id { get; set; } + + public Guid TicketId { get; set; } + public Guid UserId { get; set; } + + [Required] + public string Content { get; set; } = string.Empty; + + public bool IsInternal { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + // Navigation + public Ticket Ticket { get; set; } = null!; + public User User { get; set; } = null!; +} diff --git a/backend/HelpDesk.Models/Entities/User.cs b/backend/HelpDesk.Models/Entities/User.cs new file mode 100644 index 0000000..4db9e32 --- /dev/null +++ b/backend/HelpDesk.Models/Entities/User.cs @@ -0,0 +1,50 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using HelpDesk.Models.Enums; + +namespace HelpDesk.Models.Entities; + +[Table("users")] +public class User +{ + [Key] + public Guid Id { get; set; } + + [Required] + [MaxLength(100)] + public string FirstName { get; set; } = string.Empty; + + [Required] + [MaxLength(100)] + public string LastName { get; set; } = string.Empty; + + [Required] + [MaxLength(256)] + public string Email { get; set; } = string.Empty; + + [Required] + [MaxLength(500)] + public string PasswordHash { get; set; } = string.Empty; + + [Required] + [MaxLength(50)] + public UserRole Role { get; set; } = UserRole.Customer; + + public bool IsActive { get; set; } = true; + + [MaxLength(50)] + public string? PhoneNumber { get; set; } + + [MaxLength(500)] + public string? AvatarUrl { get; set; } + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; + + // Navigation + public ICollection RequestedTickets { get; set; } = new List(); + public ICollection AssignedTickets { get; set; } = new List(); + public ICollection Comments { get; set; } = new List(); + public ICollection Articles { get; set; } = new List(); + public ICollection ActivityLogs { get; set; } = new List(); +} diff --git a/backend/HelpDesk.Models/Enums/TicketPriority.cs b/backend/HelpDesk.Models/Enums/TicketPriority.cs new file mode 100644 index 0000000..15cc65b --- /dev/null +++ b/backend/HelpDesk.Models/Enums/TicketPriority.cs @@ -0,0 +1,9 @@ +namespace HelpDesk.Models.Enums; + +public enum TicketPriority +{ + Low = 0, + Medium = 1, + High = 2, + Critical = 3 +} diff --git a/backend/HelpDesk.Models/Enums/TicketStatus.cs b/backend/HelpDesk.Models/Enums/TicketStatus.cs new file mode 100644 index 0000000..24f5934 --- /dev/null +++ b/backend/HelpDesk.Models/Enums/TicketStatus.cs @@ -0,0 +1,10 @@ +namespace HelpDesk.Models.Enums; + +public enum TicketStatus +{ + Open = 0, + InProgress = 1, + Pending = 2, + Resolved = 3, + Closed = 4 +} diff --git a/backend/HelpDesk.Models/Enums/UserRole.cs b/backend/HelpDesk.Models/Enums/UserRole.cs new file mode 100644 index 0000000..9e72055 --- /dev/null +++ b/backend/HelpDesk.Models/Enums/UserRole.cs @@ -0,0 +1,9 @@ +namespace HelpDesk.Models.Enums; + +public enum UserRole +{ + Customer = 0, + Agent = 1, + Admin = 2, + SuperAdmin = 3 +} diff --git a/backend/HelpDesk.Models/HelpDesk.Models.csproj b/backend/HelpDesk.Models/HelpDesk.Models.csproj new file mode 100644 index 0000000..b760144 --- /dev/null +++ b/backend/HelpDesk.Models/HelpDesk.Models.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/backend/HelpDesk.slnx b/backend/HelpDesk.slnx new file mode 100644 index 0000000..e43c2e3 --- /dev/null +++ b/backend/HelpDesk.slnx @@ -0,0 +1,5 @@ + + + + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b496aa2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,126 @@ +# ============================================================ +# HelpDesk ICHN - Docker Compose Principal +# Caddy + Backend .NET 10 + Frontend Next.js 15 + PG + Redis +# ============================================================ + +networks: + helpdesk-net: + name: helpdesk-net + driver: bridge + +volumes: + pgdata-helpdesk: + redis-data: + caddy-data: + caddy-config: + +services: + # ============================================= + # GATEWAY - Caddy Reverse Proxy + # ============================================= + caddy: + image: caddy:2-alpine + container_name: helpdesk-caddy + restart: unless-stopped + ports: + - "8080:80" + - "8443:443" + volumes: + - ./gateway/Caddyfile:/etc/caddy/Caddyfile:ro + - ./gateway/certs:/certs:ro + - caddy-data:/data + - caddy-config:/config + networks: + - helpdesk-net + + # ============================================= + # BACKEND - .NET 10 (HelpDesk.API) + # ============================================= + api: + build: + context: ./backend + dockerfile: Dockerfile + container_name: helpdesk-api + restart: unless-stopped + environment: + ASPNETCORE_URLS: http://+:5000 + DB_CONNECTION: Host=helpdesk-postgres;Database=helpdesk;Username=helpdesk;Password=${PG_PASSWORD:-helpdesk_pass} + JWT_SECRET: ${JWT_SECRET:-your-super-secret-key-change-in-production-2024} + JWT_ISSUER: HelpDeskICHH + JWT_AUDIENCE: HelpDeskICHH + JWT_EXPIRY_MINUTES: "480" + FrontendUrl: http://localhost:3000 + networks: + - helpdesk-net + healthcheck: + test: ["CMD-SHELL", "pgrep -x dotnet || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s + depends_on: + postgres: + condition: service_healthy + + # ============================================= + # FRONTEND - Next.js 15 + # ============================================= + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: helpdesk-frontend + restart: unless-stopped + environment: + NEXT_PUBLIC_API_URL: http://localhost:5000 + networks: + - helpdesk-net + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:3000 || exit 1"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 20s + + # ============================================= + # DATABASE - PostgreSQL 16 + # ============================================= + postgres: + image: postgres:16-alpine + container_name: helpdesk-postgres + restart: unless-stopped + environment: + POSTGRES_DB: helpdesk + POSTGRES_USER: helpdesk + POSTGRES_PASSWORD: ${PG_PASSWORD:-helpdesk_pass} + volumes: + - pgdata-helpdesk:/var/lib/postgresql/data + - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql:ro + ports: + - "5433:5432" + networks: + - helpdesk-net + healthcheck: + test: ["CMD-SHELL", "pg_isready -U helpdesk -d helpdesk"] + interval: 10s + timeout: 5s + retries: 5 + + # ============================================= + # CACHE - Redis 7 + # ============================================= + redis: + image: redis:7-alpine + container_name: helpdesk-redis + restart: unless-stopped + volumes: + - redis-data:/data + ports: + - "6381:6379" + networks: + - helpdesk-net + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md new file mode 100644 index 0000000..643577d --- /dev/null +++ b/frontend/AGENTS.md @@ -0,0 +1,9 @@ + + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/frontend/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..f322870 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,38 @@ +FROM node:24-alpine AS base + +# --- DEPENDENCIES --- +FROM base AS deps +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci + +# --- BUILD --- +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_ENV=production +RUN npm run build + +# --- PRODUCTION --- +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs +EXPOSE 3000 +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs new file mode 100644 index 0000000..05e726d --- /dev/null +++ b/frontend/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/frontend/next.config.ts b/frontend/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/frontend/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..175a0c3 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,7273 @@ +{ + "name": "frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.1.0", + "dependencies": { + "@types/react-bootstrap": "^1.0.1", + "bootstrap": "^5.3.8", + "bootstrap-icons": "^1.13.1", + "next": "16.3.4", + "react": "^19.3.0", + "react-bootstrap": "^2.10.10", + "react-dom": "^19.3.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.3.4", + "tailwindcss": "^4", + "typescript": "^5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", + "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@internationalized/date": { + "version": "3.12.4", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.4.tgz", + "integrity": "sha512-M1dEn4c1U1HsSlaVR8upZtSqvXrTkHDfv18H01uCSJyjVLDxnBR38v/fMxecmlwXKR4i9HeZcmgQAPE6A+aGJQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.8", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.8.tgz", + "integrity": "sha512-8UmMFia46DUt+k97zKd9fKWXcWHR+k8ae3eYzILETuT2KbIvLyOfac7zesw+sJdRAAZ7Q9pM1Mk22aXp2LD0Ig==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/string": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.10.tgz", + "integrity": "sha512-PDx6//vHSpRnHfxqMqto11zQvhsaU74O3mKv2F/0eicGZcl9NLjQmGlbHz/LsJh5tLKp4A4L7ZVTzN1/MmMTvA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@next/env": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.4.tgz", + "integrity": "sha512-cjWZnUUa6jZq2kFaNe/ZyJdZonOZ/QoN0Zka2nz/FLOrfx14pQuM9c5RaSVkWMqgdt4ksgPAMWPyHSs/CyV48Q==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.4.tgz", + "integrity": "sha512-szW9y2Aumu4z88YXfTzcFsgUAg2k64uzbtcO5L9f1AKS4w/GUKJcbFllRflROVyNPgJtGOnvNxiyp3v6b+prIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "4.9.1", + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.4.tgz", + "integrity": "sha512-iBr3I5LZNk5/bgl5//iTgD2tcym14MX0Xo7fD//u9dYAEgGzza1y9oywluPtf74YnOswVdH1908aK9xVz7zQTw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.4.tgz", + "integrity": "sha512-2dpiSyl2Jw/NrBPaU2MAKGSa+2MR82pJIn4Sm5Rjr+gxAeuh0z158Su3Z2O8zn7UNNq+ej4bToed6RcRN/Lydg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.4.tgz", + "integrity": "sha512-+t+U8HZT+fApePCS5h89CSH3datz29MkzyfCn+6fpsZBG/oiEOhINcb9rtkv6sdpToLGFn2e6146NzaKCXkqrA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.4.tgz", + "integrity": "sha512-mx03GNs1ocQA5JQ4FxDMmIsNkdrZh8cuezKCrId28e5/gIPU/l7Kcy2+vmCCzdjnnmXJy+iOAu+7K0QppO6Urg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.4.tgz", + "integrity": "sha512-YIhGY6fSMfha52bnVxnzc9zaVBzJg+cqQTOD8tXIBSx4fuv0pVMxQTE0PaS59YhnMOiYiG09IMwxJAf/CFm/Dw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.4.tgz", + "integrity": "sha512-+eaaX6axpDb0yF1GCpiERe6njplvdC+nks/fKfcHu3XPGRrald8P3/X7yv7QLdjA51knnxwl9pxdIJsg+w1L+Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.4.tgz", + "integrity": "sha512-0jcXW7Xs/uzICrmgV3MhDYDeRy++1CqnpDIerlPIqYO4bhzB4WNbX/aRnQclustsAyTkFKB0z6rbcjmNg5tR8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.4.tgz", + "integrity": "sha512-vvBzwu1pYQCp92maZCFCIw/XgOTMR5tur9GjakwIo2cmwRTMKajRZZDS9+e4KsUZWKu1E007WUeAFXRRjZeuzw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.10.1.tgz", + "integrity": "sha512-jn038/ZYmu6DpfXJ6r2U9zFFppjbc9wnApPJSCxao2RZVEqep4YyoniHSy8qv6V21/xyS4IV7W9a+X2jOjSuag==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0", + "react-aria": "^3.48.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.36.1.tgz", + "integrity": "sha512-AzsuD9OfxTOZMMvTRhlN3oHBwOmFN7tDh27LzqmHt4+uOgPhJT7ZM7/kVs/8/o0WxayMUIk3hBmCFRHv1FUoag==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.4.tgz", + "integrity": "sha512-N4C7haUc3vn4LTwVUPlkJN8Ach/+yIMvRuTVIhjilNHqegY60SGLrzud6errOMNJwSnmYFnt1J0H/k8FE3A4KA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@popperjs/core": "^2.11.8", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.5.0", + "@types/warning": "^3.0.3", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.4", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/@restart/hooks": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.1.tgz", + "integrity": "sha512-EMoH04NHS1pbn07iLTjIjgttuqb7qu4+/EyhAx27MHpoENcB2ZdSsLTNxmKD+WEPnZigo62Qc8zjGnNxoSE/5Q==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.3.tgz", + "integrity": "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "postcss": "^8.5.16", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-bootstrap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/react-bootstrap/-/react-bootstrap-1.0.1.tgz", + "integrity": "sha512-n68SeFrpOFWiSN2DCpyn17ZwTZzY8+mio8E9oCrl7abHytUz3r7ZYIKPxaETHYQxCi8oxF3NultpMAyBxFIjhg==", + "license": "MIT" + }, + "node_modules/@types/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/warning": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.4.tgz", + "integrity": "sha512-CqN8MnISMwQbLJXO3doBAV4Yw9hx9/Pyr2rZ78+NfaCnhyRA/nKrpyk6E7mKw17ZOaQdLpK9GiUjrqLzBlN3sg==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz", + "integrity": "sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/type-utils": "8.70.0", + "@typescript-eslint/utils": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.70.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.9.tgz", + "integrity": "sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.70.0.tgz", + "integrity": "sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.0.tgz", + "integrity": "sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.70.0", + "@typescript-eslint/types": "^8.70.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.0.tgz", + "integrity": "sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.0.tgz", + "integrity": "sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.0.tgz", + "integrity": "sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/utils": "8.70.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.0.tgz", + "integrity": "sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.0.tgz", + "integrity": "sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.70.0", + "@typescript-eslint/tsconfig-utils": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.0.tgz", + "integrity": "sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.0.tgz", + "integrity": "sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.70.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.13.0.tgz", + "integrity": "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.423", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.423.tgz", + "integrity": "sha512-rRZfTSY8ptHYMQxa+uIycJMFKmY1T0GIApNMXJYGehguTZa56TEEl19pKPCoBqk5Gpf7QizZn/jt7xur+DYxag==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz", + "integrity": "sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.3.4.tgz", + "integrity": "sha512-35/8RM10huEL9vlr8hUZMERMENHBrnyHN3ZZkF9efSgzGaqK34jIqry44A956//zriUhUAUW0XSkcolhrryqAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "16.3.4", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "16.3.4", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.4.tgz", + "integrity": "sha512-/Ztf6CeRH+ejEXUrYtqI4gkS66eFIHuSwqi60RgcpWKodxFZx2/dqVCMKBwILfAHXQ+F1b1vAudgj3mnxqtoIA==", + "license": "MIT", + "dependencies": { + "@next/env": "16.3.4", + "@swc/helpers": "0.5.23", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.5.23", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.3.4", + "@next/swc-darwin-x64": "16.3.4", + "@next/swc-linux-arm64-gnu": "16.3.4", + "@next/swc-linux-arm64-musl": "16.3.4", + "@next/swc-linux-x64-gnu": "16.3.4", + "@next/swc-linux-x64-musl": "16.3.4", + "@next/swc-win32-arm64-msvc": "16.3.4", + "@next/swc-win32-x64-msvc": "16.3.4", + "sharp": "^0.35.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", + "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-releases": { + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "license": "MIT", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-aria": { + "version": "3.52.1", + "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.52.1.tgz", + "integrity": "sha512-fdZZruC9/x/joCg0mhKGs5aHpwrXLCSZ4GOJmhhYyiE0ffyEsk9MLFt9LCOCF9tn8ErTD+UDQP4oDUSlZkmpCg==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.4", + "@internationalized/number": "^3.6.8", + "@internationalized/string": "^3.2.10", + "@react-types/shared": "^3.36.1", + "@swc/helpers": "^0.5.0", + "aria-hidden": "^1.2.3", + "clsx": "^2.0.0", + "react-stately": "3.50.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/react-bootstrap": { + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.10.tgz", + "integrity": "sha512-gMckKUqn8aK/vCnfwoBpBVFUGT9SVQxwsYrp9yDHt0arXMamxALerliKBxr1TPbntirK/HGrUAHYbAeQTa9GHQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.9.4", + "@types/prop-types": "^15.7.12", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" + }, + "node_modules/react-stately": { + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.50.0.tgz", + "integrity": "sha512-TnckvpDQGU0672wEaLZqdzvhuSeXWjgW6vijMWxiKOxc8CosQUfPGISdcZyfHqjX3XMjEtRxj4w9HumvX4h4mw==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.12.4", + "@internationalized/number": "^3.6.8", + "@internationalized/string": "^3.2.10", + "@react-types/shared": "^3.36.1", + "@swc/helpers": "^0.5.0", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.1.0.tgz", + "integrity": "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "get-intrinsic": "^1.3.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.4", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.70.0.tgz", + "integrity": "sha512-P/W5cz70/cQAuKfY3xwQMWWTV7BvJ0mAQmi+9mBcsVPaBUpd6Ohpa+fECv9rBFrQcig86jAiNBFNWUqnTjr4pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.70.0", + "@typescript-eslint/parser": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/utils": "8.70.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..6607149 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,30 @@ +{ + "name": "frontend", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "@types/react-bootstrap": "^1.0.1", + "bootstrap": "^5.3.8", + "bootstrap-icons": "^1.13.1", + "next": "16.3.4", + "react": "^19.3.0", + "react-bootstrap": "^2.10.10", + "react-dom": "^19.3.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.3.4", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/frontend/postcss.config.mjs b/frontend/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/frontend/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/frontend/public/file.svg b/frontend/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/frontend/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/globe.svg b/frontend/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/frontend/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/next.svg b/frontend/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/frontend/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/vercel.svg b/frontend/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/frontend/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/window.svg b/frontend/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/frontend/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/app/(app)/knowledge-base/page.tsx b/frontend/src/app/(app)/knowledge-base/page.tsx new file mode 100644 index 0000000..60b513c --- /dev/null +++ b/frontend/src/app/(app)/knowledge-base/page.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { getKBArticles, searchKB, type KBArticle } from "@/lib/api"; + +export default function KnowledgeBasePage() { + const [articles, setArticles] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(true); + + useEffect(() => { + getKBArticles() + .then(setArticles) + .catch(() => { + setArticles([ + { id: 1, title: "Cómo configurar tu correo en Outlook", content: "Guía paso a paso para configurar...", category: "Correo", publishedAt: "2026-09-01" }, + { id: 2, title: "Solicitar acceso a una carpeta compartida", content: "Proceso para solicitar acceso...", category: "Acceso", publishedAt: "2026-09-02" }, + { id: 3, title: "Problemas comunes con la impresora", content: "Soluciones a problemas frecuentes...", category: "Hardware", publishedAt: "2026-09-03" }, + { id: 4, title: "Instalación de software aprobado", content: "Lista de software aprobado y cómo instalarlo...", category: "Software", publishedAt: "2026-09-04" }, + { id: 5, title: "Conectar a la VPN corporativa", content: "Instrucciones para conectarse a la VPN...", category: "Red", publishedAt: "2026-09-05" }, + { id: 6, title: "Resetear tu contraseña", content: "Cómo restablecer tu contraseña del dominio...", category: "Acceso", publishedAt: "2026-09-06" }, + ]); + }) + .finally(() => setLoading(false)); + }, []); + + const handleSearch = async () => { + if (!searchQuery.trim()) return; + setLoading(true); + try { + const results = await searchKB(searchQuery); + setArticles(results); + } catch { + // Keep existing articles on error + } finally { + setLoading(false); + } + }; + + const categories = [ + { name: "Software", icon: "bi-laptop", color: "#0d6efd", count: articles.filter((a) => a.category === "Software").length }, + { name: "Hardware", icon: "bi-pc-display", color: "#198754", count: articles.filter((a) => a.category === "Hardware").length }, + { name: "Red", icon: "bi-wifi", color: "#6f42c1", count: articles.filter((a) => a.category === "Red").length }, + { name: "Acceso", icon: "bi-key", color: "#fd7e14", count: articles.filter((a) => a.category === "Acceso").length }, + { name: "Correo", icon: "bi-envelope", color: "#dc3545", count: articles.filter((a) => a.category === "Correo").length }, + ]; + + return ( +
+
+

Base de Conocimiento

+
+ + {/* Search */} +
+
+
+ + + + setSearchQuery(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleSearch()} + /> + +
+
+
+ + {/* Categories */} +
+ {categories.map((cat) => ( +
+
{ setSearchQuery(cat.name); handleSearch(); }} + > +
+ +
{cat.name}
+ {cat.count} artículos +
+
+
+ ))} +
+ + {/* Articles List */} +
+
+ + Artículos Recientes +
+
+ {loading ? ( +
+
+
+ ) : articles.length === 0 ? ( +
+ +
No se encontraron artículos
+

Intenta con otros términos de búsqueda

+
+ ) : ( + articles.map((article) => ( +
(e.currentTarget.style.backgroundColor = "#f8f9fa")} + onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = "transparent")} + > +
+ +
+
+
{article.title}
+ {article.category} · {new Date(article.publishedAt).toLocaleDateString("es-CL")} +
+ +
+ )) + )} +
+
+
+ ); +} diff --git a/frontend/src/app/(app)/layout.tsx b/frontend/src/app/(app)/layout.tsx new file mode 100644 index 0000000..5a6d6ad --- /dev/null +++ b/frontend/src/app/(app)/layout.tsx @@ -0,0 +1,110 @@ +"use client"; + +import { useState } from "react"; +import { AuthProvider, useAuth } from "@/lib/auth"; +import Sidebar from "@/components/Sidebar"; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; + +function AppShell({ children }: { children: React.ReactNode }) { + const { isAuthenticated, isLoading } = useAuth(); + const router = useRouter(); + const [sidebarOpen, setSidebarOpen] = useState(false); + + useEffect(() => { + if (!isLoading && !isAuthenticated) { + router.push("/login"); + } + }, [isLoading, isAuthenticated, router]); + + if (isLoading) { + return ( +
+
+
+

Cargando...

+
+
+ ); + } + + if (!isAuthenticated) return null; + + return ( +
+ {/* Sidebar Overlay for mobile */} +
setSidebarOpen(false)} + /> + + {/* Sidebar */} +
+ +
+ + {/* Main Content */} +
+ {/* Top Navbar */} +
+ + +
+ + +
+ +
+ + + +
+
+ + {/* Page Content */} +
+ {children} +
+
+
+ ); +} + +function TopNavbarUser() { + const { user } = useAuth(); + return ( +
+
+ {user?.name + ?.split(" ") + .map((n) => n[0]) + .join("") + .toUpperCase() + .slice(0, 2) || "U"} +
+ + {user?.name || "Usuario"} + +
+ ); +} + +export default function AppLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/frontend/src/app/(app)/page.tsx b/frontend/src/app/(app)/page.tsx new file mode 100644 index 0000000..139a6cb --- /dev/null +++ b/frontend/src/app/(app)/page.tsx @@ -0,0 +1,204 @@ +"use client"; + +import { useEffect, useState } from "react"; +import StatsCard from "@/components/StatsCard"; +import BadgeStatus from "@/components/BadgeStatus"; +import { getDashboardStats, type DashboardStats } from "@/lib/api"; +import Link from "next/link"; + +export default function DashboardPage() { + const [stats, setStats] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + getDashboardStats() + .then(setStats) + .catch(() => { + // Set mock data for demo + setStats({ + totalTickets: 128, + openTickets: 24, + pendingTickets: 18, + resolvedTickets: 62, + closedTickets: 24, + avgResponseTime: "2.4h", + ticketsByPriority: { low: 30, medium: 45, high: 38, urgent: 15 }, + recentTickets: [], + agentWorkload: [ + { agent: "Carlos Méndez", count: 12 }, + { agent: "Ana García", count: 9 }, + { agent: "Roberto Díaz", count: 7 }, + { agent: "María López", count: 5 }, + ], + }); + }) + .finally(() => setLoading(false)); + }, []); + + if (loading) { + return ( +
+
+
+ ); + } + + const maxPriority = Math.max(...Object.values(stats?.ticketsByPriority || {}), 1); + const maxWorkload = Math.max(...(stats?.agentWorkload?.map((a) => a.count) || [1])); + + return ( +
+
+

Dashboard

+ + + Nuevo Ticket + +
+ + {/* Stats Cards */} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ {/* Priority Distribution */} +
+
+
+ + Tickets por Prioridad +
+
+ {Object.entries(stats?.ticketsByPriority || {}).map(([priority, count]) => { + const labels: Record = { low: "Baja", medium: "Media", high: "Alta", urgent: "Urgente" }; + const colors: Record = { low: "#6c757d", medium: "#0d6efd", high: "#fd7e14", urgent: "#dc3545" }; + return ( +
+
+ {labels[priority] || priority} + {count} +
+
+
+
+
+ ); + })} +
+
+
+ + {/* Agent Workload */} +
+
+
+ + Carga de Agentes +
+
+ {stats?.agentWorkload?.map((item) => ( +
+
+ {item.agent} + {item.count} tickets +
+
+
+
+
+ ))} + + {(!stats?.agentWorkload || stats.agentWorkload.length === 0) && ( +
+ + Sin datos de agentes disponibles +
+ )} +
+
+
+
+ + {/* Recent Tickets */} +
+
+
+ + Tickets Recientes +
+ + Ver todos + +
+
+ + + + + + + + + + + + + {stats?.recentTickets?.length ? ( + stats.recentTickets.map((ticket) => ( + window.location.href = `/tickets/${ticket.id}`}> + + + + + + + + )) + ) : ( + + + + )} + +
#AsuntoSolicitanteEstadoPrioridadActualizado
+ {ticket.ticketNumber || `#${ticket.id}`} + {ticket.subject}{ticket.requester?.name || "N/A"}{ticket.priority}{new Date(ticket.updatedAt).toLocaleDateString("es-CL")}
+ + No hay tickets recientes +
+
+
+
+ ); +} diff --git a/frontend/src/app/(app)/tickets/[id]/page.tsx b/frontend/src/app/(app)/tickets/[id]/page.tsx new file mode 100644 index 0000000..27e6fe1 --- /dev/null +++ b/frontend/src/app/(app)/tickets/[id]/page.tsx @@ -0,0 +1,320 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useParams, useRouter } from "next/navigation"; +import { getTicket, addComment, updateTicketStatus, assignTicket, getAgents, type Ticket, type User } from "@/lib/api"; +import BadgeStatus from "@/components/BadgeStatus"; +import BadgePriority from "@/components/BadgePriority"; +import { useAuth } from "@/lib/auth"; +import Link from "next/link"; + +export default function TicketDetailPage() { + const params = useParams(); + const router = useRouter(); + const { user } = useAuth(); + const id = params.id as string; + + const [ticket, setTicket] = useState(null); + const [agents, setAgents] = useState([]); + const [loading, setLoading] = useState(true); + const [comment, setComment] = useState(""); + const [isInternal, setIsInternal] = useState(false); + const [submitting, setSubmitting] = useState(false); + + useEffect(() => { + Promise.all([ + getTicket(id).catch(() => ({ + id: parseInt(id), + ticketNumber: "#TK-" + id.padStart(3, "0"), + subject: "No puedo acceder al sistema de correo electrónico desde esta mañana", + description: "Buenos días, desde esta mañana no puedo acceder a mi correo electrónico. Ya intenté reiniciar la computadora pero el problema persiste. Necesito acceso urgente para revisar correos importantes del proyecto.", + status: "open", + priority: "high", + category: "Software", + requesterId: 1, + groupId: null, + requester: { id: 1, name: "María García", email: "maria@test.com", role: "customer" as const }, + assigneeId: 2, + assignee: { id: 2, name: "Carlos Méndez", email: "carlos@test.com", role: "agent" as const }, + group: { id: 1, name: "Soporte TI" }, + comments: [ + { id: 1, ticketId: parseInt(id), userId: 2, user: { id: 2, name: "Carlos Méndez", email: "carlos@test.com", role: "agent" as const }, content: "Estoy revisando el problema. Verificaré la configuración de su cuenta de correo.", isInternal: false, createdAt: "2026-09-07T10:30:00Z" }, + { id: 2, ticketId: parseInt(id), userId: 1, user: { id: 1, name: "María García", email: "maria@test.com", role: "customer" as const }, content: "Gracias, estoy esperando. Necesito acceso lo antes posible.", isInternal: false, createdAt: "2026-09-07T11:15:00Z" }, + { id: 3, ticketId: parseInt(id), userId: 2, user: { id: 2, name: "Carlos Méndez", email: "carlos@test.com", role: "agent" as const }, content: "Nota interna: Verificar con el equipo de infra si hay problemas con el servidor de Exchange.", isInternal: true, createdAt: "2026-09-07T11:30:00Z" }, + ], + createdAt: "2026-09-07T09:00:00Z", + updatedAt: "2026-09-07T11:30:00Z", + } as Ticket)), + getAgents().catch(() => []), + ]).then(([ticketData, agentsData]) => { + setTicket(ticketData); + setAgents(agentsData); + }).finally(() => setLoading(false)); + }, [id]); + + const handleAddComment = async (e: React.FormEvent) => { + e.preventDefault(); + if (!comment.trim()) return; + setSubmitting(true); + try { + const newComment = await addComment(id, comment, isInternal); + setTicket((prev) => { + if (!prev) return prev; + return { ...prev, comments: [...(prev.comments || []), newComment] }; + }); + setComment(""); + setIsInternal(false); + } catch { + // Add locally for demo + if (ticket) { + const localComment = { + id: Date.now(), + ticketId: parseInt(id), + userId: user?.id || 0, + user: user || { id: 0, name: "Usuario", email: "", role: "agent" as const }, + content: comment, + isInternal, + createdAt: new Date().toISOString(), + }; + setTicket({ ...ticket, comments: [...(ticket.comments || []), localComment] }); + setComment(""); + setIsInternal(false); + } + } finally { + setSubmitting(false); + } + }; + + const handleStatusChange = async (newStatus: string) => { + try { + await updateTicketStatus(id, parseInt(newStatus)); + if (ticket) setTicket({ ...ticket, status: newStatus }); + } catch { + if (ticket) setTicket({ ...ticket, status: newStatus }); + } + }; + + const handleAssign = async (agentId: string) => { + if (!agentId) return; + try { + await assignTicket(id, parseInt(agentId)); + const agent = agents.find((a) => a.id === parseInt(agentId)); + if (ticket && agent) setTicket({ ...ticket, assigneeId: agent.id, assignee: agent }); + } catch { + // ignore + } + }; + + if (loading) { + return ( +
+
+
+ ); + } + + if (!ticket) { + return ( +
+ +
Ticket no encontrado
+ Volver a tickets +
+ ); + } + + return ( +
+ {/* Ticket Header */} +
+
+
+ + + + {ticket.ticketNumber || `#${ticket.id}`} +
+

{ticket.subject}

+
+ + +
+
+
+ {/* Status Change */} +
+ +
    + {[ + { value: "open", label: "Abierto" }, + { value: "pending", label: "Pendiente" }, + { value: "in_progress", label: "En Progreso" }, + { value: "resolved", label: "Resuelto" }, + { value: "closed", label: "Cerrado" }, + ].map((s) => ( +
  • + +
  • + ))} +
+
+ + {/* Assign */} +
+ +
    + {agents.map((a) => ( +
  • + +
  • + ))} +
+
+
+
+ +
+ {/* Comments Timeline */} +
+
+
+ + Comentarios ({ticket.comments?.length || 0}) +
+
+ {ticket.comments?.length ? ( + ticket.comments.map((c) => { + const isAgent = c.user?.role === "agent" || c.user?.role === "admin"; + return ( +
+
+
+ {c.user?.name || "Usuario"} + {c.isInternal && ( + + Nota Interna + + )} +
+
{c.content}
+
+ {new Date(c.createdAt).toLocaleString("es-CL")} +
+
+
+ ); + }) + ) : ( +
+ + No hay comentarios aún +
+ )} +
+
+ + {/* Comment Form */} +
+
+
+ +
+
+
+ setIsInternal(e.target.checked)} + /> + +
+ +
+
+
+
+ + {/* Ticket Info Sidebar */} +
+
+
Información del Ticket
+
+ Solicitante + {ticket.requester?.name || "N/A"} +
+
+ Email + {ticket.requester?.email || "N/A"} +
+
+ Asignado a + {ticket.assignee?.name || "Sin asignar"} +
+
+ Grupo + {ticket.group?.name || "N/A"} +
+
+ Categoría + {ticket.category || "N/A"} +
+
+ Creado + {new Date(ticket.createdAt).toLocaleString("es-CL")} +
+
+ Actualizado + {new Date(ticket.updatedAt).toLocaleString("es-CL")} +
+
+ + {/* Description */} +
+
Descripción
+

+ {ticket.description || "Sin descripción"} +

+
+
+
+
+ ); +} diff --git a/frontend/src/app/(app)/tickets/new/page.tsx b/frontend/src/app/(app)/tickets/new/page.tsx new file mode 100644 index 0000000..ee8430a --- /dev/null +++ b/frontend/src/app/(app)/tickets/new/page.tsx @@ -0,0 +1,191 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import { createTicket, getAgents, type User } from "@/lib/api"; +import Link from "next/link"; + +export default function NewTicketPage() { + const router = useRouter(); + const [agents, setAgents] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + const [subject, setSubject] = useState(""); + const [description, setDescription] = useState(""); + const [priority, setPriority] = useState("medium"); + const [category, setCategory] = useState(""); + const [assigneeId, setAssigneeId] = useState(""); + + useEffect(() => { + getAgents().catch((): User[] => [ + { id: 2, name: "Carlos Méndez", email: "carlos@test.com", role: "agent" as const }, + { id: 4, name: "Ana García", email: "ana@test.com", role: "agent" as const }, + ]).then((data) => setAgents(data)); + }, []); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + setLoading(true); + try { + const ticket = await createTicket({ + subject, + description, + priority, + category, + assigneeId: assigneeId ? parseInt(assigneeId) : undefined, + }); + router.push(`/tickets/${ticket.id}`); + } catch (err: any) { + setError(err.message || "Error al crear el ticket"); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+ + Volver a tickets + +

Nuevo Ticket

+
+
+ +
+
+
+
+ {error && ( +
+ + {error} +
+ )} + +
+
+ + setSubject(e.target.value)} + required + autoFocus + /> +
+ +
+ + +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ + + Cancelar + +
+
+
+
+
+ + {/* Tips */} +
+
+
+
+ + Consejos +
+
    +
  • Sé específico en el asunto del ticket
  • +
  • Incluye pasos para reproducir el problema
  • +
  • Adjunta capturas de pantalla si es posible
  • +
  • Selecciona la categoría correcta para una atención más rápida
  • +
  • Asigna la prioridad adecuada según el impacto
  • +
+
+
+
+
+
+ ); +} diff --git a/frontend/src/app/(app)/tickets/page.tsx b/frontend/src/app/(app)/tickets/page.tsx new file mode 100644 index 0000000..1ef8f20 --- /dev/null +++ b/frontend/src/app/(app)/tickets/page.tsx @@ -0,0 +1,176 @@ +"use client"; + +import { useEffect, useState, useCallback } from "react"; +import { useRouter } from "next/navigation"; +import { getTickets, type Ticket } from "@/lib/api"; +import BadgeStatus from "@/components/BadgeStatus"; +import BadgePriority from "@/components/BadgePriority"; +import PaginationComponent from "@/components/Pagination"; +import Link from "next/link"; + +export default function TicketsPage() { + const router = useRouter(); + const [tickets, setTickets] = useState([]); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [loading, setLoading] = useState(true); + const [statusFilter, setStatusFilter] = useState(""); + const [priorityFilter, setPriorityFilter] = useState(""); + const [search, setSearch] = useState(""); + const pageSize = 10; + + const fetchTickets = useCallback(async () => { + setLoading(true); + try { + const data = await getTickets({ + status: statusFilter || undefined, + priority: priorityFilter || undefined, + search: search || undefined, + page, + pageSize, + }); + setTickets(data.tickets || []); + setTotal(data.total || 0); + } catch { + setTickets([ + { id: 1, ticketNumber: "#TK-001", subject: "No puedo acceder a mi correo", description: "", status: "open", priority: "high", category: "Software", requesterId: 1, groupId: null, requester: { id: 1, name: "María García", email: "maria@test.com", role: "customer" }, assigneeId: 2, assignee: { id: 2, name: "Carlos Méndez", email: "carlos@test.com", role: "agent" }, createdAt: "2026-09-01", updatedAt: "2026-09-07" }, + { id: 2, ticketNumber: "#TK-002", subject: "Impresora del piso 3 no funciona", description: "", status: "pending", priority: "medium", category: "Hardware", requesterId: 3, groupId: null, requester: { id: 3, name: "Pedro Soto", email: "pedro@test.com", role: "customer" }, assigneeId: 4, assignee: { id: 4, name: "Ana García", email: "ana@test.com", role: "agent" }, createdAt: "2026-09-05", updatedAt: "2026-09-07" }, + { id: 3, ticketNumber: "#TK-003", subject: "Solicitud de acceso al sistema contable", description: "", status: "resolved", priority: "low", category: "Acceso", requesterId: 5, groupId: null, requester: { id: 5, name: "Laura Fuentes", email: "laura@test.com", role: "customer" }, assigneeId: null, createdAt: "2026-09-03", updatedAt: "2026-09-06" }, + { id: 4, ticketNumber: "#TK-004", subject: "Laptop con pantalla parpadeante", description: "", status: "in_progress", priority: "urgent", category: "Hardware", requesterId: 6, groupId: null, requester: { id: 6, name: "Roberto Díaz", email: "roberto@test.com", role: "customer" }, assigneeId: 2, assignee: { id: 2, name: "Carlos Méndez", email: "carlos@test.com", role: "agent" }, createdAt: "2026-09-08", updatedAt: "2026-09-08" }, + ]); + setTotal(4); + } finally { + setLoading(false); + } + }, [statusFilter, priorityFilter, search, page]); + + useEffect(() => { + fetchTickets(); + }, [fetchTickets]); + + const totalPages = Math.ceil(total / pageSize); + + return ( +
+
+

Tickets

+ + + Nuevo Ticket + +
+ + {/* Filters */} +
+
+ + Filtros: +
+ + + + + +
+ + { setSearch(e.target.value); setPage(1); }} + /> +
+
+ + {/* Tickets Table */} +
+ + + + + + + + + + + + + + {loading ? ( + + + + ) : tickets.length === 0 ? ( + + + + ) : ( + tickets.map((ticket) => ( + router.push(`/tickets/${ticket.id}`)} + style={{ cursor: "pointer" }} + > + + + + + + + + + )) + )} + +
#AsuntoSolicitanteEstadoPrioridadAsignadoActualizado
+
+
+ +
No se encontraron tickets
+

Prueba ajustando los filtros de búsqueda

+
+ {ticket.ticketNumber || `#${ticket.id}`} + +
{ticket.subject}
+ {ticket.category} +
{ticket.requester?.name || "N/A"}{ticket.assignee?.name || Sin asignar} + {new Date(ticket.updatedAt).toLocaleDateString("es-CL")} +
+
+ + {/* Pagination */} +
+ + Mostrando {tickets.length} de {total} tickets + + +
+
+ ); +} diff --git a/frontend/src/app/(auth)/login/page.tsx b/frontend/src/app/(auth)/login/page.tsx new file mode 100644 index 0000000..5e99e82 --- /dev/null +++ b/frontend/src/app/(auth)/login/page.tsx @@ -0,0 +1,133 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { useAuth } from "@/lib/auth"; + +export default function LoginPage() { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + const { login } = useAuth(); + const router = useRouter(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + setLoading(true); + try { + await login(email, password); + router.push("/"); + } catch (err: any) { + setError(err.message || "Credenciales inválidas"); + } finally { + setLoading(false); + } + }; + + return ( +
+ {/* Left Panel */} +
+
+ +

HelpDesk ICHN

+

+ Sistema de gestión de tickets de soporte técnico. Gestiona incidencias, + solicitudes y consultas de manera eficiente. +

+
+
+ + Gestión centralizada de tickets +
+
+ + Base de conocimiento integrada +
+
+ + SLA y métricas de rendimiento +
+
+
+
+ + {/* Right Panel - Form */} +
+
+
+ +

HelpDesk ICHN

+
+ +

Iniciar Sesión

+

Ingresa tus credenciales para acceder al sistema

+ + {error && ( +
+ + {error} +
+ )} + +
+
+ + setEmail(e.target.value)} + required + autoFocus + /> +
+ +
+ + setPassword(e.target.value)} + required + /> +
+ + +
+ +
+ + ¿No tienes cuenta?{" "} + + Regístrate aquí + + +
+
+
+
+ ); +} diff --git a/frontend/src/app/(auth)/register/page.tsx b/frontend/src/app/(auth)/register/page.tsx new file mode 100644 index 0000000..fd5ca43 --- /dev/null +++ b/frontend/src/app/(auth)/register/page.tsx @@ -0,0 +1,169 @@ +"use client"; + +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { register as apiRegister } from "@/lib/api"; +import { useAuth } from "@/lib/auth"; + +export default function RegisterPage() { + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + const { login } = useAuth(); + const router = useRouter(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + + if (password !== confirmPassword) { + setError("Las contraseñas no coinciden"); + return; + } + + setLoading(true); + try { + await apiRegister({ name, email, password }); + await login(email, password); + router.push("/"); + } catch (err: any) { + setError(err.message || "Error al registrar usuario"); + } finally { + setLoading(false); + } + }; + + return ( +
+ {/* Left Panel */} +
+
+ +

HelpDesk ICHN

+

+ Crea tu cuenta para comenzar a gestionar tickets de soporte técnico + y acceder a la base de conocimiento. +

+
+
+ + Acceso completo al sistema +
+
+ + Crear y responder tickets +
+
+ + Buscar en la base de conocimiento +
+
+
+
+ + {/* Right Panel - Form */} +
+
+
+ +

HelpDesk ICHN

+
+ +

Crear Cuenta

+

Regístrate para acceder al sistema de soporte

+ + {error && ( +
+ + {error} +
+ )} + +
+
+ + setName(e.target.value)} + required + autoFocus + /> +
+ +
+ + setEmail(e.target.value)} + required + /> +
+ +
+ + setPassword(e.target.value)} + required + minLength={6} + /> +
+ +
+ + setConfirmPassword(e.target.value)} + required + minLength={6} + /> +
+ + +
+ +
+ + ¿Ya tienes cuenta?{" "} + + Inicia sesión + + +
+
+
+
+ ); +} diff --git a/frontend/src/app/favicon.ico b/frontend/src/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/frontend/src/app/favicon.ico differ diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css new file mode 100644 index 0000000..34f9f91 --- /dev/null +++ b/frontend/src/app/globals.css @@ -0,0 +1,800 @@ +:root { + --primary: #00285d; + --primary-light: #1a3f7a; + --primary-lighter: #2a5a9a; + --primary-gradient: linear-gradient(135deg, #00285d 0%, #1a4a8a 100%); + --bg: #f0f2f5; + --card-shadow: 0 2px 12px rgba(0, 40, 93, 0.08); + --card-hover-shadow: 0 4px 20px rgba(0, 40, 93, 0.12); + --radius: 12px; + --radius-sm: 8px; + --text-dark: #1a1a2e; + --text-muted: #6c757d; + --sidebar-width: 260px; + --navbar-height: 64px; +} + +* { + box-sizing: border-box; +} + +body { + background-color: var(--bg); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + color: var(--text-dark); +} + +/* Bootstrap Primary Override */ +:root { + --bs-primary: #00285d; + --bs-primary-rgb: 0, 40, 93; + --bs-link-color: #00285d; + --bs-link-hover-color: #1a3f7a; +} + +.btn-primary { + background: var(--primary-gradient) !important; + border: none !important; + border-radius: var(--radius-sm) !important; + font-weight: 500; + padding: 0.5rem 1.25rem; + transition: all 0.2s ease; + box-shadow: 0 2px 8px rgba(0, 40, 93, 0.2); +} + +.btn-primary:hover { + transform: translateY(-1px); + box-shadow: 0 4px 16px rgba(0, 40, 93, 0.3); +} + +.btn-primary:active { + transform: translateY(0); +} + +.btn-outline-primary { + border-color: var(--primary) !important; + color: var(--primary) !important; + border-radius: var(--radius-sm) !important; +} + +.btn-outline-primary:hover { + background-color: var(--primary) !important; + color: #fff !important; +} + +/* Sidebar */ +.sidebar { + width: var(--sidebar-width); + min-height: 100vh; + background: linear-gradient(180deg, #00285d 0%, #001a3d 100%); + color: #fff; + position: fixed; + top: 0; + left: 0; + z-index: 1040; + display: flex; + flex-direction: column; + transition: transform 0.3s ease; +} + +.sidebar .nav-link { + color: rgba(255, 255, 255, 0.7); + padding: 0.75rem 1.25rem; + border-radius: var(--radius-sm); + margin: 0.125rem 0.75rem; + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 0.9rem; + transition: all 0.2s ease; + text-decoration: none; +} + +.sidebar .nav-link:hover { + color: #fff; + background-color: rgba(255, 255, 255, 0.1); +} + +.sidebar .nav-link.active { + color: #fff; + background-color: rgba(255, 255, 255, 0.15); + font-weight: 500; +} + +.sidebar .nav-link i { + font-size: 1.1rem; + width: 24px; + text-align: center; +} + +.sidebar-brand { + padding: 1.5rem 1.25rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + gap: 0.75rem; +} + +.sidebar-brand h5 { + margin: 0; + font-weight: 700; + font-size: 1.1rem; + letter-spacing: 0.02em; +} + +.sidebar-user { + padding: 1.25rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + margin-top: auto; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.sidebar-user .avatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.2); + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 0.9rem; +} + +.sidebar-user-info { + flex: 1; + min-width: 0; +} + +.sidebar-user-info .name { + font-size: 0.85rem; + font-weight: 600; + color: #fff; +} + +.sidebar-user-info .role { + font-size: 0.75rem; + color: rgba(255, 255, 255, 0.6); +} + +/* Top Navbar */ +.top-navbar { + height: var(--navbar-height); + background: #fff; + border-bottom: 1px solid #e9ecef; + display: flex; + align-items: center; + padding: 0 1.5rem; + position: sticky; + top: 0; + z-index: 1030; +} + +.top-navbar .search-input { + border-radius: 20px; + border: 1px solid #e9ecef; + padding: 0.5rem 1rem 0.5rem 2.5rem; + font-size: 0.875rem; + width: 300px; + max-width: 100%; + transition: border-color 0.2s; +} + +.top-navbar .search-input:focus { + border-color: var(--primary); + box-shadow: 0 0 0 0.15rem rgba(0, 40, 93, 0.15); + outline: none; +} + +.notification-btn { + position: relative; + background: none; + border: none; + color: var(--text-muted); + font-size: 1.25rem; + padding: 0.5rem; + border-radius: 50%; + transition: all 0.2s; +} + +.notification-btn:hover { + background-color: #f0f2f5; + color: var(--primary); +} + +.notification-badge { + position: absolute; + top: 2px; + right: 2px; + width: 18px; + height: 18px; + background: #dc3545; + color: #fff; + font-size: 0.65rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; +} + +/* Main Content */ +.main-content { + margin-left: var(--sidebar-width); + min-height: 100vh; +} + +.content-wrapper { + padding: 1.5rem; + max-width: 1400px; +} + +/* Cards */ +.card { + border: none; + border-radius: var(--radius); + box-shadow: var(--card-shadow); + background: #fff; + transition: box-shadow 0.2s ease; +} + +.card:hover { + box-shadow: var(--card-hover-shadow); +} + +.card-header { + background: transparent; + border-bottom: 1px solid #f0f2f5; + padding: 1rem 1.25rem; + font-weight: 600; +} + +/* Stats Card */ +.stats-card { + border-radius: var(--radius); + padding: 1.25rem; + background: #fff; + border: none; + box-shadow: var(--card-shadow); + transition: all 0.2s ease; +} + +.stats-card:hover { + transform: translateY(-2px); + box-shadow: var(--card-hover-shadow); +} + +.stats-card .stats-icon { + width: 48px; + height: 48px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.25rem; +} + +.stats-card .stats-value { + font-size: 1.75rem; + font-weight: 700; + color: var(--text-dark); + line-height: 1; +} + +.stats-card .stats-label { + font-size: 0.8rem; + color: var(--text-muted); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-top: 0.25rem; +} + +/* Status Badges */ +.badge-status { + padding: 0.35rem 0.75rem; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.02em; +} + +.badge-open { + background-color: #d1e7dd; + color: #0f5132; +} + +.badge-pending { + background-color: #fff3cd; + color: #664d03; +} + +.badge-resolved { + background-color: #cfe2ff; + color: #084298; +} + +.badge-closed { + background-color: #e2e3e5; + color: #41464b; +} + +.badge-in-progress { + background-color: #cff4fc; + color: #055160; +} + +/* Priority Badges */ +.badge-priority { + padding: 0.35rem 0.75rem; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.02em; +} + +.badge-low { + background-color: #e2e3e5; + color: #41464b; +} + +.badge-medium { + background-color: #cfe2ff; + color: #084298; +} + +.badge-high { + background-color: #ffecb5; + color: #664d03; +} + +.badge-urgent { + background-color: #f8d7da; + color: #842029; +} + +/* Tables */ +.table-modern { + background: #fff; + border-radius: var(--radius); + overflow: hidden; + box-shadow: var(--card-shadow); +} + +.table-modern thead { + background: #f8f9fa; +} + +.table-modern thead th { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + font-weight: 600; + padding: 0.875rem 1rem; + border-bottom: 2px solid #e9ecef; +} + +.table-modern tbody tr { + transition: background-color 0.15s; + cursor: pointer; +} + +.table-modern tbody tr:hover { + background-color: #f8f9fa; +} + +.table-modern tbody td { + padding: 0.875rem 1rem; + vertical-align: middle; + font-size: 0.875rem; + border-bottom: 1px solid #f0f2f5; +} + +.table-modern tbody tr:last-child td { + border-bottom: none; +} + +/* Login/Register Page */ +.auth-page { + min-height: 100vh; +} + +.auth-panel-left { + background: var(--primary-gradient); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 3rem; + color: #fff; + text-align: center; +} + +.auth-panel-left h2 { + font-weight: 700; + font-size: 2rem; + margin-top: 1.5rem; + margin-bottom: 0.5rem; +} + +.auth-panel-left p { + font-size: 1rem; + opacity: 0.85; + max-width: 320px; +} + +.auth-panel-right { + display: flex; + align-items: center; + justify-content: center; + padding: 2rem; +} + +.auth-form { + width: 100%; + max-width: 400px; +} + +.auth-form h3 { + font-weight: 700; + color: var(--text-dark); + margin-bottom: 0.25rem; +} + +.auth-form .subtitle { + color: var(--text-muted); + margin-bottom: 2rem; +} + +.auth-form .form-label { + font-size: 0.85rem; + font-weight: 500; + color: var(--text-dark); +} + +.auth-form .form-control { + border-radius: var(--radius-sm); + border: 1px solid #dee2e6; + padding: 0.625rem 0.875rem; + font-size: 0.9rem; +} + +.auth-form .form-control:focus { + border-color: var(--primary); + box-shadow: 0 0 0 0.15rem rgba(0, 40, 93, 0.15); +} + +/* Page Header */ +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.5rem; +} + +.page-header h1 { + font-size: 1.5rem; + font-weight: 700; + color: var(--text-dark); + margin: 0; +} + +/* Filter Bar */ +.filter-bar { + background: #fff; + border-radius: var(--radius); + padding: 1rem 1.25rem; + box-shadow: var(--card-shadow); + display: flex; + gap: 0.75rem; + align-items: center; + flex-wrap: wrap; +} + +.filter-bar .form-select, +.filter-bar .form-control { + border-radius: var(--radius-sm); + border: 1px solid #dee2e6; + font-size: 0.85rem; + padding: 0.5rem 0.75rem; +} + +/* Ticket Detail */ +.ticket-header { + background: #fff; + border-radius: var(--radius); + padding: 1.25rem 1.5rem; + box-shadow: var(--card-shadow); + margin-bottom: 1.25rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; +} + +.ticket-number { + font-size: 0.8rem; + color: var(--text-muted); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.ticket-subject { + font-size: 1.25rem; + font-weight: 700; + color: var(--text-dark); + margin: 0; +} + +.ticket-info-card { + background: #fff; + border-radius: var(--radius); + box-shadow: var(--card-shadow); + padding: 1.25rem; + margin-bottom: 1rem; +} + +.ticket-info-card h6 { + font-weight: 600; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #f0f2f5; +} + +.ticket-info-row { + display: flex; + justify-content: space-between; + padding: 0.5rem 0; + font-size: 0.875rem; +} + +.ticket-info-row .label { + color: var(--text-muted); +} + +.ticket-info-row .value { + font-weight: 500; + color: var(--text-dark); +} + +/* Comments Timeline */ +.comment { + margin-bottom: 1rem; +} + +.comment-agent { + display: flex; + justify-content: flex-end; +} + +.comment-agent .comment-bubble { + background: var(--primary); + color: #fff; + border-radius: var(--radius) var(--radius) 4px var(--radius); + padding: 0.875rem 1rem; + max-width: 70%; +} + +.comment-customer { + display: flex; + justify-content: flex-start; +} + +.comment-customer .comment-bubble { + background: #f0f2f5; + color: var(--text-dark); + border-radius: var(--radius) var(--radius) var(--radius) 4px; + padding: 0.875rem 1rem; + max-width: 70%; +} + +.comment-internal .comment-bubble { + background: #fff3cd; + border-left: 4px solid #ffc107; +} + +.comment-author { + font-size: 0.75rem; + font-weight: 600; + margin-bottom: 0.25rem; + opacity: 0.8; +} + +.comment-time { + font-size: 0.7rem; + opacity: 0.6; + margin-top: 0.25rem; +} + +.comment-text { + font-size: 0.875rem; + line-height: 1.5; +} + +/* Comment Form */ +.comment-form { + background: #fff; + border-radius: var(--radius); + box-shadow: var(--card-shadow); + padding: 1.25rem; +} + +/* Pagination */ +.pagination-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 0; +} + +.pagination-info { + font-size: 0.85rem; + color: var(--text-muted); +} + +/* Responsive */ +@media (max-width: 991.98px) { + .sidebar { + transform: translateX(-100%); + } + + .sidebar.show { + transform: translateX(0); + } + + .main-content { + margin-left: 0; + } + + .content-wrapper { + padding: 1rem; + } + + .top-navbar .search-input { + width: 200px; + } + + .auth-panel-left { + display: none; + } + + .auth-panel-right { + min-height: 100vh; + } +} + +@media (max-width: 767.98px) { + .page-header { + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; + } + + .ticket-header { + flex-direction: column; + align-items: flex-start; + } + + .comment-bubble { + max-width: 90% !important; + } + + .filter-bar { + flex-direction: column; + } + + .filter-bar .form-select, + .filter-bar .form-control { + width: 100%; + } +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: #f0f2f5; +} + +::-webkit-scrollbar-thumb { + background: #c4c8cc; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #a0a4a8; +} + +/* Loading Spinner */ +.loading-spinner { + display: flex; + justify-content: center; + align-items: center; + min-height: 200px; +} + +/* KB Search */ +.kb-search-input { + border-radius: var(--radius); + border: 2px solid #e9ecef; + padding: 0.875rem 1.25rem; + font-size: 1rem; + width: 100%; + transition: border-color 0.2s; +} + +.kb-search-input:focus { + border-color: var(--primary); + box-shadow: 0 0 0 0.15rem rgba(0, 40, 93, 0.1); + outline: none; +} + +/* Empty State */ +.empty-state { + text-align: center; + padding: 3rem 1rem; + color: var(--text-muted); +} + +.empty-state i { + font-size: 3rem; + margin-bottom: 1rem; + opacity: 0.3; +} + +.empty-state h5 { + font-weight: 600; + color: var(--text-dark); +} + +/* Sidebar overlay for mobile */ +.sidebar-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 1035; +} + +@media (max-width: 991.98px) { + .sidebar-overlay.show { + display: block; + } +} + +/* Transition utilities */ +.fade-in { + animation: fadeIn 0.3s ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} + +/* User avatar circle */ +.avatar-circle { + width: 36px; + height: 36px; + border-radius: 50%; + background: var(--primary-gradient); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 0.8rem; +} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx new file mode 100644 index 0000000..9115c00 --- /dev/null +++ b/frontend/src/app/layout.tsx @@ -0,0 +1,36 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "bootstrap/dist/css/bootstrap.min.css"; +import "./globals.css"; + +const inter = Inter({ + variable: "--font-inter", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "HelpDesk ICHN", + description: "Sistema de Gestión de Tickets de Soporte - ICHN", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + + + {children} + + ); +} diff --git a/frontend/src/components/BadgePriority.tsx b/frontend/src/components/BadgePriority.tsx new file mode 100644 index 0000000..aec1779 --- /dev/null +++ b/frontend/src/components/BadgePriority.tsx @@ -0,0 +1,15 @@ +const priorityConfig: Record = { + low: { className: "badge-low", label: "Baja" }, + medium: { className: "badge-medium", label: "Media" }, + high: { className: "badge-high", label: "Alta" }, + urgent: { className: "badge-urgent", label: "Urgente" }, + "1": { className: "badge-low", label: "Baja" }, + "2": { className: "badge-medium", label: "Media" }, + "3": { className: "badge-high", label: "Alta" }, + "4": { className: "badge-urgent", label: "Urgente" }, +}; + +export default function BadgePriority({ priority }: { priority: string }) { + const config = priorityConfig[priority] || { className: "badge-medium", label: priority }; + return {config.label}; +} diff --git a/frontend/src/components/BadgeStatus.tsx b/frontend/src/components/BadgeStatus.tsx new file mode 100644 index 0000000..c0ef9f6 --- /dev/null +++ b/frontend/src/components/BadgeStatus.tsx @@ -0,0 +1,17 @@ +const statusConfig: Record = { + open: { className: "badge-open", label: "Abierto" }, + pending: { className: "badge-pending", label: "Pendiente" }, + in_progress: { className: "badge-in-progress", label: "En Progreso" }, + resolved: { className: "badge-resolved", label: "Resuelto" }, + closed: { className: "badge-closed", label: "Cerrado" }, + "1": { className: "badge-open", label: "Abierto" }, + "2": { className: "badge-pending", label: "Pendiente" }, + "3": { className: "badge-in-progress", label: "En Progreso" }, + "4": { className: "badge-resolved", label: "Resuelto" }, + "5": { className: "badge-closed", label: "Cerrado" }, +}; + +export default function BadgeStatus({ status }: { status: string }) { + const config = statusConfig[status] || { className: "badge-open", label: status }; + return {config.label}; +} diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx new file mode 100644 index 0000000..12ccac6 --- /dev/null +++ b/frontend/src/components/Pagination.tsx @@ -0,0 +1,53 @@ +interface PaginationProps { + currentPage: number; + totalPages: number; + onPageChange: (page: number) => void; +} + +export default function PaginationComponent({ currentPage, totalPages, onPageChange }: PaginationProps) { + if (totalPages <= 1) return null; + + const pages: (number | "...")[] = []; + for (let i = 1; i <= totalPages; i++) { + if (i === 1 || i === totalPages || (i >= currentPage - 1 && i <= currentPage + 1)) { + pages.push(i); + } else if (pages[pages.length - 1] !== "...") { + pages.push("..."); + } + } + + return ( +
+ +
+ ); +} diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx new file mode 100644 index 0000000..b7698e7 --- /dev/null +++ b/frontend/src/components/Sidebar.tsx @@ -0,0 +1,67 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { useAuth } from "@/lib/auth"; + +export default function Sidebar() { + const pathname = usePathname(); + const { user, logout } = useAuth(); + + const navItems = [ + { href: "/", icon: "bi-speedometer2", label: "Dashboard" }, + { href: "/tickets", icon: "bi-ticket-detailed", label: "Tickets" }, + { href: "/knowledge-base", icon: "bi-journal-bookmark", label: "Knowledge Base" }, + { href: "/users", icon: "bi-people", label: "Users" }, + { href: "/settings", icon: "bi-gear", label: "Settings" }, + ]; + + return ( + + ); +} diff --git a/frontend/src/components/StatsCard.tsx b/frontend/src/components/StatsCard.tsx new file mode 100644 index 0000000..e034497 --- /dev/null +++ b/frontend/src/components/StatsCard.tsx @@ -0,0 +1,22 @@ +interface StatsCardProps { + icon: string; + value: string | number; + label: string; + color?: string; +} + +export default function StatsCard({ icon, value, label, color = "#00285d" }: StatsCardProps) { + return ( +
+
+
+ +
+
+
{value}
+
{label}
+
+
+
+ ); +} diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts new file mode 100644 index 0000000..5533149 --- /dev/null +++ b/frontend/src/lib/api.ts @@ -0,0 +1,252 @@ +const API_BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:5000"; + +async function fetchApi( + path: string, + options: RequestInit = {} +): Promise { + const token = + typeof window !== "undefined" ? localStorage.getItem("helpdesk_token") : null; + + const headers: Record = { + "Content-Type": "application/json", + ...((options.headers as Record) || {}), + }; + + if (token) { + headers["Authorization"] = `Bearer ${token}`; + } + + const res = await fetch(`${API_BASE}${path}`, { + ...options, + headers, + }); + + if (!res.ok) { + const errorData = await res.json().catch(() => ({})); + throw new Error(errorData.message || `API Error: ${res.status}`); + } + + if (res.status === 204) return undefined as T; + return res.json(); +} + +// Auth +export async function login(email: string, password: string) { + return fetchApi<{ token: string; user: User }>("/api/auth/login", { + method: "POST", + body: JSON.stringify({ email, password }), + }); +} + +export async function register(data: { + name: string; + email: string; + password: string; + role?: string; +}) { + return fetchApi<{ token: string; user: User }>("/api/auth/register", { + method: "POST", + body: JSON.stringify(data), + }); +} + +export async function getMe() { + return fetchApi("/api/auth/me"); +} + +// Tickets +export async function getTickets(params?: { + status?: string; + priority?: string; + assigneeId?: string; + requesterId?: string; + search?: string; + page?: number; + pageSize?: number; +}) { + const query = new URLSearchParams(); + if (params) { + Object.entries(params).forEach(([key, value]) => { + if (value !== undefined && value !== "") query.set(key, String(value)); + }); + } + return fetchApi<{ tickets: Ticket[]; total: number; page: number; pageSize: number }>( + `/api/tickets?${query.toString()}` + ); +} + +export async function getTicket(id: string) { + return fetchApi(`/api/tickets/${id}`); +} + +export async function createTicket(data: { + subject: string; + description: string; + priority?: string; + category?: string; + assigneeId?: number; +}) { + return fetchApi("/api/tickets", { + method: "POST", + body: JSON.stringify(data), + }); +} + +export async function updateTicket(id: string, data: Partial) { + return fetchApi(`/api/tickets/${id}`, { + method: "PUT", + body: JSON.stringify(data), + }); +} + +export async function deleteTicket(id: string) { + return fetchApi(`/api/tickets/${id}`, { + method: "DELETE", + }); +} + +export async function updateTicketStatus(id: string, status: number) { + return fetchApi(`/api/tickets/${id}/status`, { + method: "PUT", + body: JSON.stringify({ status }), + }); +} + +export async function assignTicket(id: string, assigneeId: number) { + return fetchApi(`/api/tickets/${id}/assign`, { + method: "PUT", + body: JSON.stringify({ assigneeId }), + }); +} + +export async function addComment( + ticketId: string, + content: string, + isInternal: boolean = false +) { + return fetchApi(`/api/tickets/${ticketId}/comments`, { + method: "POST", + body: JSON.stringify({ content, isInternal }), + }); +} + +export async function getTicketStats() { + return fetchApi("/api/tickets/stats"); +} + +// Users +export async function getUsers() { + return fetchApi("/api/users"); +} + +export async function getAgents() { + return fetchApi("/api/users/agents"); +} + +// Knowledge Base +export async function getKBArticles() { + return fetchApi("/api/kb/articles/published"); +} + +export async function searchKB(query: string) { + return fetchApi(`/api/kb/search?q=${encodeURIComponent(query)}`); +} + +// Dashboard +export async function getDashboardStats() { + return fetchApi("/api/dashboard/stats"); +} + +// Groups & SLA +export async function getGroups() { + return fetchApi("/api/groups"); +} + +export async function getSLAPolicies() { + return fetchApi("/api/sla"); +} + +// Types +export interface User { + id: number; + name: string; + email: string; + role: "admin" | "agent" | "customer"; + createdAt?: string; +} + +export interface Ticket { + id: number; + ticketNumber: string; + subject: string; + description: string; + status: string; + priority: string; + category: string; + requesterId: number; + requester?: User; + assigneeId: number | null; + assignee?: User; + groupId: number | null; + group?: Group; + comments?: Comment[]; + createdAt: string; + updatedAt: string; +} + +export interface Comment { + id: number; + ticketId: number; + userId: number; + user?: User; + content: string; + isInternal: boolean; + createdAt: string; +} + +export interface Group { + id: number; + name: string; + description?: string; +} + +export interface KBArticle { + id: number; + title: string; + content: string; + category: string; + author?: User; + publishedAt: string; +} + +export interface SLAPolicy { + id: number; + name: string; + priority: string; + responseTime: string; + resolutionTime: string; +} + +export interface TicketStats { + total: number; + open: number; + pending: number; + resolved: number; + closed: number; + inProgress: number; + avgResponseTime: string; + byPriority: Record; + byCategory: Record; +} + +export interface DashboardStats { + totalTickets: number; + openTickets: number; + pendingTickets: number; + resolvedTickets: number; + closedTickets: number; + avgResponseTime: string; + ticketsByPriority: Record; + recentTickets: Ticket[]; + agentWorkload: { agent: string; count: number }[]; +} diff --git a/frontend/src/lib/auth.tsx b/frontend/src/lib/auth.tsx new file mode 100644 index 0000000..2dc2d9d --- /dev/null +++ b/frontend/src/lib/auth.tsx @@ -0,0 +1,64 @@ +"use client"; + +import React, { createContext, useContext, useState, useEffect, useCallback } from "react"; +import { getMe, login as apiLogin, type User } from "./api"; + +interface AuthContextType { + user: User | null; + login: (email: string, password: string) => Promise; + logout: () => void; + isAuthenticated: boolean; + isLoading: boolean; +} + +const AuthContext = createContext({ + user: null, + login: async () => {}, + logout: () => {}, + isAuthenticated: false, + isLoading: true, +}); + +export function AuthProvider({ children }: { children: React.ReactNode }) { + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const token = localStorage.getItem("helpdesk_token"); + if (token) { + getMe() + .then((u) => { + setUser(u); + }) + .catch(() => { + localStorage.removeItem("helpdesk_token"); + }) + .finally(() => setIsLoading(false)); + } else { + setIsLoading(false); + } + }, []); + + const login = useCallback(async (email: string, password: string) => { + const { token, user } = await apiLogin(email, password); + localStorage.setItem("helpdesk_token", token); + setUser(user); + }, []); + + const logout = useCallback(() => { + localStorage.removeItem("helpdesk_token"); + setUser(null); + }, []); + + return ( + + {children} + + ); +} + +export function useAuth() { + return useContext(AuthContext); +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} diff --git a/gateway/Caddyfile b/gateway/Caddyfile new file mode 100644 index 0000000..eef481e --- /dev/null +++ b/gateway/Caddyfile @@ -0,0 +1,24 @@ +# HelpDesk ICHN - Caddyfile +# Reverse proxy for API + Frontend + +{ + admin off + auto_https off +} + +:80 { + # API routes + handle /api/* { + reverse_proxy api:5000 + } + + # Swagger (dev only) + handle /swagger/* { + reverse_proxy api:5000 + } + + # Frontend (Next.js) + handle { + reverse_proxy frontend:3000 + } +} diff --git a/scripts/init-db.sql b/scripts/init-db.sql new file mode 100644 index 0000000..3777c4e --- /dev/null +++ b/scripts/init-db.sql @@ -0,0 +1,7 @@ +-- HelpDesk ICHN - Database Initialization +-- This is handled by EF Core migrations, but we keep this for reference + +-- Ensure the database exists (already created by POSTGRES_DB env var) +-- Tables will be created by EF Core on first run + +SELECT 'HelpDesk database initialized' AS status;