diff --git a/source/ticketAPI/api/Controllers/EventController.cs b/source/ticketAPI/api/Controllers/EventController.cs index 7261e1e..434c3e4 100644 --- a/source/ticketAPI/api/Controllers/EventController.cs +++ b/source/ticketAPI/api/Controllers/EventController.cs @@ -14,7 +14,7 @@ namespace api.Controllers; public class EventController(IEventManager eventManager) : ControllerBase { [HttpPost] - public ActionResult Post([FromBody] AddEvent request) + public IActionResult Post([FromBody] AddEvent request) { //TODO: Protect Endpoint @@ -35,7 +35,7 @@ public class EventController(IEventManager eventManager) : ControllerBase /// New Event Information /// [HttpPatch] - public ActionResult Patch([FromBody] PatchEvent request) + public IActionResult Patch([FromBody] PatchEvent request) { //TODO: Protect Endpoint @@ -51,16 +51,21 @@ public class EventController(IEventManager eventManager) : ControllerBase } /// - /// Gets a list of all events between a start date and an end date + /// Gets a list of all events between a start date and an end date. + /// If the values are null, all events are returned. /// /// Start date for the event search /// End date for the event search /// [HttpGet] - public ActionResult> Get(DateTime startDate, DateTime endDate) + public ActionResult> Get(DateTime? startDate, DateTime? endDate) { try { + if (startDate == null && endDate == null) + { + return Ok(eventManager.GetAllEvents()); + } return Ok(eventManager.GetEvents(startDate, endDate)); } catch (Exception e) diff --git a/source/ticketAPI/api/Controllers/SeasonController.cs b/source/ticketAPI/api/Controllers/SeasonController.cs new file mode 100644 index 0000000..ebad377 --- /dev/null +++ b/source/ticketAPI/api/Controllers/SeasonController.cs @@ -0,0 +1,75 @@ +using api.Interfaces; +using Microsoft.AspNetCore.Mvc; +using models.Core; +using models.Request; + +namespace api.Controllers; + +/// +/// Endpoints for Season Management +/// +/// Event Manager Service +[ApiController] +[Route("[controller]")] +public class SeasonController(ISeasonManager seasonManager) : ControllerBase +{ + /// + /// Adds a season to the database + /// + /// Season to add + /// + [HttpPost] + public IActionResult Post([FromBody] AddSeason request) + { + //TODO: Protect Endpoint + + try + { + seasonManager.AddSeason(request); + return Ok(); + } + catch (Exception e) + { + return BadRequest(e.Message); + } + } + + /// + /// Gets all seasons + /// + /// List of Seasons + [HttpGet] + public ActionResult> Get() + { + //TODO: Protect Endpoint + try + { + return Ok(seasonManager.GetSeasons()); + } + catch (Exception e) + { + return BadRequest(e.Message); + } + } + + /// + /// Adds an event to a season + /// + /// Season Id + /// Event Id + /// + [HttpPut] + public IActionResult Put(Guid eventId, Guid seasonId) + { + //TODO: Protect Endpoint + try + { + seasonManager.AddEventToSeason(eventId, seasonId); + return Ok(); + } + catch (Exception e) + { + return BadRequest(e.Message); + } + } +} \ No newline at end of file diff --git a/source/ticketAPI/api/IServiceCollectionExtensions.cs b/source/ticketAPI/api/IServiceCollectionExtensions.cs index 8d0f5f1..7f49db6 100644 --- a/source/ticketAPI/api/IServiceCollectionExtensions.cs +++ b/source/ticketAPI/api/IServiceCollectionExtensions.cs @@ -10,5 +10,6 @@ public static class ServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } } \ No newline at end of file diff --git a/source/ticketAPI/api/Interfaces/IEventManager.cs b/source/ticketAPI/api/Interfaces/IEventManager.cs index 87b130b..90f003a 100644 --- a/source/ticketAPI/api/Interfaces/IEventManager.cs +++ b/source/ticketAPI/api/Interfaces/IEventManager.cs @@ -7,5 +7,6 @@ public interface IEventManager { void AddEvent(AddEvent request); void PatchEvent(PatchEvent request); - List GetEvents(DateTime startDate, DateTime endDate); + List GetEvents(DateTime? startDate, DateTime? endDate); + List GetAllEvents(); } \ No newline at end of file diff --git a/source/ticketAPI/api/Interfaces/ISeasonManager.cs b/source/ticketAPI/api/Interfaces/ISeasonManager.cs new file mode 100644 index 0000000..65279fd --- /dev/null +++ b/source/ticketAPI/api/Interfaces/ISeasonManager.cs @@ -0,0 +1,11 @@ +using models.Core; +using models.Request; + +namespace api.Interfaces; + +public interface ISeasonManager +{ + void AddSeason(AddSeason season); + List GetSeasons(); + void AddEventToSeason(Guid eventId, Guid seasonId); +} \ No newline at end of file diff --git a/source/ticketAPI/api/RestFiles/event.http b/source/ticketAPI/api/RestFiles/event.http index b6a6464..b2f4eb2 100644 --- a/source/ticketAPI/api/RestFiles/event.http +++ b/source/ticketAPI/api/RestFiles/event.http @@ -54,4 +54,10 @@ Content-Type: application/json GET {{api_HostAddress}}/event?startDate=2024-12-01&endDate=2024-12-31 Accept: application/json +Content-Type: application/json + +### + +GET {{api_HostAddress}}/event +Accept: application/json Content-Type: application/json \ No newline at end of file diff --git a/source/ticketAPI/api/RestFiles/season.http b/source/ticketAPI/api/RestFiles/season.http new file mode 100644 index 0000000..f68639c --- /dev/null +++ b/source/ticketAPI/api/RestFiles/season.http @@ -0,0 +1,26 @@ +@api_HostAddress = http://localhost:5168 +@seasonId = 76178cf4-3805-4a66-a3d9-a0223d99c2fa +@eventId = b9f4478b-701b-4223-9aaa-042b6f53b83a + +GET {{api_HostAddress}}/season +Accept: application/json +Content-Type: application/json + +### + +POST {{api_HostAddress}}/season +Accept: application/json +Content-Type: application/json + +{ + "SeasonName": "PSO 25 - 26", + "SeasonDescription": "Parma Symphony Orchestra's 53rd Season", + "StartDate": "2025-08-01T00:00:00.991Z", + "EndDate": "2026-05-31T00:00:00.991Z" +} + +### + +PUT {{api_HostAddress}}/season?eventId={{eventId}}&seasonId={{seasonId}} +Accept: application/json +Content-Type: application/json \ No newline at end of file diff --git a/source/ticketAPI/api/Services/EventManager.cs b/source/ticketAPI/api/Services/EventManager.cs index 017c917..04a8b2b 100644 --- a/source/ticketAPI/api/Services/EventManager.cs +++ b/source/ticketAPI/api/Services/EventManager.cs @@ -27,8 +27,13 @@ public class EventManager : IEventManager new Update().Execute(request); } - public List GetEvents(DateTime startDate, DateTime endDate) + public List GetEvents(DateTime? startDate, DateTime? endDate) { return new GetInDates().Execute(startDate, endDate); } + + public List GetAllEvents() + { + return new GetAll().Execute(); + } } \ No newline at end of file diff --git a/source/ticketAPI/api/Services/SeasonManager.cs b/source/ticketAPI/api/Services/SeasonManager.cs new file mode 100644 index 0000000..590b663 --- /dev/null +++ b/source/ticketAPI/api/Services/SeasonManager.cs @@ -0,0 +1,34 @@ +using api.Interfaces; +using data.Seasons; +using models.Core; +using models.Request; +using AddEvent = data.Seasons.AddEvent; + +namespace api.Services; + +public class SeasonManager : ISeasonManager +{ + public void AddSeason(AddSeason request) + { + var season = new Season + { + Id = Guid.NewGuid(), + Name = request.SeasonName, + Description = request.SeasonDescription, + StartDate = request.StartDate, + EndDate = request.EndDate, + }; + + new Save().Execute(season); + } + + public List GetSeasons() + { + return new GetAll().Execute(); + } + + public void AddEventToSeason(Guid eventId, Guid seasonId) + { + new AddEvent().Execute(eventId, seasonId); + } +} \ No newline at end of file diff --git a/source/ticketAPI/data/Events/GetAll.cs b/source/ticketAPI/data/Events/GetAll.cs new file mode 100644 index 0000000..8723d37 --- /dev/null +++ b/source/ticketAPI/data/Events/GetAll.cs @@ -0,0 +1,15 @@ +using models.Core; +using MongoDB.Driver; + +namespace data.Events; + +public class GetAll +{ + public List Execute() + { + var database = MongoFactory.GetDatabase(); + var collection = database.GetCollection("events"); + + return collection.Find(_ => true).ToList(); + } +} \ No newline at end of file diff --git a/source/ticketAPI/data/Events/GetInDates.cs b/source/ticketAPI/data/Events/GetInDates.cs index 1840046..6a2fc58 100644 --- a/source/ticketAPI/data/Events/GetInDates.cs +++ b/source/ticketAPI/data/Events/GetInDates.cs @@ -6,7 +6,7 @@ namespace data.Events; public class GetInDates { - public List Execute(DateTime startDate, DateTime endDate) + public List Execute(DateTime? startDate, DateTime? endDate) { var database = MongoFactory.GetDatabase(); var collection = database.GetCollection("events"); diff --git a/source/ticketAPI/data/Seasons/AddEvent.cs b/source/ticketAPI/data/Seasons/AddEvent.cs new file mode 100644 index 0000000..14efbd3 --- /dev/null +++ b/source/ticketAPI/data/Seasons/AddEvent.cs @@ -0,0 +1,18 @@ +using models.Core; +using MongoDB.Driver; + +namespace data.Seasons; + +public class AddEvent +{ + public void Execute(Guid eventId, Guid seasonId) + { + var database = MongoFactory.GetDatabase(); + var collection = database.GetCollection("seasons"); + var filter = Builders.Filter.Eq(s => s.Id, seasonId); + + var update = Builders.Update.Push("EventIds", eventId); + + collection.FindOneAndUpdate(filter, update); + } +} \ No newline at end of file diff --git a/source/ticketAPI/data/Seasons/GetAll.cs b/source/ticketAPI/data/Seasons/GetAll.cs new file mode 100644 index 0000000..2716609 --- /dev/null +++ b/source/ticketAPI/data/Seasons/GetAll.cs @@ -0,0 +1,15 @@ +using models.Core; +using MongoDB.Driver; + +namespace data.Seasons; + +public class GetAll +{ + public List Execute() + { + var database = MongoFactory.GetDatabase(); + var collection = database.GetCollection("seasons"); + + return collection.Find(_ => true).ToList(); + } +} \ No newline at end of file diff --git a/source/ticketAPI/data/Seasons/Save.cs b/source/ticketAPI/data/Seasons/Save.cs new file mode 100644 index 0000000..e82b51e --- /dev/null +++ b/source/ticketAPI/data/Seasons/Save.cs @@ -0,0 +1,14 @@ +using models.Core; + +namespace data.Seasons; + +public class Save +{ + public void Execute(Season season) + { + var database = MongoFactory.GetDatabase(); + var collection = database.GetCollection("seasons"); + + collection.InsertOne(season); + } +} \ No newline at end of file diff --git a/source/ticketAPI/models/Core/Season.cs b/source/ticketAPI/models/Core/Season.cs new file mode 100644 index 0000000..130d5c7 --- /dev/null +++ b/source/ticketAPI/models/Core/Season.cs @@ -0,0 +1,11 @@ +namespace models.Core; + +public class Season +{ + public Guid Id { get; set; } + public required string Name { get; set; } + public string? Description { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public List EventIds { get; set; } = []; +} \ No newline at end of file diff --git a/source/ticketAPI/models/Request/AddSeason.cs b/source/ticketAPI/models/Request/AddSeason.cs new file mode 100644 index 0000000..0b57034 --- /dev/null +++ b/source/ticketAPI/models/Request/AddSeason.cs @@ -0,0 +1,9 @@ +namespace models.Request; + +public class AddSeason +{ + public required string SeasonName { get; set; } + public string? SeasonDescription { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } +} \ No newline at end of file diff --git a/source/ticketUI/package.json b/source/ticketUI/package.json index 8a79004..5f955fb 100644 --- a/source/ticketUI/package.json +++ b/source/ticketUI/package.json @@ -18,6 +18,9 @@ "@angular/platform-browser": "^19.0.0", "@angular/platform-browser-dynamic": "^19.0.0", "@angular/router": "^19.0.0", + "@zxing/browser": "^0.1.5", + "@zxing/library": "^0.21.3", + "@zxing/ngx-scanner": "^19.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" diff --git a/source/ticketUI/src/app/page/scan/scan.component.html b/source/ticketUI/src/app/page/scan/scan.component.html index c706b4d..c55f3ea 100644 --- a/source/ticketUI/src/app/page/scan/scan.component.html +++ b/source/ticketUI/src/app/page/scan/scan.component.html @@ -1 +1,14 @@ -

scan works!

+ +@if(hideScanner) { +
+ +
+} @else { + +} diff --git a/source/ticketUI/src/app/page/scan/scan.component.ts b/source/ticketUI/src/app/page/scan/scan.component.ts index c92f5c0..9803faf 100644 --- a/source/ticketUI/src/app/page/scan/scan.component.ts +++ b/source/ticketUI/src/app/page/scan/scan.component.ts @@ -1,11 +1,18 @@ import { Component } from '@angular/core'; +import {ZXingScannerModule} from '@zxing/ngx-scanner'; +import {ScanResultComponent} from '../../scan-result/scan-result.component'; @Component({ selector: 'app-scan', - imports: [], + imports: [ZXingScannerModule, ScanResultComponent], templateUrl: './scan.component.html', styleUrl: './scan.component.scss' }) export class ScanComponent { + public hideScanner: boolean = false; + public onCodeResult(resultString: string): void { + this.hideScanner = false; + console.log(resultString); + } } diff --git a/source/ticketUI/src/app/scan-result/scan-result.component.html b/source/ticketUI/src/app/scan-result/scan-result.component.html new file mode 100644 index 0000000..3059525 --- /dev/null +++ b/source/ticketUI/src/app/scan-result/scan-result.component.html @@ -0,0 +1,5 @@ +@if(ticketValidity() !== TicketValidity.Invalid && ticketType() !== TicketTypeEnum.Null) { +
+ +
+} diff --git a/source/ticketUI/src/app/scan-result/scan-result.component.scss b/source/ticketUI/src/app/scan-result/scan-result.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/source/ticketUI/src/app/scan-result/scan-result.component.ts b/source/ticketUI/src/app/scan-result/scan-result.component.ts new file mode 100644 index 0000000..0a9cb97 --- /dev/null +++ b/source/ticketUI/src/app/scan-result/scan-result.component.ts @@ -0,0 +1,18 @@ +import {Component, inject} from '@angular/core'; +import {TicketService} from '../services/ticket.service'; +import {TicketValidity} from '../../models/enums/ticket-validity.enum'; +import {TicketTypeEnum} from '../../models/enums/ticket-type.enum'; + +@Component({ + selector: 'app-scan-result', + imports: [], + templateUrl: './scan-result.component.html', + styleUrl: './scan-result.component.scss' +}) +export class ScanResultComponent { + private ticket = inject(TicketService); + public ticketValidity = this.ticket.ticketValid$; + public ticketType = this.ticket.ticketType$; + protected readonly TicketValidity = TicketValidity; + protected readonly TicketTypeEnum = TicketTypeEnum; +} diff --git a/source/ticketUI/src/app/services/ticket.service.ts b/source/ticketUI/src/app/services/ticket.service.ts index 017e0f8..07e0e8e 100644 --- a/source/ticketUI/src/app/services/ticket.service.ts +++ b/source/ticketUI/src/app/services/ticket.service.ts @@ -1,6 +1,5 @@ -import {inject, Injectable, WritableSignal} from '@angular/core'; +import {inject, Injectable, signal, WritableSignal} from '@angular/core'; import {HttpClient} from '@angular/common/http'; -import {signal} from '@angular/core'; import {catchError, map, of} from 'rxjs'; import {environment} from '../../environments/environment'; import {MintResponse} from '../../models/response/mint-response'; @@ -8,13 +7,16 @@ import {Endpoints} from '../../models/endpoints'; import {MintRequest} from '../../models/request/mint-request'; import {ApiUtils} from './api-utils'; import {TicketValidity} from '../../models/enums/ticket-validity.enum'; +import {TicketTypeEnum} from '../../models/enums/ticket-type.enum'; +import {TicketSearch} from '../../models/response/ticket-search'; @Injectable({ providedIn: 'root' }) export class TicketService extends ApiUtils { - public dataSignal: WritableSignal = signal(''); - public validitySignal: WritableSignal = signal(null); + public dataSignal$: WritableSignal = signal(''); + public ticketValid$: WritableSignal = signal(TicketValidity.Null); + public ticketType$: WritableSignal = signal(TicketTypeEnum.Null); private httpClient = inject(HttpClient); constructor() { @@ -34,7 +36,7 @@ export class TicketService extends ApiUtils { }) ).subscribe((res: MintResponse) => { if (res !== undefined) { - this.dataSignal.set(res.qrCode); + this.dataSignal$.set(res.qrCode); } }); } @@ -43,16 +45,17 @@ export class TicketService extends ApiUtils { const options = this.setHttpRequestOptions(); const url = environment.apiBase + Endpoints.TICKET_SEARCH(ticketId); - this.httpClient.get(url, options) + this.httpClient.get(url, options) .pipe( map((response: any) => response.body), catchError(error => { console.log(error); return of(undefined); }) - ).subscribe((res: TicketValidity) => { + ).subscribe((res: TicketSearch) => { if (res !== undefined) { - this.validitySignal.set(res); + this.ticketType$.set(res.ticketType); + this.ticketValid$.set(res.ticketValidity) } }); } diff --git a/source/ticketUI/src/app/ticket/ticket.component.ts b/source/ticketUI/src/app/ticket/ticket.component.ts index 6a523cd..dcf5890 100644 --- a/source/ticketUI/src/app/ticket/ticket.component.ts +++ b/source/ticketUI/src/app/ticket/ticket.component.ts @@ -8,6 +8,6 @@ import {TicketService} from '../services/ticket.service'; styleUrl: './ticket.component.scss' }) export class TicketComponent { - private ticketMinter = inject(TicketService); - public qrCode = this.ticketMinter.dataSignal; + private ticket = inject(TicketService); + public qrCode = this.ticket.dataSignal$; } diff --git a/source/ticketUI/src/models/enums/ticket-type.enum.ts b/source/ticketUI/src/models/enums/ticket-type.enum.ts index 4ae4212..ed3a8cc 100644 --- a/source/ticketUI/src/models/enums/ticket-type.enum.ts +++ b/source/ticketUI/src/models/enums/ticket-type.enum.ts @@ -1,4 +1,5 @@ export enum TicketTypeEnum { + Null, Single, SingleSeason, Family, diff --git a/source/ticketUI/src/models/enums/ticket-validity.enum.ts b/source/ticketUI/src/models/enums/ticket-validity.enum.ts index 3876b42..99b468a 100644 --- a/source/ticketUI/src/models/enums/ticket-validity.enum.ts +++ b/source/ticketUI/src/models/enums/ticket-validity.enum.ts @@ -1,4 +1,5 @@ export enum TicketValidity { + Null, Valid, Expired, Early, diff --git a/source/ticketUI/yarn.lock b/source/ticketUI/yarn.lock index 9d3a4ae..8c2731c 100644 --- a/source/ticketUI/yarn.lock +++ b/source/ticketUI/yarn.lock @@ -2508,6 +2508,34 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== +"@zxing/browser@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@zxing/browser/-/browser-0.1.5.tgz#94c0cdbc8ab5114a82dadee54939de6d9bf7e313" + integrity sha512-4Lmrn/il4+UNb87Gk8h1iWnhj39TASEHpd91CwwSJtY5u+wa0iH9qS0wNLAWbNVYXR66WmT5uiMhZ7oVTrKfxw== + optionalDependencies: + "@zxing/text-encoding" "^0.9.0" + +"@zxing/library@^0.21.3": + version "0.21.3" + resolved "https://registry.yarnpkg.com/@zxing/library/-/library-0.21.3.tgz#0a4cb777701884131832b05922d7e88ef1b87ab4" + integrity sha512-hZHqFe2JyH/ZxviJZosZjV+2s6EDSY0O24R+FQmlWZBZXP9IqMo7S3nb3+2LBWxodJQkSurdQGnqE7KXqrYgow== + dependencies: + ts-custom-error "^3.2.1" + optionalDependencies: + "@zxing/text-encoding" "~0.9.0" + +"@zxing/ngx-scanner@^19.0.0": + version "19.0.0" + resolved "https://registry.yarnpkg.com/@zxing/ngx-scanner/-/ngx-scanner-19.0.0.tgz#7a3fb054bfdba661b00ecb0a100af12fc76ec513" + integrity sha512-2V0617jKVTjZ3ekEf5uMjfjOE0jjazt2GT/zvfGt68ZxjDMYyGMuKj6p+0KP+pjnxiXHE6NwWaVUWfW43uKtvA== + dependencies: + tslib "^2.3.0" + +"@zxing/text-encoding@^0.9.0", "@zxing/text-encoding@~0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" + integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== + abbrev@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" @@ -6492,6 +6520,11 @@ tree-kill@1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== +ts-custom-error@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/ts-custom-error/-/ts-custom-error-3.3.1.tgz#8bd3c8fc6b8dc8e1cb329267c45200f1e17a65d1" + integrity sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A== + tslib@2.8.1, tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"