diff --git a/kabinizer-back-end/kabinizer-api/Controllers/BookingRequestController.cs b/kabinizer-back-end/kabinizer-api/Controllers/BookingRequestController.cs index 5103ad7..a732209 100644 --- a/kabinizer-back-end/kabinizer-api/Controllers/BookingRequestController.cs +++ b/kabinizer-back-end/kabinizer-api/Controllers/BookingRequestController.cs @@ -1,9 +1,8 @@ using kabinizer_api.Dtos.BookingRequest; using kabinizer_api.Model; -using kabinizer_api.Services; +using kabinizer_api.Services.BookingRequest; using kabinizer_api.Services.Export; using kabinizer_data; -using kabinizer_data.Entities; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; @@ -12,71 +11,124 @@ namespace kabinizer_api.Controllers; [Route("api/[controller]")] [ApiController] -public class BookingRequestController(EntityContext entityContext, ITokenService tokenService) : ControllerBase +public class BookingRequestController( + EntityContext entityContext, + BookingRequestService bookingRequestService +) : ControllerBase { - [HttpGet] - public IEnumerable GetBookingRequests() + [HttpGet("{id:guid}")] + [ProducesResponseType(200, Type = typeof(BookingRequestDto))] + [ProducesResponseType(404, Type = typeof(string))] + public async Task> GetBookingRequest(Guid id) { - var currentUserId = tokenService.GetUserId(); - return entityContext.BookingRequests - .Include(br => br.User) - .Include(br => br.Period) - .Where(b => b.UserId == currentUserId) - .AsEnumerable().Select(BookingRequest.FromModel); + try + { + var bookingRequest = await bookingRequestService.GetBookingRequest(id); + if (bookingRequest == null) + { + return NotFound("Booking request does not exist or does not belong to the current user"); + } + + return Ok(BookingRequestDto.FromModel(bookingRequest)); + } + catch (Exception ex) + { + return NotFound(ex.Message); + } } - [HttpPost] - public void AddBookingRequests([Required] IEnumerable requests) + [HttpGet] + [ProducesResponseType(200, Type = typeof(IEnumerable))] + [ProducesResponseType(404, Type = typeof(string))] + public async Task>> GetBookingRequests() { - var currentUserId = tokenService.GetUserId(); - var periodIds = requests.Select(e => e.PeriodId).ToList(); - - var periods = entityContext.Periods - .Where(p => periodIds.Contains(p.Id)) - .Include(p => p.Draw) - .ToList(); - - if (periods.Count != periodIds.Count) + try { - throw new Exception("One or more periods do not exist"); + var bookingRequests = await bookingRequestService.GetBookingRequests(); + return Ok(bookingRequests.Select(BookingRequestDto.FromModel)); } - - var periodsWithoutDraw = periods.Where(p => p.Draw == null).ToList(); - if (periodsWithoutDraw.Count != 0) + catch (Exception ex) { - throw new Exception("One or more periods are not part of a draw"); + return NotFound(ex.Message); } - - var periodsWithEndedDraw = periods.Where(p => p.Draw?.DeadlineEnd > DateTime.Now).ToList(); - if (periodsWithEndedDraw.Count != 0) + } + + [HttpGet("user/{userId:guid}")] + [ProducesResponseType(200, Type = typeof(IEnumerable))] + [ProducesResponseType(404, Type = typeof(string))] + public async Task>> GetBookingRequestsByUser(Guid userId) + { + try { - throw new Exception("Cannot make a booking request for a draw that has ended"); + var bookingRequests = await bookingRequestService.GetBookingRequestsByUser(userId); + return Ok(bookingRequests.Select(BookingRequestDto.FromModel)); + } + catch (Exception ex) + { + return NotFound(ex.Message); + } + } + + [HttpGet("period/{periodId:guid}")] + [ProducesResponseType(200, Type = typeof(IEnumerable))] + [ProducesResponseType(404, Type = typeof(string))] + public async Task>> GetBookingRequestsByPeriod(Guid periodId) + { + try + { + var bookingRequests = await bookingRequestService.GetBookingRequestsByPeriod(periodId); + if (bookingRequests.Count == 0) + { + return NotFound("No booking requests found for the period"); + } + + return Ok(bookingRequests.Select(BookingRequestDto.FromModel)); + } + catch (Exception ex) + { + return NotFound(ex.Message); } - - var bookingRequestEntities = periodIds.Select(id => new BookingRequestEntity(currentUserId, id)); - entityContext.BookingRequests.AddRange(bookingRequestEntities); - entityContext.SaveChanges(); } - [HttpDelete] - public bool DeleteBookingRequests([Required] IEnumerable requests) + [HttpPost] + [ProducesResponseType(200, Type = typeof(BookingRequestDto))] + [ProducesResponseType(400, Type = typeof(string))] + public async Task>> AddBookingRequests([Required] IEnumerable requests) { - var currentUserId = tokenService.GetUserId(); + try + { + var bookingRequest = await bookingRequestService.AddBookingRequest(requests.First()); + if (bookingRequest == null) + { + return BadRequest("Booking request could not be added"); + } - foreach (Guid requestId in requests) + return Ok(BookingRequestDto.FromModel(bookingRequest)); + } + catch (Exception e) { - BookingRequestEntity entityToRemove = entityContext.BookingRequests.Single(br => br.Id == requestId); + return BadRequest(e.Message); + } + } - if (entityToRemove.UserId != currentUserId) + [HttpDelete] + [ProducesResponseType(200, Type = typeof(string))] + [ProducesResponseType(400, Type = typeof(string))] + public async Task DeleteBookingRequests([Required] IEnumerable requests) + { + try + { + foreach (Guid request in requests) { - throw new Exception("You cannot remove a booking request for another user"); + await bookingRequestService.DeleteBookingRequest(request); } - entityContext.BookingRequests.Remove(entityToRemove); + return Ok("Booking requests deleted"); + } + catch (Exception e) + { + return BadRequest(e.Message); } - - entityContext.SaveChanges(); - return true; } [HttpGet] diff --git a/kabinizer-back-end/kabinizer-api/Dtos/BookingRequest/BookingRequestDto.cs b/kabinizer-back-end/kabinizer-api/Dtos/BookingRequest/BookingRequestDto.cs new file mode 100644 index 0000000..bc7854a --- /dev/null +++ b/kabinizer-back-end/kabinizer-api/Dtos/BookingRequest/BookingRequestDto.cs @@ -0,0 +1,28 @@ +using kabinizer_data.Entities; + +namespace kabinizer_api.Dtos.BookingRequest; + +public class BookingRequestDto +{ + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid PeriodId { get; set; } + public DateTime CreatedDate { get; set; } + public Guid CreatedBy { get; set; } + public DateTime? UpdatedDate { get; set; } + public Guid? UpdatedBy { get; set; } + + public static BookingRequestDto FromModel(BookingRequestEntity e) + { + return new BookingRequestDto + { + Id = e.Id, + UserId = e.UserId, + PeriodId = e.PeriodId, + CreatedDate = e.CreatedDate, + CreatedBy = e.CreatedBy, + UpdatedDate = e.UpdatedDate, + UpdatedBy = e.UpdatedBy + }; + } +} \ No newline at end of file diff --git a/kabinizer-back-end/kabinizer-api/Model/Period.cs b/kabinizer-back-end/kabinizer-api/Model/Period.cs index 971c0ed..b04669e 100644 --- a/kabinizer-back-end/kabinizer-api/Model/Period.cs +++ b/kabinizer-back-end/kabinizer-api/Model/Period.cs @@ -2,9 +2,9 @@ namespace kabinizer_api.Model; -public record Period(Guid Id, DateTime PeriodStart, DateTime PeriodEnd, string Title, Guid DrawId) +public record Period(Guid Id, DateTime PeriodStart, DateTime PeriodEnd, string? Title, Guid DrawId) { - public Period(DateTime periodStart, DateTime periodEnd, string title, Guid DrawId) + public Period(DateTime periodStart, DateTime periodEnd, string? title, Guid DrawId) : this(Guid.NewGuid(), periodStart, periodEnd, title, DrawId) { } diff --git a/kabinizer-back-end/kabinizer-api/Model/User.cs b/kabinizer-back-end/kabinizer-api/Model/User.cs index 56112ec..30cb39a 100644 --- a/kabinizer-back-end/kabinizer-api/Model/User.cs +++ b/kabinizer-back-end/kabinizer-api/Model/User.cs @@ -2,7 +2,7 @@ namespace kabinizer_api.Model; -public record User(Guid Id, string Name) +public record User(Guid Id, string? Name) { public static User FromEntity(UserEntity u) { diff --git a/kabinizer-back-end/kabinizer-api/Program.cs b/kabinizer-back-end/kabinizer-api/Program.cs index 9665c9d..610f268 100644 --- a/kabinizer-back-end/kabinizer-api/Program.cs +++ b/kabinizer-back-end/kabinizer-api/Program.cs @@ -1,16 +1,19 @@ using kabinizer_api.Services; +using kabinizer_api.Services.BookingRequest; using kabinizer_api.Services.Draw; using kabinizer_data; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Web; using Microsoft.OpenApi.Models; +using System.Text.Json.Serialization; var builder = WebApplication.CreateBuilder(args); builder.Services.AddCors(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Add services to the container. builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) diff --git a/kabinizer-back-end/kabinizer-api/Services/BookingRequest/BookingRequestService.cs b/kabinizer-back-end/kabinizer-api/Services/BookingRequest/BookingRequestService.cs new file mode 100644 index 0000000..459f725 --- /dev/null +++ b/kabinizer-back-end/kabinizer-api/Services/BookingRequest/BookingRequestService.cs @@ -0,0 +1,119 @@ +using kabinizer_api.Dtos.BookingRequest; +using kabinizer_data; +using kabinizer_data.Entities; +using Microsoft.EntityFrameworkCore; + +namespace kabinizer_api.Services.BookingRequest; + +public class BookingRequestService(EntityContext entityContext, ITokenService tokenService) +{ + private async Task GetBookingRequestById(Guid bookingRequestId) + { + return await entityContext.BookingRequests + .Include(br => br.User) + .Include(br => br.Period) + .FirstOrDefaultAsync(b => b.Id == bookingRequestId && b.UserId == tokenService.GetUserId()) + ?? throw new InvalidOperationException("No matching booking request found."); + } + + public async Task GetBookingRequest(Guid bookingRequestId) + { + BookingRequestEntity? id = await GetBookingRequestById(bookingRequestId); + if (id == null) + { + throw new Exception("Booking request does not exist or does not belong to the current user"); + } + + return id; + } + + public async Task> GetBookingRequests() + { + var bookingRequestEntities = await entityContext.BookingRequests + .Include(br => br.User) + .Include(br => br.Period) + .Where(b => b.UserId == tokenService.GetUserId()) + .ToListAsync(); + if (bookingRequestEntities == null) + { + throw new Exception("No booking requests found for the current user"); + } + + return bookingRequestEntities; + } + + public async Task AddBookingRequest(CreateBookingRequestDto request) + { + var period = await entityContext.Periods + .Include(p => p.Draw) + .FirstOrDefaultAsync(p => p.Draw != null && p.Id == request.PeriodId && p.Draw.DeadlineEnd >= DateTime.Now); + + if (period == null) + { + throw new Exception("Period does not exist, is not part of a draw, or the draw has ended"); + } + + var user = await entityContext.Users.FirstOrDefaultAsync(u => u.Id == tokenService.GetUserId()); + if (user == null) + { + throw new Exception("User does not exist"); + } + + var bookingRequest = new BookingRequestEntity + { + PeriodId = period.Id, + UserId = user.Id, + CreatedDate = DateTime.Now, + CreatedBy = user.Id, + User = user, + Period = period + }; + entityContext.BookingRequests.Add(bookingRequest); + await entityContext.SaveChangesAsync(); + return bookingRequest; + } + + public async Task DeleteBookingRequest(Guid bookingRequestId) + { + var bookingRequest = await entityContext.BookingRequests.FirstOrDefaultAsync(b => b.Id == bookingRequestId); + if (bookingRequest == null) + { + throw new Exception( + $"Booking request with id {bookingRequestId} does not exist or does not belong to the current user"); + } + + entityContext.BookingRequests.Remove(bookingRequest); + await entityContext.SaveChangesAsync(); + } + + public async Task> GetBookingRequestsByUser(Guid userId) + { + var bookingRequests = await entityContext.BookingRequests + .Include(br => br.User) + .Include(br => br.Period) + .Where(b => b.UserId == userId) + .ToListAsync(); + if (bookingRequests == null) + { + throw new Exception("No booking requests found for the user"); + } + + return bookingRequests; + } + + public async Task> GetBookingRequestsByPeriod(Guid periodId) + { + var bookingRequests = await entityContext.BookingRequests + .Include(br => br.User) + .Include(br => br.Period) + .Where(b => b.PeriodId == periodId) + .ToListAsync(); + + if (bookingRequests == null) + { + throw new Exception("No booking requests found for the period"); + } + + return bookingRequests; + } +} \ No newline at end of file diff --git a/kabinizer-back-end/kabinizer-api/Services/Export/CsvService.cs b/kabinizer-back-end/kabinizer-api/Services/Export/CsvService.cs index e244b7f..ef7bfc1 100644 --- a/kabinizer-back-end/kabinizer-api/Services/Export/CsvService.cs +++ b/kabinizer-back-end/kabinizer-api/Services/Export/CsvService.cs @@ -8,13 +8,13 @@ namespace kabinizer_api.Services.Export; public static class CsvService { - public static byte[] ExportToCsv(IEnumerable requests) + public static byte[] ExportToCsv(IEnumerable requests) { using MemoryStream memoryStream = new(); using StreamWriter streamWriter = new(memoryStream); CsvConfiguration config = new(CultureInfo.CurrentCulture) { Delimiter = ";", Encoding = Encoding.UTF8 }; using CsvWriter csv = new(streamWriter, config); - + csv.WriteRecords(ConvertToCsvRecords(requests)); streamWriter.Flush(); return memoryStream.ToArray(); @@ -25,14 +25,14 @@ private static int ConvertDateToWeekNumber(DateTime date) return ISOWeek.GetWeekOfYear(date); } - private static IEnumerable ConvertToCsvRecords(IEnumerable bookingRequests) + private static IEnumerable ConvertToCsvRecords(IEnumerable bookingRequests) { return bookingRequests .GroupBy( req => req.User.Name, req => ConvertDateToWeekNumber(req.Period.PeriodStart), - (name, weeks) => new CsvRecord(name.ToString(), string.Join(", ", weeks))); + (name, weeks) => new CsvRecord(name?.ToString(), string.Join(", ", weeks))); } - private record CsvRecord(string Name, string Weeks); + private record CsvRecord(string? Name, string Weeks); } \ No newline at end of file diff --git a/kabinizer-back-end/kabinizer-data/Entities/BookingRequestEntity.cs b/kabinizer-back-end/kabinizer-data/Entities/BookingRequestEntity.cs index 26d171a..aba3c79 100644 --- a/kabinizer-back-end/kabinizer-data/Entities/BookingRequestEntity.cs +++ b/kabinizer-back-end/kabinizer-data/Entities/BookingRequestEntity.cs @@ -3,36 +3,32 @@ namespace kabinizer_data.Entities; [Table("BookingRequest")] -public class BookingRequestEntity +public class BookingRequestEntity( + Guid id, + Guid userId, + Guid periodId, + DateTime createdDate, + Guid createdBy, + DateTime? updatedDate, + Guid? updatedBy, + UserEntity user, + PeriodEntity period +) { - public Guid Id { get; set; } - public Guid UserId { get; set; } - public UserEntity User { get; set; } - public PeriodEntity Period { get; set; } - public Guid PeriodId { get; set; } - public DateTime CreatedDate { get; set; } - public Guid CreatedBy { get; set; } - public DateTime? UpdatedDate { get; set; } - public Guid? UpdatedBy { get; set; } - - public BookingRequestEntity(Guid id, Guid userId, Guid periodId, DateTime createdDate, - Guid createdBy, DateTime? updatedDate, Guid? updatedBy) + public BookingRequestEntity() : this(Guid.NewGuid(), Guid.Empty, Guid.Empty, DateTime.Now, Guid.Empty, null, null, + new UserEntity(), new PeriodEntity { Title = null, DrawId = default } + ) { - Id = id; - UserId = userId; - PeriodId = periodId; - CreatedDate = createdDate; - CreatedBy = createdBy; - UpdatedDate = updatedDate; - UpdatedBy = updatedBy; } - public BookingRequestEntity(Guid userId, Guid periodId) - { - Id = Guid.NewGuid(); - UserId = userId; - PeriodId = periodId; - CreatedDate = DateTime.Now; - CreatedBy = userId; // Is this even needed? Won't it always be the same as the user id? - } + + public Guid Id { get; set; } = id; + public Guid UserId { get; set; } = userId; + public UserEntity User { get; set; } = user; + public PeriodEntity Period { get; set; } = period; + public Guid PeriodId { get; set; } = periodId; + public DateTime CreatedDate { get; set; } = createdDate; + public Guid CreatedBy { get; set; } = createdBy; + public DateTime? UpdatedDate { get; set; } = updatedDate; + public Guid? UpdatedBy { get; set; } = updatedBy; } \ No newline at end of file diff --git a/kabinizer-back-end/kabinizer-data/Entities/PeriodEntity.cs b/kabinizer-back-end/kabinizer-data/Entities/PeriodEntity.cs index d0b2af7..2f93db7 100644 --- a/kabinizer-back-end/kabinizer-data/Entities/PeriodEntity.cs +++ b/kabinizer-back-end/kabinizer-data/Entities/PeriodEntity.cs @@ -8,7 +8,7 @@ public class PeriodEntity public Guid Id { get; set; } public DateTime PeriodStart { get; set; } public DateTime PeriodEnd { get; set; } - public required string Title { get; set; } + public required string? Title { get; set; } public required Guid DrawId { get; set; } public DrawEntity? Draw { get; set; } diff --git a/kabinizer-back-end/kabinizer-data/Entities/UserEntity.cs b/kabinizer-back-end/kabinizer-data/Entities/UserEntity.cs index 5971595..5269265 100644 --- a/kabinizer-back-end/kabinizer-data/Entities/UserEntity.cs +++ b/kabinizer-back-end/kabinizer-data/Entities/UserEntity.cs @@ -6,5 +6,5 @@ namespace kabinizer_data.Entities; public class UserEntity { public Guid Id { get; set; } - public string Name { get; set; } + public string? Name { get; set; } } \ No newline at end of file diff --git a/kabinizer-front-end/api/core/ApiError.ts b/kabinizer-front-end/api/core/ApiError.ts index d6b8fcc..ebc1161 100644 --- a/kabinizer-front-end/api/core/ApiError.ts +++ b/kabinizer-front-end/api/core/ApiError.ts @@ -2,24 +2,28 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; +import type { ApiRequestOptions } from "./ApiRequestOptions"; +import type { ApiResult } from "./ApiResult"; export class ApiError extends Error { - public readonly url: string; - public readonly status: number; - public readonly statusText: string; - public readonly body: any; - public readonly request: ApiRequestOptions; + public readonly url: string; + public readonly status: number; + public readonly statusText: string; + public readonly body: any; + public readonly request: ApiRequestOptions; - constructor(request: ApiRequestOptions, response: ApiResult, message: string) { - super(message); + constructor( + request: ApiRequestOptions, + response: ApiResult, + message: string, + ) { + super(message); - this.name = 'ApiError'; - this.url = response.url; - this.status = response.status; - this.statusText = response.statusText; - this.body = response.body; - this.request = request; - } + this.name = "ApiError"; + this.url = response.url; + this.status = response.status; + this.statusText = response.statusText; + this.body = response.body; + this.request = request; + } } diff --git a/kabinizer-front-end/api/core/ApiRequestOptions.ts b/kabinizer-front-end/api/core/ApiRequestOptions.ts index c19adcc..ac9a2ca 100644 --- a/kabinizer-front-end/api/core/ApiRequestOptions.ts +++ b/kabinizer-front-end/api/core/ApiRequestOptions.ts @@ -3,15 +3,22 @@ /* tslint:disable */ /* eslint-disable */ export type ApiRequestOptions = { - readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; - readonly url: string; - readonly path?: Record; - readonly cookies?: Record; - readonly headers?: Record; - readonly query?: Record; - readonly formData?: Record; - readonly body?: any; - readonly mediaType?: string; - readonly responseHeader?: string; - readonly errors?: Record; + readonly method: + | "GET" + | "PUT" + | "POST" + | "DELETE" + | "OPTIONS" + | "HEAD" + | "PATCH"; + readonly url: string; + readonly path?: Record; + readonly cookies?: Record; + readonly headers?: Record; + readonly query?: Record; + readonly formData?: Record; + readonly body?: any; + readonly mediaType?: string; + readonly responseHeader?: string; + readonly errors?: Record; }; diff --git a/kabinizer-front-end/api/core/ApiResult.ts b/kabinizer-front-end/api/core/ApiResult.ts index ad8fef2..63ed6c4 100644 --- a/kabinizer-front-end/api/core/ApiResult.ts +++ b/kabinizer-front-end/api/core/ApiResult.ts @@ -3,9 +3,9 @@ /* tslint:disable */ /* eslint-disable */ export type ApiResult = { - readonly url: string; - readonly ok: boolean; - readonly status: number; - readonly statusText: string; - readonly body: any; + readonly url: string; + readonly ok: boolean; + readonly status: number; + readonly statusText: string; + readonly body: any; }; diff --git a/kabinizer-front-end/api/core/CancelablePromise.ts b/kabinizer-front-end/api/core/CancelablePromise.ts index 55fef85..6aefa9e 100644 --- a/kabinizer-front-end/api/core/CancelablePromise.ts +++ b/kabinizer-front-end/api/core/CancelablePromise.ts @@ -3,129 +3,128 @@ /* tslint:disable */ /* eslint-disable */ export class CancelError extends Error { - - constructor(message: string) { - super(message); - this.name = 'CancelError'; - } - - public get isCancelled(): boolean { - return true; - } + constructor(message: string) { + super(message); + this.name = "CancelError"; + } + + public get isCancelled(): boolean { + return true; + } } export interface OnCancel { - readonly isResolved: boolean; - readonly isRejected: boolean; - readonly isCancelled: boolean; + readonly isResolved: boolean; + readonly isRejected: boolean; + readonly isCancelled: boolean; - (cancelHandler: () => void): void; + (cancelHandler: () => void): void; } export class CancelablePromise implements Promise { - #isResolved: boolean; - #isRejected: boolean; - #isCancelled: boolean; - readonly #cancelHandlers: (() => void)[]; - readonly #promise: Promise; - #resolve?: (value: T | PromiseLike) => void; - #reject?: (reason?: any) => void; - - constructor( - executor: ( - resolve: (value: T | PromiseLike) => void, - reject: (reason?: any) => void, - onCancel: OnCancel - ) => void - ) { - this.#isResolved = false; - this.#isRejected = false; - this.#isCancelled = false; - this.#cancelHandlers = []; - this.#promise = new Promise((resolve, reject) => { - this.#resolve = resolve; - this.#reject = reject; - - const onResolve = (value: T | PromiseLike): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isResolved = true; - this.#resolve?.(value); - }; - - const onReject = (reason?: any): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isRejected = true; - this.#reject?.(reason); - }; - - const onCancel = (cancelHandler: () => void): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#cancelHandlers.push(cancelHandler); - }; - - Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this.#isResolved, - }); - - Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this.#isRejected, - }); - - Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this.#isCancelled, - }); - - return executor(onResolve, onReject, onCancel as OnCancel); - }); - } - - get [Symbol.toStringTag]() { - return "Cancellable Promise"; - } - - public then( - onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onRejected?: ((reason: any) => TResult2 | PromiseLike) | null - ): Promise { - return this.#promise.then(onFulfilled, onRejected); - } - - public catch( - onRejected?: ((reason: any) => TResult | PromiseLike) | null - ): Promise { - return this.#promise.catch(onRejected); - } + #isResolved: boolean; + #isRejected: boolean; + #isCancelled: boolean; + readonly #cancelHandlers: (() => void)[]; + readonly #promise: Promise; + #resolve?: (value: T | PromiseLike) => void; + #reject?: (reason?: any) => void; + + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason?: any) => void, + onCancel: OnCancel, + ) => void, + ) { + this.#isResolved = false; + this.#isRejected = false; + this.#isCancelled = false; + this.#cancelHandlers = []; + this.#promise = new Promise((resolve, reject) => { + this.#resolve = resolve; + this.#reject = reject; + + const onResolve = (value: T | PromiseLike): void => { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { + return; + } + this.#isResolved = true; + this.#resolve?.(value); + }; - public finally(onFinally?: (() => void) | null): Promise { - return this.#promise.finally(onFinally); - } + const onReject = (reason?: any): void => { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { + return; + } + this.#isRejected = true; + this.#reject?.(reason); + }; - public cancel(): void { + const onCancel = (cancelHandler: () => void): void => { if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; + return; } - this.#isCancelled = true; - if (this.#cancelHandlers.length) { - try { - for (const cancelHandler of this.#cancelHandlers) { - cancelHandler(); - } - } catch (error) { - console.warn('Cancellation threw an error', error); - return; - } + this.#cancelHandlers.push(cancelHandler); + }; + + Object.defineProperty(onCancel, "isResolved", { + get: (): boolean => this.#isResolved, + }); + + Object.defineProperty(onCancel, "isRejected", { + get: (): boolean => this.#isRejected, + }); + + Object.defineProperty(onCancel, "isCancelled", { + get: (): boolean => this.#isCancelled, + }); + + return executor(onResolve, onReject, onCancel as OnCancel); + }); + } + + get [Symbol.toStringTag]() { + return "Cancellable Promise"; + } + + public then( + onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onRejected?: ((reason: any) => TResult2 | PromiseLike) | null, + ): Promise { + return this.#promise.then(onFulfilled, onRejected); + } + + public catch( + onRejected?: ((reason: any) => TResult | PromiseLike) | null, + ): Promise { + return this.#promise.catch(onRejected); + } + + public finally(onFinally?: (() => void) | null): Promise { + return this.#promise.finally(onFinally); + } + + public cancel(): void { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { + return; + } + this.#isCancelled = true; + if (this.#cancelHandlers.length) { + try { + for (const cancelHandler of this.#cancelHandlers) { + cancelHandler(); } - this.#cancelHandlers.length = 0; - this.#reject?.(new CancelError('Request aborted')); + } catch (error) { + console.warn("Cancellation threw an error", error); + return; + } } + this.#cancelHandlers.length = 0; + this.#reject?.(new CancelError("Request aborted")); + } - public get isCancelled(): boolean { - return this.#isCancelled; - } + public get isCancelled(): boolean { + return this.#isCancelled; + } } diff --git a/kabinizer-front-end/api/core/OpenAPI.ts b/kabinizer-front-end/api/core/OpenAPI.ts index 0fdd044..dec19a3 100644 --- a/kabinizer-front-end/api/core/OpenAPI.ts +++ b/kabinizer-front-end/api/core/OpenAPI.ts @@ -2,31 +2,31 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { ApiRequestOptions } from "./ApiRequestOptions"; type Resolver = (options: ApiRequestOptions) => Promise; type Headers = Record; export type OpenAPIConfig = { - BASE: string; - VERSION: string; - WITH_CREDENTIALS: boolean; - CREDENTIALS: 'include' | 'omit' | 'same-origin'; - TOKEN?: string | Resolver | undefined; - USERNAME?: string | Resolver | undefined; - PASSWORD?: string | Resolver | undefined; - HEADERS?: Headers | Resolver | undefined; - ENCODE_PATH?: ((path: string) => string) | undefined; + BASE: string; + VERSION: string; + WITH_CREDENTIALS: boolean; + CREDENTIALS: "include" | "omit" | "same-origin"; + TOKEN?: string | Resolver | undefined; + USERNAME?: string | Resolver | undefined; + PASSWORD?: string | Resolver | undefined; + HEADERS?: Headers | Resolver | undefined; + ENCODE_PATH?: ((path: string) => string) | undefined; }; export const OpenAPI: OpenAPIConfig = { - BASE: '', - VERSION: '1.0', - WITH_CREDENTIALS: false, - CREDENTIALS: 'include', - TOKEN: undefined, - USERNAME: undefined, - PASSWORD: undefined, - HEADERS: undefined, - ENCODE_PATH: undefined, + BASE: "", + VERSION: "1.0", + WITH_CREDENTIALS: false, + CREDENTIALS: "include", + TOKEN: undefined, + USERNAME: undefined, + PASSWORD: undefined, + HEADERS: undefined, + ENCODE_PATH: undefined, }; diff --git a/kabinizer-front-end/api/core/request.ts b/kabinizer-front-end/api/core/request.ts index b018a07..72ed014 100644 --- a/kabinizer-front-end/api/core/request.ts +++ b/kabinizer-front-end/api/core/request.ts @@ -2,283 +2,310 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import { ApiError } from './ApiError'; -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; -import { CancelablePromise } from './CancelablePromise'; -import type { OnCancel } from './CancelablePromise'; -import type { OpenAPIConfig } from './OpenAPI'; - -export const isDefined = (value: T | null | undefined): value is Exclude => { - return value !== undefined && value !== null; +import { ApiError } from "./ApiError"; +import type { ApiRequestOptions } from "./ApiRequestOptions"; +import type { ApiResult } from "./ApiResult"; +import { CancelablePromise } from "./CancelablePromise"; +import type { OnCancel } from "./CancelablePromise"; +import type { OpenAPIConfig } from "./OpenAPI"; + +export const isDefined = ( + value: T | null | undefined, +): value is Exclude => { + return value !== undefined && value !== null; }; export const isString = (value: any): value is string => { - return typeof value === 'string'; + return typeof value === "string"; }; export const isStringWithValue = (value: any): value is string => { - return isString(value) && value !== ''; + return isString(value) && value !== ""; }; export const isBlob = (value: any): value is Blob => { - return ( - typeof value === 'object' && - typeof value.type === 'string' && - typeof value.stream === 'function' && - typeof value.arrayBuffer === 'function' && - typeof value.constructor === 'function' && - typeof value.constructor.name === 'string' && - /^(Blob|File)$/.test(value.constructor.name) && - /^(Blob|File)$/.test(value[Symbol.toStringTag]) - ); + return ( + typeof value === "object" && + typeof value.type === "string" && + typeof value.stream === "function" && + typeof value.arrayBuffer === "function" && + typeof value.constructor === "function" && + typeof value.constructor.name === "string" && + /^(Blob|File)$/.test(value.constructor.name) && + /^(Blob|File)$/.test(value[Symbol.toStringTag]) + ); }; export const isFormData = (value: any): value is FormData => { - return value instanceof FormData; + return value instanceof FormData; }; export const base64 = (str: string): string => { - try { - return btoa(str); - } catch (err) { - // @ts-ignore - return Buffer.from(str).toString('base64'); - } + try { + return btoa(str); + } catch (err) { + // @ts-ignore + return Buffer.from(str).toString("base64"); + } }; export const getQueryString = (params: Record): string => { - const qs: string[] = []; + const qs: string[] = []; - const append = (key: string, value: any) => { - qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); - }; + const append = (key: string, value: any) => { + qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); + }; - const process = (key: string, value: any) => { - if (isDefined(value)) { - if (Array.isArray(value)) { - value.forEach(v => { - process(key, v); - }); - } else if (typeof value === 'object') { - Object.entries(value).forEach(([k, v]) => { - process(`${key}[${k}]`, v); - }); - } else { - append(key, value); - } - } - }; + const process = (key: string, value: any) => { + if (isDefined(value)) { + if (Array.isArray(value)) { + value.forEach((v) => { + process(key, v); + }); + } else if (typeof value === "object") { + Object.entries(value).forEach(([k, v]) => { + process(`${key}[${k}]`, v); + }); + } else { + append(key, value); + } + } + }; - Object.entries(params).forEach(([key, value]) => { - process(key, value); - }); + Object.entries(params).forEach(([key, value]) => { + process(key, value); + }); - if (qs.length > 0) { - return `?${qs.join('&')}`; - } + if (qs.length > 0) { + return `?${qs.join("&")}`; + } - return ''; + return ""; }; const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { - const encoder = config.ENCODE_PATH || encodeURI; - - const path = options.url - .replace('{api-version}', config.VERSION) - .replace(/{(.*?)}/g, (substring: string, group: string) => { - if (options.path?.hasOwnProperty(group)) { - return encoder(String(options.path[group])); - } - return substring; - }); + const encoder = config.ENCODE_PATH || encodeURI; + + const path = options.url + .replace("{api-version}", config.VERSION) + .replace(/{(.*?)}/g, (substring: string, group: string) => { + if (options.path?.hasOwnProperty(group)) { + return encoder(String(options.path[group])); + } + return substring; + }); - const url = `${config.BASE}${path}`; - if (options.query) { - return `${url}${getQueryString(options.query)}`; - } - return url; + const url = `${config.BASE}${path}`; + if (options.query) { + return `${url}${getQueryString(options.query)}`; + } + return url; }; -export const getFormData = (options: ApiRequestOptions): FormData | undefined => { - if (options.formData) { - const formData = new FormData(); +export const getFormData = ( + options: ApiRequestOptions, +): FormData | undefined => { + if (options.formData) { + const formData = new FormData(); - const process = (key: string, value: any) => { - if (isString(value) || isBlob(value)) { - formData.append(key, value); - } else { - formData.append(key, JSON.stringify(value)); - } - }; + const process = (key: string, value: any) => { + if (isString(value) || isBlob(value)) { + formData.append(key, value); + } else { + formData.append(key, JSON.stringify(value)); + } + }; - Object.entries(options.formData) - .filter(([_, value]) => isDefined(value)) - .forEach(([key, value]) => { - if (Array.isArray(value)) { - value.forEach(v => process(key, v)); - } else { - process(key, value); - } - }); - - return formData; - } - return undefined; + Object.entries(options.formData) + .filter(([_, value]) => isDefined(value)) + .forEach(([key, value]) => { + if (Array.isArray(value)) { + value.forEach((v) => process(key, v)); + } else { + process(key, value); + } + }); + + return formData; + } + return undefined; }; type Resolver = (options: ApiRequestOptions) => Promise; -export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { - if (typeof resolver === 'function') { - return (resolver as Resolver)(options); - } - return resolver; +export const resolve = async ( + options: ApiRequestOptions, + resolver?: T | Resolver, +): Promise => { + if (typeof resolver === "function") { + return (resolver as Resolver)(options); + } + return resolver; }; -export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => { - const token = await resolve(options, config.TOKEN); - const username = await resolve(options, config.USERNAME); - const password = await resolve(options, config.PASSWORD); - const additionalHeaders = await resolve(options, config.HEADERS); - - const headers = Object.entries({ - Accept: 'application/json', - ...additionalHeaders, - ...options.headers, - }) - .filter(([_, value]) => isDefined(value)) - .reduce((headers, [key, value]) => ({ - ...headers, - [key]: String(value), - }), {} as Record); - - if (isStringWithValue(token)) { - headers['Authorization'] = `Bearer ${token}`; - } +export const getHeaders = async ( + config: OpenAPIConfig, + options: ApiRequestOptions, +): Promise => { + const token = await resolve(options, config.TOKEN); + const username = await resolve(options, config.USERNAME); + const password = await resolve(options, config.PASSWORD); + const additionalHeaders = await resolve(options, config.HEADERS); + + const headers = Object.entries({ + Accept: "application/json", + ...additionalHeaders, + ...options.headers, + }) + .filter(([_, value]) => isDefined(value)) + .reduce( + (headers, [key, value]) => ({ + ...headers, + [key]: String(value), + }), + {} as Record, + ); - if (isStringWithValue(username) && isStringWithValue(password)) { - const credentials = base64(`${username}:${password}`); - headers['Authorization'] = `Basic ${credentials}`; + if (isStringWithValue(token)) { + headers["Authorization"] = `Bearer ${token}`; + } + + if (isStringWithValue(username) && isStringWithValue(password)) { + const credentials = base64(`${username}:${password}`); + headers["Authorization"] = `Basic ${credentials}`; + } + + if (options.body) { + if (options.mediaType) { + headers["Content-Type"] = options.mediaType; + } else if (isBlob(options.body)) { + headers["Content-Type"] = options.body.type || "application/octet-stream"; + } else if (isString(options.body)) { + headers["Content-Type"] = "text/plain"; + } else if (!isFormData(options.body)) { + headers["Content-Type"] = "application/json"; } + } - if (options.body) { - if (options.mediaType) { - headers['Content-Type'] = options.mediaType; - } else if (isBlob(options.body)) { - headers['Content-Type'] = options.body.type || 'application/octet-stream'; - } else if (isString(options.body)) { - headers['Content-Type'] = 'text/plain'; - } else if (!isFormData(options.body)) { - headers['Content-Type'] = 'application/json'; - } - } - - return new Headers(headers); + return new Headers(headers); }; export const getRequestBody = (options: ApiRequestOptions): any => { - if (options.body !== undefined) { - if (options.mediaType?.includes('/json')) { - return JSON.stringify(options.body) - } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) { - return options.body; - } else { - return JSON.stringify(options.body); - } + if (options.body !== undefined) { + if (options.mediaType?.includes("/json")) { + return JSON.stringify(options.body); + } else if ( + isString(options.body) || + isBlob(options.body) || + isFormData(options.body) + ) { + return options.body; + } else { + return JSON.stringify(options.body); } - return undefined; + } + return undefined; }; export const sendRequest = async ( - config: OpenAPIConfig, - options: ApiRequestOptions, - url: string, - body: any, - formData: FormData | undefined, - headers: Headers, - onCancel: OnCancel + config: OpenAPIConfig, + options: ApiRequestOptions, + url: string, + body: any, + formData: FormData | undefined, + headers: Headers, + onCancel: OnCancel, ): Promise => { - const controller = new AbortController(); + const controller = new AbortController(); - const request: RequestInit = { - headers, - body: body ?? formData, - method: options.method, - signal: controller.signal, - }; + const request: RequestInit = { + headers, + body: body ?? formData, + method: options.method, + signal: controller.signal, + }; - if (config.WITH_CREDENTIALS) { - request.credentials = config.CREDENTIALS; - } + if (config.WITH_CREDENTIALS) { + request.credentials = config.CREDENTIALS; + } - onCancel(() => controller.abort()); + onCancel(() => controller.abort()); - return await fetch(url, request); + return await fetch(url, request); }; -export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => { - if (responseHeader) { - const content = response.headers.get(responseHeader); - if (isString(content)) { - return content; - } +export const getResponseHeader = ( + response: Response, + responseHeader?: string, +): string | undefined => { + if (responseHeader) { + const content = response.headers.get(responseHeader); + if (isString(content)) { + return content; } - return undefined; + } + return undefined; }; export const getResponseBody = async (response: Response): Promise => { - if (response.status !== 204) { - try { - const contentType = response.headers.get('Content-Type'); - if (contentType) { - const jsonTypes = ['application/json', 'application/problem+json'] - const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type)); - if (isJSON) { - return await response.json(); - } else { - return await response.text(); - } - } - } catch (error) { - console.error(error); + if (response.status !== 204) { + try { + const contentType = response.headers.get("Content-Type"); + if (contentType) { + const jsonTypes = ["application/json", "application/problem+json"]; + const isJSON = jsonTypes.some((type) => + contentType.toLowerCase().startsWith(type), + ); + if (isJSON) { + return await response.json(); + } else { + return await response.text(); } + } + } catch (error) { + console.error(error); } - return undefined; + } + return undefined; }; -export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { - const errors: Record = { - 400: 'Bad Request', - 401: 'Unauthorized', - 403: 'Forbidden', - 404: 'Not Found', - 500: 'Internal Server Error', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - ...options.errors, - } - - const error = errors[result.status]; - if (error) { - throw new ApiError(options, result, error); - } - - if (!result.ok) { - const errorStatus = result.status ?? 'unknown'; - const errorStatusText = result.statusText ?? 'unknown'; - const errorBody = (() => { - try { - return JSON.stringify(result.body, null, 2); - } catch (e) { - return undefined; - } - })(); - - throw new ApiError(options, result, - `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` - ); - } +export const catchErrorCodes = ( + options: ApiRequestOptions, + result: ApiResult, +): void => { + const errors: Record = { + 400: "Bad Request", + 401: "Unauthorized", + 403: "Forbidden", + 404: "Not Found", + 500: "Internal Server Error", + 502: "Bad Gateway", + 503: "Service Unavailable", + ...options.errors, + }; + + const error = errors[result.status]; + if (error) { + throw new ApiError(options, result, error); + } + + if (!result.ok) { + const errorStatus = result.status ?? "unknown"; + const errorStatusText = result.statusText ?? "unknown"; + const errorBody = (() => { + try { + return JSON.stringify(result.body, null, 2); + } catch (e) { + return undefined; + } + })(); + + throw new ApiError( + options, + result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`, + ); + } }; /** @@ -288,33 +315,47 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): * @returns CancelablePromise * @throws ApiError */ -export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => { - return new CancelablePromise(async (resolve, reject, onCancel) => { - try { - const url = getUrl(config, options); - const formData = getFormData(options); - const body = getRequestBody(options); - const headers = await getHeaders(config, options); - - if (!onCancel.isCancelled) { - const response = await sendRequest(config, options, url, body, formData, headers, onCancel); - const responseBody = await getResponseBody(response); - const responseHeader = getResponseHeader(response, options.responseHeader); - - const result: ApiResult = { - url, - ok: response.ok, - status: response.status, - statusText: response.statusText, - body: responseHeader ?? responseBody, - }; - - catchErrorCodes(options, result); - - resolve(result.body); - } - } catch (error) { - reject(error); - } - }); +export const request = ( + config: OpenAPIConfig, + options: ApiRequestOptions, +): CancelablePromise => { + return new CancelablePromise(async (resolve, reject, onCancel) => { + try { + const url = getUrl(config, options); + const formData = getFormData(options); + const body = getRequestBody(options); + const headers = await getHeaders(config, options); + + if (!onCancel.isCancelled) { + const response = await sendRequest( + config, + options, + url, + body, + formData, + headers, + onCancel, + ); + const responseBody = await getResponseBody(response); + const responseHeader = getResponseHeader( + response, + options.responseHeader, + ); + + const result: ApiResult = { + url, + ok: response.ok, + status: response.status, + statusText: response.statusText, + body: responseHeader ?? responseBody, + }; + + catchErrorCodes(options, result); + + resolve(result.body); + } + } catch (error) { + reject(error); + } + }); }; diff --git a/kabinizer-front-end/api/index.ts b/kabinizer-front-end/api/index.ts index 488602d..2fd69a1 100644 --- a/kabinizer-front-end/api/index.ts +++ b/kabinizer-front-end/api/index.ts @@ -2,19 +2,18 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export { ApiError } from './core/ApiError'; -export { CancelablePromise, CancelError } from './core/CancelablePromise'; -export { OpenAPI } from './core/OpenAPI'; -export type { OpenAPIConfig } from './core/OpenAPI'; +export { ApiError } from "./core/ApiError"; +export { CancelablePromise, CancelError } from "./core/CancelablePromise"; +export { OpenAPI } from "./core/OpenAPI"; +export type { OpenAPIConfig } from "./core/OpenAPI"; -export type { BookingRequest } from './models/BookingRequest'; -export type { CreateBookingRequestDto } from './models/CreateBookingRequestDto'; -export type { CreateDrawDto } from './models/CreateDrawDto'; -export type { Draw } from './models/Draw'; -export type { DrawPeriod } from './models/DrawPeriod'; -export type { Period } from './models/Period'; -export type { User } from './models/User'; +export type { BookingRequestDto } from "./models/BookingRequestDto"; +export type { CreateBookingRequestDto } from "./models/CreateBookingRequestDto"; +export type { CreateDrawDto } from "./models/CreateDrawDto"; +export type { Draw } from "./models/Draw"; +export type { DrawPeriod } from "./models/DrawPeriod"; +export type { Period } from "./models/Period"; -export { BookingRequestService } from './services/BookingRequestService'; -export { DrawService } from './services/DrawService'; -export { PeriodService } from './services/PeriodService'; +export { BookingRequestService } from "./services/BookingRequestService"; +export { DrawService } from "./services/DrawService"; +export { PeriodService } from "./services/PeriodService"; diff --git a/kabinizer-front-end/api/models/BookingRequest.ts b/kabinizer-front-end/api/models/BookingRequest.ts deleted file mode 100644 index 0b18f47..0000000 --- a/kabinizer-front-end/api/models/BookingRequest.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -import type { Period } from './Period'; -import type { User } from './User'; - -export type BookingRequest = { - bookingRequestId?: string; - period?: Period; - user?: User; -}; - diff --git a/kabinizer-front-end/api/models/BookingRequestDto.ts b/kabinizer-front-end/api/models/BookingRequestDto.ts new file mode 100644 index 0000000..c47cca3 --- /dev/null +++ b/kabinizer-front-end/api/models/BookingRequestDto.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type BookingRequestDto = { + id?: string; + userId?: string; + periodId?: string; + createdDate?: string; + createdBy?: string; + updatedDate?: string | null; + updatedBy?: string | null; +}; diff --git a/kabinizer-front-end/api/models/CreateBookingRequestDto.ts b/kabinizer-front-end/api/models/CreateBookingRequestDto.ts index 8efb255..73bfd30 100644 --- a/kabinizer-front-end/api/models/CreateBookingRequestDto.ts +++ b/kabinizer-front-end/api/models/CreateBookingRequestDto.ts @@ -4,6 +4,5 @@ /* eslint-disable */ export type CreateBookingRequestDto = { - periodId?: string; + periodId?: string; }; - diff --git a/kabinizer-front-end/api/models/CreateDrawDto.ts b/kabinizer-front-end/api/models/CreateDrawDto.ts index f169d92..61e53a4 100644 --- a/kabinizer-front-end/api/models/CreateDrawDto.ts +++ b/kabinizer-front-end/api/models/CreateDrawDto.ts @@ -3,13 +3,12 @@ /* tslint:disable */ /* eslint-disable */ -import type { DrawPeriod } from './DrawPeriod'; +import type { DrawPeriod } from "./DrawPeriod"; export type CreateDrawDto = { - deadlineStart?: string; - deadlineEnd?: string; - title?: string | null; - drawPeriods?: Array | null; - isSpecial?: boolean; + deadlineStart?: string; + deadlineEnd?: string; + title?: string | null; + drawPeriods?: Array | null; + isSpecial?: boolean; }; - diff --git a/kabinizer-front-end/api/models/Draw.ts b/kabinizer-front-end/api/models/Draw.ts index f00c417..5add249 100644 --- a/kabinizer-front-end/api/models/Draw.ts +++ b/kabinizer-front-end/api/models/Draw.ts @@ -3,14 +3,13 @@ /* tslint:disable */ /* eslint-disable */ -import type { Period } from './Period'; +import type { Period } from "./Period"; export type Draw = { - id?: string; - start?: string; - end?: string; - title?: string | null; - periods?: Array | null; - isSpecial?: boolean; + id?: string; + start?: string; + end?: string; + title?: string | null; + periods?: Array | null; + isSpecial?: boolean; }; - diff --git a/kabinizer-front-end/api/models/DrawPeriod.ts b/kabinizer-front-end/api/models/DrawPeriod.ts index 80a9566..729e794 100644 --- a/kabinizer-front-end/api/models/DrawPeriod.ts +++ b/kabinizer-front-end/api/models/DrawPeriod.ts @@ -4,8 +4,7 @@ /* eslint-disable */ export type DrawPeriod = { - start?: string; - end?: string; - title?: string | null; + start?: string; + end?: string; + title?: string | null; }; - diff --git a/kabinizer-front-end/api/models/Period.ts b/kabinizer-front-end/api/models/Period.ts index 72589c3..5fe4978 100644 --- a/kabinizer-front-end/api/models/Period.ts +++ b/kabinizer-front-end/api/models/Period.ts @@ -4,10 +4,9 @@ /* eslint-disable */ export type Period = { - id?: string; - periodStart?: string; - periodEnd?: string; - title?: string | null; - drawId?: string; + id?: string; + periodStart?: string; + periodEnd?: string; + title?: string | null; + drawId?: string; }; - diff --git a/kabinizer-front-end/api/models/User.ts b/kabinizer-front-end/api/models/User.ts deleted file mode 100644 index 427d455..0000000 --- a/kabinizer-front-end/api/models/User.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -export type User = { - id?: string; - name?: string | null; -}; - diff --git a/kabinizer-front-end/api/services/BookingRequestService.ts b/kabinizer-front-end/api/services/BookingRequestService.ts index f28625d..f7ce1ff 100644 --- a/kabinizer-front-end/api/services/BookingRequestService.ts +++ b/kabinizer-front-end/api/services/BookingRequestService.ts @@ -2,67 +2,136 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { BookingRequest } from '../models/BookingRequest'; -import type { CreateBookingRequestDto } from '../models/CreateBookingRequestDto'; +import type { BookingRequestDto } from "../models/BookingRequestDto"; +import type { CreateBookingRequestDto } from "../models/CreateBookingRequestDto"; -import type { CancelablePromise } from '../core/CancelablePromise'; -import { OpenAPI } from '../core/OpenAPI'; -import { request as __request } from '../core/request'; +import type { CancelablePromise } from "../core/CancelablePromise"; +import { OpenAPI } from "../core/OpenAPI"; +import { request as __request } from "../core/request"; export class BookingRequestService { + /** + * @param id + * @returns BookingRequestDto Success + * @throws ApiError + */ + public static getApiBookingRequest( + id: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: "GET", + url: "/api/BookingRequest/{id}", + path: { + id: id, + }, + errors: { + 404: `Not Found`, + }, + }); + } - /** - * @returns BookingRequest Success - * @throws ApiError - */ - public static getApiBookingRequest(): CancelablePromise> { - return __request(OpenAPI, { - method: 'GET', - url: '/api/BookingRequest', - }); - } + /** + * @returns BookingRequestDto Success + * @throws ApiError + */ + public static getApiBookingRequest1(): CancelablePromise< + Array + > { + return __request(OpenAPI, { + method: "GET", + url: "/api/BookingRequest", + errors: { + 404: `Not Found`, + }, + }); + } - /** - * @param requestBody - * @returns any Success - * @throws ApiError - */ - public static postApiBookingRequest( - requestBody: Array, - ): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/api/BookingRequest', - body: requestBody, - mediaType: 'application/json', - }); - } + /** + * @param requestBody + * @returns BookingRequestDto Success + * @throws ApiError + */ + public static postApiBookingRequest( + requestBody: Array, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/api/BookingRequest", + body: requestBody, + mediaType: "application/json", + errors: { + 400: `Bad Request`, + }, + }); + } - /** - * @param requestBody - * @returns boolean Success - * @throws ApiError - */ - public static deleteApiBookingRequest( - requestBody: Array, - ): CancelablePromise { - return __request(OpenAPI, { - method: 'DELETE', - url: '/api/BookingRequest', - body: requestBody, - mediaType: 'application/json', - }); - } + /** + * @param requestBody + * @returns string Success + * @throws ApiError + */ + public static deleteApiBookingRequest( + requestBody: Array, + ): CancelablePromise { + return __request(OpenAPI, { + method: "DELETE", + url: "/api/BookingRequest", + body: requestBody, + mediaType: "application/json", + errors: { + 400: `Bad Request`, + }, + }); + } - /** - * @returns any Success - * @throws ApiError - */ - public static getApiBookingRequestExport(): CancelablePromise { - return __request(OpenAPI, { - method: 'GET', - url: '/api/BookingRequest/export', - }); - } + /** + * @param userId + * @returns BookingRequestDto Success + * @throws ApiError + */ + public static getApiBookingRequestUser( + userId: string, + ): CancelablePromise> { + return __request(OpenAPI, { + method: "GET", + url: "/api/BookingRequest/user/{userId}", + path: { + userId: userId, + }, + errors: { + 404: `Not Found`, + }, + }); + } + /** + * @param periodId + * @returns BookingRequestDto Success + * @throws ApiError + */ + public static getApiBookingRequestPeriod( + periodId: string, + ): CancelablePromise> { + return __request(OpenAPI, { + method: "GET", + url: "/api/BookingRequest/period/{periodId}", + path: { + periodId: periodId, + }, + errors: { + 404: `Not Found`, + }, + }); + } + + /** + * @returns any Success + * @throws ApiError + */ + public static getApiBookingRequestExport(): CancelablePromise { + return __request(OpenAPI, { + method: "GET", + url: "/api/BookingRequest/export", + }); + } } diff --git a/kabinizer-front-end/api/services/DrawService.ts b/kabinizer-front-end/api/services/DrawService.ts index 6552f81..0a13d69 100644 --- a/kabinizer-front-end/api/services/DrawService.ts +++ b/kabinizer-front-end/api/services/DrawService.ts @@ -2,40 +2,38 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { CreateDrawDto } from '../models/CreateDrawDto'; -import type { Draw } from '../models/Draw'; +import type { CreateDrawDto } from "../models/CreateDrawDto"; +import type { Draw } from "../models/Draw"; -import type { CancelablePromise } from '../core/CancelablePromise'; -import { OpenAPI } from '../core/OpenAPI'; -import { request as __request } from '../core/request'; +import type { CancelablePromise } from "../core/CancelablePromise"; +import { OpenAPI } from "../core/OpenAPI"; +import { request as __request } from "../core/request"; export class DrawService { + /** + * @returns Draw Success + * @throws ApiError + */ + public static getApiDraw(): CancelablePromise> { + return __request(OpenAPI, { + method: "GET", + url: "/api/Draw", + }); + } - /** - * @returns Draw Success - * @throws ApiError - */ - public static getApiDraw(): CancelablePromise> { - return __request(OpenAPI, { - method: 'GET', - url: '/api/Draw', - }); - } - - /** - * @param requestBody - * @returns any Success - * @throws ApiError - */ - public static postApiDraw( - requestBody: CreateDrawDto, - ): CancelablePromise { - return __request(OpenAPI, { - method: 'POST', - url: '/api/Draw', - body: requestBody, - mediaType: 'application/json', - }); - } - + /** + * @param requestBody + * @returns any Success + * @throws ApiError + */ + public static postApiDraw( + requestBody: CreateDrawDto, + ): CancelablePromise { + return __request(OpenAPI, { + method: "POST", + url: "/api/Draw", + body: requestBody, + mediaType: "application/json", + }); + } } diff --git a/kabinizer-front-end/api/services/PeriodService.ts b/kabinizer-front-end/api/services/PeriodService.ts index 4601463..65f9352 100644 --- a/kabinizer-front-end/api/services/PeriodService.ts +++ b/kabinizer-front-end/api/services/PeriodService.ts @@ -2,23 +2,21 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Period } from '../models/Period'; +import type { Period } from "../models/Period"; -import type { CancelablePromise } from '../core/CancelablePromise'; -import { OpenAPI } from '../core/OpenAPI'; -import { request as __request } from '../core/request'; +import type { CancelablePromise } from "../core/CancelablePromise"; +import { OpenAPI } from "../core/OpenAPI"; +import { request as __request } from "../core/request"; export class PeriodService { - - /** - * @returns Period Success - * @throws ApiError - */ - public static getApiPeriod(): CancelablePromise> { - return __request(OpenAPI, { - method: 'GET', - url: '/api/Period', - }); - } - + /** + * @returns Period Success + * @throws ApiError + */ + public static getApiPeriod(): CancelablePromise> { + return __request(OpenAPI, { + method: "GET", + url: "/api/Period", + }); + } } diff --git a/kabinizer-front-end/package.json b/kabinizer-front-end/package.json index ca6b8ca..54eed9d 100644 --- a/kabinizer-front-end/package.json +++ b/kabinizer-front-end/package.json @@ -9,7 +9,8 @@ "test": "vitest", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", - "generate": "openapi -i https://app-kabinizer-dev.azurewebsites.net/swagger/v1/swagger.json -o ./api --useUnionTypes & prettier -w ./api" + "generate": "openapi -i https://app-kabinizer-dev.azurewebsites.net/swagger/v1/swagger.json -o ./api --useUnionTypes & prettier -w ./api", + "generate:local": "openapi -i http://localhost:5111/swagger/v1/swagger.json -o ./api --useUnionTypes & prettier -w ./api" }, "dependencies": { "@radix-ui/react-avatar": "^1.0.4", diff --git a/kabinizer-front-end/src/components/MonthVertical.tsx b/kabinizer-front-end/src/components/MonthVertical.tsx deleted file mode 100644 index 7c5ea08..0000000 --- a/kabinizer-front-end/src/components/MonthVertical.tsx +++ /dev/null @@ -1,20 +0,0 @@ -const MonthColumn = ({ month, color }: { month: string; color: string }) => { - return ( -
- {month} -
- ); -}; - -export default MonthColumn; diff --git a/kabinizer-front-end/src/components/OptionButton.tsx b/kabinizer-front-end/src/components/OptionButton.tsx index 77eedf4..fd8db6c 100644 --- a/kabinizer-front-end/src/components/OptionButton.tsx +++ b/kabinizer-front-end/src/components/OptionButton.tsx @@ -1,5 +1,5 @@ -import { ColorType } from "../types"; -import { NumberOfDays } from "../utils"; +import { ColorType } from "@/types"; +import { NumberOfDays } from "@/utils"; const OptionButton = ({ colors, @@ -8,6 +8,7 @@ const OptionButton = ({ isSpecial, selected, onClick, + isLoading = false, }: { colors: ColorType; from: Date; @@ -15,6 +16,7 @@ const OptionButton = ({ isSpecial: boolean; selected: boolean; onClick: () => void; + isLoading?: boolean; }) => { const days = NumberOfDays(from, to) + 1; @@ -27,36 +29,45 @@ const OptionButton = ({ return ( ); diff --git a/kabinizer-front-end/src/components/VerticalText.tsx b/kabinizer-front-end/src/components/VerticalText.tsx new file mode 100644 index 0000000..536a468 --- /dev/null +++ b/kabinizer-front-end/src/components/VerticalText.tsx @@ -0,0 +1,16 @@ +const MonthColumn = ({ text }: { text: string }) => { + return ( +
+ {text} +
+ ); +}; + +export default MonthColumn; diff --git a/kabinizer-front-end/src/pages/selectPeriods/Calendar/Calendar.tsx b/kabinizer-front-end/src/pages/selectPeriods/Calendar/Calendar.tsx deleted file mode 100644 index 1c22f2f..0000000 --- a/kabinizer-front-end/src/pages/selectPeriods/Calendar/Calendar.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { CreateBookingRequestDto, Draw } from "../../../../api"; -import { CompareDates } from "../../../utils"; -import { GetWeeklyPeriods, getMonthsMap } from "@/utils/calendar"; -import Months from "./Months"; - -const Calendar = ({ - draws, - selected, - setSelected, -}: { - draws: Draw[]; - selected: CreateBookingRequestDto[]; - setSelected: (selected: CreateBookingRequestDto[]) => void; -}) => { - const periods = draws - .map((value) => value.periods ?? []) - .flat() - .sort((a, b) => { - return CompareDates(a, b); - }); - - const onClick = (periodId: string) => { - const checked = selected.find((s) => s.periodId === periodId); - - if (checked) { - setSelected(selected.filter((s) => s.periodId !== periodId)); - } else { - const period = periods.find((p) => p.id === periodId); - if (period) { - setSelected([ - ...selected, - { - periodId: period.id ?? "", - }, - ]); - } - } - }; - - if (periods.length === 0) { - return
No periods
; - } - - const data = GetWeeklyPeriods(periods, draws); - const monthMap = getMonthsMap(data); - - console.log(monthMap); - - return ; -}; - -export default Calendar; diff --git a/kabinizer-front-end/src/pages/selectPeriods/Calendar/DrawPeriodsCalendar.tsx b/kabinizer-front-end/src/pages/selectPeriods/Calendar/DrawPeriodsCalendar.tsx new file mode 100644 index 0000000..f7c9d97 --- /dev/null +++ b/kabinizer-front-end/src/pages/selectPeriods/Calendar/DrawPeriodsCalendar.tsx @@ -0,0 +1,122 @@ +import { + BookingRequestService, + CreateBookingRequestDto, + DrawService, +} from "../../../../api"; +import { CompareDates } from "@/utils"; +import { useMutation, useQuery, useQueryClient } from "react-query"; +import useUser from "@/hooks/useUser.tsx"; +import { getMonthsMap, GetWeeklyPeriods } from "@/utils/calendar.ts"; +import { useState } from "react"; +import MonthOptionsComponent from "@/pages/selectPeriods/Calendar/MonthOptionsComponent.tsx"; +import WeekDayRow from "@/components/WeekDayRow.tsx"; + +const DrawPeriodsCalendar = () => { + const user = useUser(); + const queryClient = useQueryClient(); + + const { + data: draws, + isLoading: isLoadingDraws, + error: drawError, + } = useQuery("getApiDraw", DrawService.getApiDraw); + + const { + data: bookingRequests, + isLoading: isLoadingBookingRequests, + error: bookingError, + } = useQuery( + "myBookingRequests", + () => BookingRequestService.getApiBookingRequestUser(user.localAccountId), + { enabled: !!user.localAccountId }, + ); + + const [loadingStates, setLoadingStates] = useState>({ + "": false, + }); + + const addBooking = useMutation( + "postApiBooking", + (payload: CreateBookingRequestDto[]) => { + payload.forEach((p) => { + setLoadingStates((prev) => ({ ...prev, [p.periodId || ""]: true })); + }); + return BookingRequestService.postApiBookingRequest(payload); + }, + { + onSettled: (_data, _error, variables) => { + variables.forEach((bookingRequestDto) => { + setLoadingStates((prev) => ({ + ...prev, + [bookingRequestDto.periodId || ""]: false, + })); + }); + return queryClient.invalidateQueries("myBookingRequests"); + }, + }, + ); + + const removeBooking = useMutation( + "deleteApiBooking", + (payload: string[]) => { + payload.forEach((id) => { + setLoadingStates((prev) => ({ ...prev, [id]: true })); + }); + return BookingRequestService.deleteApiBookingRequest(payload); + }, + { + onSettled: (_data, _error, variables) => { + variables.forEach((id) => { + setLoadingStates((prev) => ({ ...prev, [id]: false })); + }); + return queryClient.invalidateQueries("myBookingRequests"); + }, + }, + ); + + if (isLoadingDraws || isLoadingBookingRequests) return
Loading...
; + + const periods = draws + ?.flatMap((value) => value.periods ?? []) + .sort(CompareDates); + + if (drawError || bookingError) { + return ( +
+

An error occurred while fetching data. Please try again later.

+
+ ); + } + + if (!periods?.length) return
No periods
; + + const monthMap = getMonthsMap(GetWeeklyPeriods(periods, draws || [])); + + const toggleBooking = (periodId: string) => { + const booking = bookingRequests?.find((b) => b.periodId === periodId); + booking?.id + ? removeBooking.mutate([booking.id]) + : addBooking.mutate([{ periodId }]); + }; + + return ( +
+ + +
+ {Object.entries(monthMap).map(([month, weeks]) => ( + + ))} +
+
+ ); +}; + +export default DrawPeriodsCalendar; diff --git a/kabinizer-front-end/src/pages/selectPeriods/Calendar/MonthOptionsComponent.tsx b/kabinizer-front-end/src/pages/selectPeriods/Calendar/MonthOptionsComponent.tsx new file mode 100644 index 0000000..a432d9e --- /dev/null +++ b/kabinizer-front-end/src/pages/selectPeriods/Calendar/MonthOptionsComponent.tsx @@ -0,0 +1,46 @@ +import { Option } from "@/types"; +import { CreateBookingRequestDto } from "../../../../api"; +import React from "react"; +import { MONTHS } from "@/options"; +import WeekOptionsComponent from "@/pages/selectPeriods/Calendar/WeekOptionsComponent.tsx"; +import VerticalText from "@/components/VerticalText.tsx"; + +type MonthOptionsComponentProps = { + month: string; + weeks: Record; + bookingRequests: CreateBookingRequestDto[] | undefined; + toggleBooking: (periodId: string) => void; + loadingStates: Record; +}; + +const MonthOptionsComponent: React.FC = ({ + month, + weeks, + bookingRequests, + toggleBooking, + loadingStates, +}) => ( +
+ +
+ {Object.entries(weeks).map(([week, options]) => ( + + ))} +
+
+); +export default MonthOptionsComponent; diff --git a/kabinizer-front-end/src/pages/selectPeriods/Calendar/Months.tsx b/kabinizer-front-end/src/pages/selectPeriods/Calendar/Months.tsx deleted file mode 100644 index 292f4b9..0000000 --- a/kabinizer-front-end/src/pages/selectPeriods/Calendar/Months.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import MonthColumn from "../../../components/MonthVertical"; -import { COLORS, MONTHS } from "@/options"; -import { MonthMapType } from "@/types"; -import Weeks from "./Weeks"; -import { OptionsProps } from "./Options"; - -type MonthsProps = { - months: MonthMapType; -} & Omit; - -const Months = ({ months, selected, onClick }: MonthsProps) => { - return ( -
- {Object.entries(months).map(([month, weeks]) => ( - - - - ))} -
- ); -}; - -type MonthProps = { - children: React.ReactNode; - month: number; -}; - -const Month = ({ children, month }: MonthProps) => { - return ( -
- -
- {children} -
-
- ); -}; - -export default Months; diff --git a/kabinizer-front-end/src/pages/selectPeriods/Calendar/WeekOptionsComponent.tsx b/kabinizer-front-end/src/pages/selectPeriods/Calendar/WeekOptionsComponent.tsx new file mode 100644 index 0000000..e772264 --- /dev/null +++ b/kabinizer-front-end/src/pages/selectPeriods/Calendar/WeekOptionsComponent.tsx @@ -0,0 +1,48 @@ +import { Option } from "@/types"; +import { CreateBookingRequestDto } from "../../../../api"; +import React from "react"; +import WeekNumber from "@/components/WeekNumber.tsx"; +import OptionButton from "@/components/OptionButton.tsx"; +import { COLORS } from "@/options"; + +type WeekOptionsComponentProps = { + week: string; + month: string; + options: Option[]; + bookingRequests: CreateBookingRequestDto[] | undefined; + toggleBooking: (periodId: string) => void; + loadingStates: Record; +}; + +const WeekOptionsComponent: React.FC = ({ + week, + month, + options, + bookingRequests, + toggleBooking, + loadingStates, +}) => ( +
+ +
+ {options.map((option) => ( +
+ s.periodId === option.id)} + onClick={() => toggleBooking(option.id ?? "")} + isLoading={loadingStates[option.id || ""]} + /> +
+ ))} +
+
+); +export default WeekOptionsComponent; diff --git a/kabinizer-front-end/src/pages/selectPeriods/Calendar/Weeks.tsx b/kabinizer-front-end/src/pages/selectPeriods/Calendar/Weeks.tsx deleted file mode 100644 index 6c4964f..0000000 --- a/kabinizer-front-end/src/pages/selectPeriods/Calendar/Weeks.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import WeekNumber from "../../../components/WeekNumber"; -import { WeekMapType } from "@/types"; -import Options, { OptionsProps } from "./Options"; - -type WeeksProps = { - weeks: WeekMapType; -} & Omit; - -const Weeks = ({ weeks, month, selected, onClick }: WeeksProps) => { - return ( -
- {Object.entries(weeks).map(([week, options]) => ( - - - - ))} -
- ); -}; - -type WeekProps = { - children: React.ReactNode; - week: number; -}; - -const Week = ({ children, week }: WeekProps) => { - return ( -
- - {children} -
- ); -}; - -export default Weeks; diff --git a/kabinizer-front-end/src/pages/selectPeriods/CurrentDrawDeadlines.tsx b/kabinizer-front-end/src/pages/selectPeriods/CurrentDrawDeadlines.tsx new file mode 100644 index 0000000..ad89183 --- /dev/null +++ b/kabinizer-front-end/src/pages/selectPeriods/CurrentDrawDeadlines.tsx @@ -0,0 +1,43 @@ +import { DrawService } from "../../../api"; +import { FormatDate } from "@/utils"; +import { useQuery } from "react-query"; + +const CurrentDrawDeadlines = () => { + const { data: draws, isLoading } = useQuery(["getApiDraw"], () => + DrawService.getApiDraw(), + ); + if (isLoading) return

Loading...

; + + return ( +
+
+
+

+ Frist +

+
+
+ {!draws && ( +
+

+ Det er for tiden ingen aktive trekninger +

+
+ )} + {draws?.map((draw) => ( +
+

+ {draw.title} +

+

+ {draw.end ? FormatDate(new Date(draw.end)) : "–"} +

+
+ ))} +
+
+
+ ); +}; + +export default CurrentDrawDeadlines; diff --git a/kabinizer-front-end/src/pages/selectPeriods/Deadline.tsx b/kabinizer-front-end/src/pages/selectPeriods/Deadline.tsx deleted file mode 100644 index 8f8a1cd..0000000 --- a/kabinizer-front-end/src/pages/selectPeriods/Deadline.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Draw } from "../../../api"; -import { FormatDate } from "../../utils"; - -type DeadlineProps = { - draws: Draw[]; -}; - -const Deadline = ({ draws }: DeadlineProps) => { - return ( -
-
-

Deadline

-
-
- {draws.map((draw) => ( -
-

- {draw.title} -

-

- {draw.end ? FormatDate(new Date(draw.end)) : "–"} -

-
- ))} -
-
- ); -}; - -export default Deadline; diff --git a/kabinizer-front-end/src/pages/selectPeriods/index.tsx b/kabinizer-front-end/src/pages/selectPeriods/index.tsx index 46255ac..cbf10f4 100644 --- a/kabinizer-front-end/src/pages/selectPeriods/index.tsx +++ b/kabinizer-front-end/src/pages/selectPeriods/index.tsx @@ -1,153 +1,19 @@ -import { useMutation, useQuery, useQueryClient } from "react-query"; -import { - BookingRequest, - BookingRequestService, - CreateBookingRequestDto, - DrawService, -} from "../../../api/index.ts"; - -import WeekDayRow from "../../components/WeekDayRow"; -import Calendar from "./Calendar/Calendar.tsx"; -import { useState } from "react"; -import Button from "../../components/Button.tsx"; -import Deadline from "./Deadline.tsx"; -import Title from "../../components/Title.tsx"; -import { getAllBookingRequests } from "@/utils/calendar.ts"; - -const getDeletedBookings = ( - bookings: BookingRequest[] | undefined = [], - selected: CreateBookingRequestDto[], -) => { - return bookings.filter( - (b) => !selected.map((s) => s.periodId).includes(b.period?.id), - ); -}; - -const SelectPeriodsView = () => { - const queryClient = useQueryClient(); - const [selected, setSelected] = useState([]); - - const { data = [], isLoading } = useQuery(["getApiDraw"], () => - DrawService.getApiDraw(), - ); - - const getSelectedBookingRequests = async () => { - try { - const bookingRequests = - await BookingRequestService.getApiBookingRequest(); - - setSelected( - bookingRequests.map((d) => ({ - periodId: d.period?.id ?? "", - userId: d.user?.id ?? "", - bookingRequestId: d.bookingRequestId ?? "", - })), - ); - - return bookingRequests; - } catch (error) { - console.error(error); - } - }; - - const { data: bookings } = useQuery( - ["getApiBookingRequest"], - () => getSelectedBookingRequests(), - { staleTime: Infinity, cacheTime: Infinity }, - ); - - const handleUpdate = async () => { - try { - const deletedBookings = getDeletedBookings(bookings, selected); - const deletedBookingIds = deletedBookings.map( - (d) => d.bookingRequestId as string, - ); - - await BookingRequestService.deleteApiBookingRequest(deletedBookingIds); - await BookingRequestService.postApiBookingRequest(selected); - - queryClient.invalidateQueries("getApiBookingRequest"); - } catch (error) { - console.error(error); - } - }; - - const { mutateAsync: update, isLoading: isUpdating } = useMutation(() => - handleUpdate(), - ); - - const handleSelectAll = () => { - const allPeriods = getAllBookingRequests(data); - if (selected.length === allPeriods.length) { - setSelected([]); - } else { - setSelected(allPeriods); - } - }; - - if (isLoading) { - return ( -
Loading...
- ); - } - - return ( -
-
- Mine ønsker -
- -
-
-
-
- - setSelected(selected)} - /> -
-
- {isUpdating ? ( -

Lagrer...

- ) : ( - - )} -
-
-
- ); -}; - -const Label = () => { - return ( -
-

Ønsker

- - - +import DrawPeriodsCalendar from "./Calendar/DrawPeriodsCalendar.tsx"; +import CurrentDrawDeadlines from "./CurrentDrawDeadlines.tsx"; + +const SelectPeriodsView = () => ( +
+
+

+ Frister for trekning +

+ +

+ Velg ønsket periode +

+
- ); -}; +
+); export default SelectPeriodsView;