diff --git a/.gitignore b/.gitignore index 9714d0f..29473f7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ **/node_modules/* **/bin/* **/obj/* +**/.angular/* # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. @@ -22,7 +23,7 @@ yarn-error.log **/.classpath/* **/.c9/* *.launch -*/.settings/** +**/.settings/* *.sublime-workspace # Visual Studio Code diff --git a/README.md b/README.md index afb67ce..e256ebb 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ An application to handle the ticketing of an event for a small organization. * [ASP.Net](https://dotnet.microsoft.com/en-us/apps/aspnet) * [QRCoder](https://github.com/codebude/QRCoder) * [Angular](https://angular.dev) - * [Tachyons](https://tachyons.io) * [zxing](https://www.npmjs.com/package/@zxing/ngx-scanner) * [SQLite](https://www.sqlite.org/index.html) diff --git a/source/ticketAPI/api/Controllers/TicketController.cs b/source/ticketAPI/api/Controllers/TicketController.cs new file mode 100644 index 0000000..00e1a27 --- /dev/null +++ b/source/ticketAPI/api/Controllers/TicketController.cs @@ -0,0 +1,55 @@ +using api.Interfaces; +using Microsoft.AspNetCore.Mvc; +using models.Core; +using models.Request; +using models.Response; + +namespace api.Controllers; + +/// +/// Endpoints for Qr Code Generation +/// +/// Injected QR Code Service +/// Injected Ticket Manager Service +[ApiController] +[Route("[controller]")] +public class TicketController( + IQrCodeGenerator qr, + ITicketManager ticketManager) : ControllerBase +{ + /// + /// Generates a Base64 String Qr Code + /// + /// Base64 String Qr Code + [HttpPost("mint")] + public ActionResult MintTicket([FromBody] MintTickets mintRequest) + { + //TODO: Protect Endpoint + + //generate ticket id + var ticketId = Guid.NewGuid(); + + //generate the qr code + var qrCode = qr.GenerateQrCode(ticketId.ToString()); + + //build the ticket + var ticket = new Ticket + { + Id = Guid.NewGuid(), + QrCode = qrCode, + Type = mintRequest.Type, + }; + + //save the minted ticket + ticketManager.SaveMintedTicket(ticket); + + //return + var response = new MintResponse + { + QrCode = ticket.QrCode, + Type = ticket.Type + }; + + return Ok(response); + } +} \ No newline at end of file diff --git a/source/ticketAPI/api/Controllers/WeatherForecastController.cs b/source/ticketAPI/api/Controllers/WeatherForecastController.cs deleted file mode 100644 index bea88b5..0000000 --- a/source/ticketAPI/api/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace api.Controllers; - -[ApiController] -[Route("[controller]")] -public class WeatherForecastController : ControllerBase -{ - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } -} \ No newline at end of file diff --git a/source/ticketAPI/api/IServiceCollectionExtensions.cs b/source/ticketAPI/api/IServiceCollectionExtensions.cs new file mode 100644 index 0000000..1d5c6d0 --- /dev/null +++ b/source/ticketAPI/api/IServiceCollectionExtensions.cs @@ -0,0 +1,13 @@ +using api.Interfaces; +using api.Services; + +namespace api; + +public static class ServiceCollectionExtensions +{ + public static void AddServices(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + } +} \ No newline at end of file diff --git a/source/ticketAPI/api/Interfaces/IQRCodeGenerator.cs b/source/ticketAPI/api/Interfaces/IQRCodeGenerator.cs new file mode 100644 index 0000000..9eed83c --- /dev/null +++ b/source/ticketAPI/api/Interfaces/IQRCodeGenerator.cs @@ -0,0 +1,13 @@ +namespace api.Interfaces; + +/// +/// Contains logic to build Qr Codes +/// +public interface IQrCodeGenerator +{ + /// + /// Generates a Qr Code with an embedded random Guid + /// + /// + string GenerateQrCode(string ticketId); +} \ No newline at end of file diff --git a/source/ticketAPI/api/Interfaces/ITicketManager.cs b/source/ticketAPI/api/Interfaces/ITicketManager.cs new file mode 100644 index 0000000..7cf4fd7 --- /dev/null +++ b/source/ticketAPI/api/Interfaces/ITicketManager.cs @@ -0,0 +1,8 @@ +using models.Core; + +namespace api.Interfaces; + +public interface ITicketManager +{ + void SaveMintedTicket(Ticket ticket); +} \ No newline at end of file diff --git a/source/ticketAPI/api/Program.cs b/source/ticketAPI/api/Program.cs index 8a07408..e3f5a1c 100644 --- a/source/ticketAPI/api/Program.cs +++ b/source/ticketAPI/api/Program.cs @@ -1,17 +1,26 @@ +using api; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. - builder.Services.AddControllers(); +builder.Services.AddServices(); +builder.Services.AddCors(o => +{ + //TODO: Allow only a specific domain to access this API. + o.AddPolicy("AllowOrigin", b => + { + b.AllowAnyOrigin(); + b.AllowAnyMethod(); + b.AllowAnyHeader(); + }); +}); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseHttpsRedirection(); - +app.UseCors("AllowOrigin"); app.UseAuthorization(); - app.MapControllers(); - app.Run(); \ No newline at end of file diff --git a/source/ticketAPI/api/api.http b/source/ticketAPI/api/RestFiles/ticket.http similarity index 51% rename from source/ticketAPI/api/api.http rename to source/ticketAPI/api/RestFiles/ticket.http index fcb8e35..edcd880 100644 --- a/source/ticketAPI/api/api.http +++ b/source/ticketAPI/api/RestFiles/ticket.http @@ -1,6 +1,10 @@ @api_HostAddress = http://localhost:5168 -GET {{api_HostAddress}}/weatherforecast/ +POST {{api_HostAddress}}/ticket/mint Accept: application/json +{ + "ticketType": "Single" +} + ### diff --git a/source/ticketAPI/api/Services/QRCodeGenerator.cs b/source/ticketAPI/api/Services/QRCodeGenerator.cs new file mode 100644 index 0000000..39669e0 --- /dev/null +++ b/source/ticketAPI/api/Services/QRCodeGenerator.cs @@ -0,0 +1,22 @@ +using api.Interfaces; +using QRCoder; + +namespace api.Services; + +/// +/// Contains logic to build Qr Codes +/// +public class QrCodeGenerator : IQrCodeGenerator +{ + /// + /// Generates a Qr Code with an embedded random Guid + /// + /// + public string GenerateQrCode(string ticketId) + { + var generator = new QRCodeGenerator(); + var data = generator.CreateQrCode(ticketId, QRCodeGenerator.ECCLevel.Q); + var qr = new Base64QRCode(data); + return qr.GetGraphic(20); + } +} \ No newline at end of file diff --git a/source/ticketAPI/api/Services/TicketManager.cs b/source/ticketAPI/api/Services/TicketManager.cs new file mode 100644 index 0000000..0325c91 --- /dev/null +++ b/source/ticketAPI/api/Services/TicketManager.cs @@ -0,0 +1,14 @@ +using api.Interfaces; +using data.Tickets; +using models.Core; + +namespace api.Services; + +public class TicketManager(IConfiguration config) : ITicketManager +{ + public void SaveMintedTicket(Ticket ticket) + { + var db = new Save(config); + db.Execute(ticket); + } +} \ No newline at end of file diff --git a/source/ticketAPI/api/WeatherForecast.cs b/source/ticketAPI/api/WeatherForecast.cs deleted file mode 100644 index c2faad5..0000000 --- a/source/ticketAPI/api/WeatherForecast.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace api; - -public class WeatherForecast -{ - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } -} \ No newline at end of file diff --git a/source/ticketAPI/api/api.csproj b/source/ticketAPI/api/api.csproj index 5a25aeb..eef141a 100644 --- a/source/ticketAPI/api/api.csproj +++ b/source/ticketAPI/api/api.csproj @@ -6,4 +6,19 @@ enable + + + + + + + + + + + + Always + + + diff --git a/source/ticketAPI/api/appsettings.Development.json b/source/ticketAPI/api/appsettings.Development.json index 0c208ae..f82d217 100644 --- a/source/ticketAPI/api/appsettings.Development.json +++ b/source/ticketAPI/api/appsettings.Development.json @@ -4,5 +4,9 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "Mongo": { + "ConnectionString": "mongodb://terralilly85:pr1ncessN0ra@192.168.1.14:27017/", + "Database": "pso" } } diff --git a/source/ticketAPI/api/appsettings.json b/source/ticketAPI/api/appsettings.json index 10f68b8..d95b08f 100644 --- a/source/ticketAPI/api/appsettings.json +++ b/source/ticketAPI/api/appsettings.json @@ -5,5 +5,9 @@ "Microsoft.AspNetCore": "Warning" } }, + "Mongo": { + "ConnectionString": "mongodb://terralilly85:pr1ncessN0ra@192.168.1.14:27017/", + "Database": "pso" + }, "AllowedHosts": "*" } diff --git a/source/ticketAPI/data/Tickets/SaveTicket.cs b/source/ticketAPI/data/Tickets/SaveTicket.cs new file mode 100644 index 0000000..9e92eaf --- /dev/null +++ b/source/ticketAPI/data/Tickets/SaveTicket.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Configuration; +using models.Core; +using MongoDB.Driver; + +namespace data.Tickets; + +public class Save(IConfiguration config) +{ + public void Execute(Ticket ticket) + { + if (String.IsNullOrEmpty(config.GetSection("Mongo:ConnectionString").Value)) + { + throw new Exception("No MongoDB connection string"); + } + + var client = new MongoClient(config.GetSection("Mongo:ConnectionString").Value); + var database = client.GetDatabase(config.GetSection("Mongo:Database").Value); + var collection = database.GetCollection("tickets"); + + collection.InsertOne(ticket); + } +} \ No newline at end of file diff --git a/source/ticketAPI/data/data.csproj b/source/ticketAPI/data/data.csproj index 17b910f..239c9e9 100644 --- a/source/ticketAPI/data/data.csproj +++ b/source/ticketAPI/data/data.csproj @@ -6,4 +6,13 @@ enable + + + + + + + + + diff --git a/source/ticketAPI/models/Core/Event.cs b/source/ticketAPI/models/Core/Event.cs new file mode 100644 index 0000000..9cf2abd --- /dev/null +++ b/source/ticketAPI/models/Core/Event.cs @@ -0,0 +1,16 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace models.Core; + +public class Event +{ + [BsonGuidRepresentation(GuidRepresentation.Standard)] + public Guid Id { get; set; } + public DateTime Date { get; set; } + public required string EventName { get; set; } + public string? EventDescription { get; set; } + public required Venue Venue { get; set; } + public required Talent Talent { get; set; } + public List TicketIds { get; set; } = []; +} \ No newline at end of file diff --git a/source/ticketAPI/models/Core/Talent.cs b/source/ticketAPI/models/Core/Talent.cs new file mode 100644 index 0000000..1bf1337 --- /dev/null +++ b/source/ticketAPI/models/Core/Talent.cs @@ -0,0 +1,12 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace models.Core; + +public class Talent +{ + [BsonGuidRepresentation(GuidRepresentation.Standard)] + public Guid Id { get; set; } + public required string Name { get; set; } + public string? Description { get; set; } +} \ No newline at end of file diff --git a/source/ticketAPI/models/Core/Ticket.cs b/source/ticketAPI/models/Core/Ticket.cs new file mode 100644 index 0000000..f238560 --- /dev/null +++ b/source/ticketAPI/models/Core/Ticket.cs @@ -0,0 +1,15 @@ +using models.Enumerations; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace models.Core; + +public class Ticket +{ + [BsonGuidRepresentation(GuidRepresentation.Standard)] + public Guid Id { get; set; } + [BsonGuidRepresentation(GuidRepresentation.Standard)] + public Guid EventId { get; set; } + public TicketType Type { get; set; } + public required string QrCode { get; set; } +} \ No newline at end of file diff --git a/source/ticketAPI/models/Core/Venue.cs b/source/ticketAPI/models/Core/Venue.cs new file mode 100644 index 0000000..e391fa4 --- /dev/null +++ b/source/ticketAPI/models/Core/Venue.cs @@ -0,0 +1,16 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace models.Core; + +public class Venue +{ + [BsonGuidRepresentation(GuidRepresentation.Standard)] + public Guid Id { get; set; } + public required string Name { get; set; } + public string? Description { get; set; } + public List Address { get; set; } = []; + public required string City { get; set; } + public required string State { get; set; } + public required string Zip { get; set; } +} \ No newline at end of file diff --git a/source/ticketAPI/models/Enumerations/TicketType.cs b/source/ticketAPI/models/Enumerations/TicketType.cs new file mode 100644 index 0000000..135f2c1 --- /dev/null +++ b/source/ticketAPI/models/Enumerations/TicketType.cs @@ -0,0 +1,11 @@ +namespace models.Enumerations; + +public enum TicketType +{ + Single, + SingleSeason, + Family, + FamilySeason, + Senior, + SeniorSeason +} \ No newline at end of file diff --git a/source/ticketAPI/models/Request/MintTickets.cs b/source/ticketAPI/models/Request/MintTickets.cs new file mode 100644 index 0000000..d560ff8 --- /dev/null +++ b/source/ticketAPI/models/Request/MintTickets.cs @@ -0,0 +1,9 @@ +using models.Enumerations; + +namespace models.Request; + +public class MintTickets +{ + public TicketType Type { get; set; } + public Guid EventId { get; set; } +} \ No newline at end of file diff --git a/source/ticketAPI/models/Response/MintResponse.cs b/source/ticketAPI/models/Response/MintResponse.cs new file mode 100644 index 0000000..1313c28 --- /dev/null +++ b/source/ticketAPI/models/Response/MintResponse.cs @@ -0,0 +1,9 @@ +using models.Enumerations; + +namespace models.Response; + +public class MintResponse +{ + public required string QrCode {get;set;} + public TicketType Type { get; set; } +} \ No newline at end of file diff --git a/source/ticketAPI/models/models.csproj b/source/ticketAPI/models/models.csproj index 17b910f..934cc62 100644 --- a/source/ticketAPI/models/models.csproj +++ b/source/ticketAPI/models/models.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/source/ticketAPI/ticketAPI.sln.DotSettings.user b/source/ticketAPI/ticketAPI.sln.DotSettings.user new file mode 100644 index 0000000..942f032 --- /dev/null +++ b/source/ticketAPI/ticketAPI.sln.DotSettings.user @@ -0,0 +1,5 @@ + + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/source/ticketUI/angular.json b/source/ticketUI/angular.json index 6514799..3c28ce6 100644 --- a/source/ticketUI/angular.json +++ b/source/ticketUI/angular.json @@ -55,7 +55,13 @@ "development": { "optimization": false, "extractLicenses": false, - "sourceMap": true + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] } }, "defaultConfiguration": "production" diff --git a/source/ticketUI/src/app/app.component.html b/source/ticketUI/src/app/app.component.html index 36093e1..0694ce4 100644 --- a/source/ticketUI/src/app/app.component.html +++ b/source/ticketUI/src/app/app.component.html @@ -1,336 +1,8 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} - - - - - } -
- -
+
+
+
-
- - - - - - - - - - - +
+ +
+ diff --git a/source/ticketUI/src/app/app.component.spec.ts b/source/ticketUI/src/app/app.component.spec.ts deleted file mode 100644 index ec9b684..0000000 --- a/source/ticketUI/src/app/app.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AppComponent], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have the 'ticketUI' title`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('ticketUI'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, ticketUI'); - }); -}); diff --git a/source/ticketUI/src/app/app.component.ts b/source/ticketUI/src/app/app.component.ts index b8abe42..77c8f56 100644 --- a/source/ticketUI/src/app/app.component.ts +++ b/source/ticketUI/src/app/app.component.ts @@ -1,12 +1,16 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import {Component} from '@angular/core'; +import {SidebarComponent} from './sidebar/sidebar.component'; +import {RouterOutlet} from '@angular/router'; @Component({ selector: 'app-root', - imports: [RouterOutlet], + imports: [ + SidebarComponent, + RouterOutlet + ], templateUrl: './app.component.html', styleUrl: './app.component.scss' }) export class AppComponent { - title = 'ticketUI'; + } diff --git a/source/ticketUI/src/app/app.config.ts b/source/ticketUI/src/app/app.config.ts index a1e7d6f..f0ce9be 100644 --- a/source/ticketUI/src/app/app.config.ts +++ b/source/ticketUI/src/app/app.config.ts @@ -2,7 +2,12 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; +import {provideHttpClient} from '@angular/common/http'; export const appConfig: ApplicationConfig = { - providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)] + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + provideHttpClient() + ] }; diff --git a/source/ticketUI/src/app/app.routes.ts b/source/ticketUI/src/app/app.routes.ts index dc39edb..fa11451 100644 --- a/source/ticketUI/src/app/app.routes.ts +++ b/source/ticketUI/src/app/app.routes.ts @@ -1,3 +1,9 @@ import { Routes } from '@angular/router'; +import {DebugComponent} from './page/debug/debug.component'; -export const routes: Routes = []; +export const routes: Routes = [ + { + path: 'debug', + component: DebugComponent + } +]; diff --git a/source/ticketUI/src/app/page/debug/debug.component.html b/source/ticketUI/src/app/page/debug/debug.component.html new file mode 100644 index 0000000..fe87625 --- /dev/null +++ b/source/ticketUI/src/app/page/debug/debug.component.html @@ -0,0 +1,3 @@ + + + diff --git a/source/ticketUI/src/app/page/debug/debug.component.scss b/source/ticketUI/src/app/page/debug/debug.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/source/ticketUI/src/app/page/debug/debug.component.spec.ts b/source/ticketUI/src/app/page/debug/debug.component.spec.ts new file mode 100644 index 0000000..38528ea --- /dev/null +++ b/source/ticketUI/src/app/page/debug/debug.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DebugComponent } from './debug.component'; + +describe('DebugComponent', () => { + let component: DebugComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DebugComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DebugComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/source/ticketUI/src/app/page/debug/debug.component.ts b/source/ticketUI/src/app/page/debug/debug.component.ts new file mode 100644 index 0000000..af85c93 --- /dev/null +++ b/source/ticketUI/src/app/page/debug/debug.component.ts @@ -0,0 +1,25 @@ +import {Component, inject} from '@angular/core'; +import {TicketComponent} from "../../ticket/ticket.component"; +import {TicketService} from '../../services/ticket.service'; +import {MintRequest} from '../../../models/request/mint-request'; +import {TicketTypeEnum} from '../../../models/enums/ticket-type.enum'; + +@Component({ + selector: 'app-debug', + imports: [ + TicketComponent + ], + templateUrl: './debug.component.html', + styleUrl: './debug.component.scss' +}) +export class DebugComponent { + private ticketMinter = inject(TicketService); + + public getQrCode(): void { + const mintRequest: MintRequest = { + ticketType: TicketTypeEnum.Single + }; + + this.ticketMinter.mintTicket(mintRequest); + } +} diff --git a/source/ticketUI/src/app/services/ticket.service.ts b/source/ticketUI/src/app/services/ticket.service.ts new file mode 100644 index 0000000..fdeb062 --- /dev/null +++ b/source/ticketUI/src/app/services/ticket.service.ts @@ -0,0 +1,64 @@ +import {Injectable} from '@angular/core'; +import {HttpClient, HttpHeaders, HttpParams} 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'; +import {Endpoints} from '../../models/endpoints'; +import {MintRequest} from '../../models/request/mint-request'; + +@Injectable({ + providedIn: 'root' +}) +export class TicketService { + public dataSignal = signal(''); + + constructor(public httpClient: HttpClient) { + } + + public mintTicket(request: MintRequest): void { + const options = this.setHttpRequestOptions(); + const url = environment.apiBase + Endpoints.MINT_TICKETS; + + this.httpClient.post(url, JSON.stringify(request), options) + .pipe( + map((response: any) => response.body), + catchError(error => { + console.log(error); + return of(undefined); + }) + ).subscribe((res: MintResponse) => { + if (res !== undefined) { + this.dataSignal.set(res.qrCode); + } + }) + } + + private setHttpRequestOptions(params?: any): any { + let headers: HttpHeaders = new HttpHeaders(); + headers = headers.set('Content-Type', 'application/json'); + + const options: any = { + headers, + observe: 'response' + }; + + if (params) { + options.params = this.setHttpParams(params); + } + + return options; + } + + private setHttpParams(query: object): HttpParams { + const params = new HttpParams(); + for (const key in query) { + // @ts-ignore + if (query[key] && query.hasOwnProperty(key)) { + // @ts-ignore + params.append(key, query[key]); + } + } + return params; + } +} diff --git a/source/ticketUI/src/app/sidebar/sidebar.component.html b/source/ticketUI/src/app/sidebar/sidebar.component.html new file mode 100644 index 0000000..9e99c43 --- /dev/null +++ b/source/ticketUI/src/app/sidebar/sidebar.component.html @@ -0,0 +1,8 @@ +
+ + + + @if(!environment.production) { + + } +
diff --git a/source/ticketUI/src/app/sidebar/sidebar.component.scss b/source/ticketUI/src/app/sidebar/sidebar.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/source/ticketUI/src/app/sidebar/sidebar.component.spec.ts b/source/ticketUI/src/app/sidebar/sidebar.component.spec.ts new file mode 100644 index 0000000..5445f3c --- /dev/null +++ b/source/ticketUI/src/app/sidebar/sidebar.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SidebarComponent } from './sidebar.component'; + +describe('SidebarComponent', () => { + let component: SidebarComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SidebarComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SidebarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/source/ticketUI/src/app/sidebar/sidebar.component.ts b/source/ticketUI/src/app/sidebar/sidebar.component.ts new file mode 100644 index 0000000..83e9e99 --- /dev/null +++ b/source/ticketUI/src/app/sidebar/sidebar.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import {environment} from '../../environments/environment'; +import {RouterModule} from '@angular/router'; + +@Component({ + selector: 'app-sidebar', + imports: [RouterModule], + templateUrl: './sidebar.component.html', + styleUrl: './sidebar.component.scss' +}) +export class SidebarComponent { + //TODO: Make sidebar stick to the left side of the screen + //TODO: Make sidebar collapsable + protected readonly environment = environment; +} diff --git a/source/ticketUI/src/app/ticket/ticket.component.html b/source/ticketUI/src/app/ticket/ticket.component.html new file mode 100644 index 0000000..b147f07 --- /dev/null +++ b/source/ticketUI/src/app/ticket/ticket.component.html @@ -0,0 +1,10 @@ +@if(qrCode().length > 0) { +
+
+ Embedded QR Code +
+
+ test +
+
+} diff --git a/source/ticketUI/src/app/ticket/ticket.component.scss b/source/ticketUI/src/app/ticket/ticket.component.scss new file mode 100644 index 0000000..a08fedc --- /dev/null +++ b/source/ticketUI/src/app/ticket/ticket.component.scss @@ -0,0 +1,5 @@ +.qrcode { + height: 150px; + width: 150px; + padding: 10px; +} diff --git a/source/ticketUI/src/app/ticket/ticket.component.ts b/source/ticketUI/src/app/ticket/ticket.component.ts new file mode 100644 index 0000000..6a523cd --- /dev/null +++ b/source/ticketUI/src/app/ticket/ticket.component.ts @@ -0,0 +1,13 @@ +import {Component, inject} from '@angular/core'; +import {TicketService} from '../services/ticket.service'; + +@Component({ + selector: 'app-ticket', + imports: [], + templateUrl: './ticket.component.html', + styleUrl: './ticket.component.scss' +}) +export class TicketComponent { + private ticketMinter = inject(TicketService); + public qrCode = this.ticketMinter.dataSignal; +} diff --git a/source/ticketUI/src/environments/environment.development.ts b/source/ticketUI/src/environments/environment.development.ts new file mode 100644 index 0000000..4aaafd8 --- /dev/null +++ b/source/ticketUI/src/environments/environment.development.ts @@ -0,0 +1,4 @@ +export const environment = { + production: false, + apiBase: 'http://localhost:5168/', +}; diff --git a/source/ticketUI/src/environments/environment.ts b/source/ticketUI/src/environments/environment.ts new file mode 100644 index 0000000..64346b2 --- /dev/null +++ b/source/ticketUI/src/environments/environment.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + apiBase: 'http://localhost:5168/', +}; diff --git a/source/ticketUI/src/models/endpoints.ts b/source/ticketUI/src/models/endpoints.ts new file mode 100644 index 0000000..56d6f00 --- /dev/null +++ b/source/ticketUI/src/models/endpoints.ts @@ -0,0 +1,4 @@ +export class Endpoints { + public static readonly TICKET = 'ticket'; + public static readonly MINT_TICKETS = Endpoints.TICKET + '/mint' +} diff --git a/source/ticketUI/src/models/enums/ticket-type.enum.ts b/source/ticketUI/src/models/enums/ticket-type.enum.ts new file mode 100644 index 0000000..4ae4212 --- /dev/null +++ b/source/ticketUI/src/models/enums/ticket-type.enum.ts @@ -0,0 +1,8 @@ +export enum TicketTypeEnum { + Single, + SingleSeason, + Family, + FamilySeason, + Senior, + SeniorSeason +} diff --git a/source/ticketUI/src/models/request/mint-request.ts b/source/ticketUI/src/models/request/mint-request.ts new file mode 100644 index 0000000..2a7ea8f --- /dev/null +++ b/source/ticketUI/src/models/request/mint-request.ts @@ -0,0 +1,5 @@ +import {TicketTypeEnum} from '../enums/ticket-type.enum'; + +export interface MintRequest { + ticketType: TicketTypeEnum +} diff --git a/source/ticketUI/src/models/response/mint-response.ts b/source/ticketUI/src/models/response/mint-response.ts new file mode 100644 index 0000000..8a9aefe --- /dev/null +++ b/source/ticketUI/src/models/response/mint-response.ts @@ -0,0 +1,3 @@ +export interface MintResponse { + qrCode: string, +} diff --git a/source/ticketUI/src/styles.scss b/source/ticketUI/src/styles.scss index 90d4ee0..07b5789 100644 --- a/source/ticketUI/src/styles.scss +++ b/source/ticketUI/src/styles.scss @@ -1 +1,52 @@ -/* You can add global styles to this file, and also import other style files */ +/* Background styles */ +body { + background-image: url('https://images.unsplash.com/photo-1648461513491-c8dc79739211?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'); + background-size: auto; +} + +/* Glassmorphism card effect */ +.card { + margin: 15px; + padding: 5px; + backdrop-filter: blur(25px) saturate(112%); + -webkit-backdrop-filter: blur(25px) saturate(112%); + background-color: rgba(255, 255, 255, 0.11); + border-radius: 12px; + border: 1px solid rgba(255, 255, 255, 0.125); + width: fit-content; + + &-large { + width: 250px; + } +} + +.button { + margin: 15px; + padding: 5px; + backdrop-filter: blur(25px) saturate(112%); + -webkit-backdrop-filter: blur(25px) saturate(112%); + background-color: rgba(255, 255, 255, 0.11); + border-radius: 8px; + border: 1px solid rgba(255, 255, 255, 0.125); + width: auto; + + &:hover { + background-color: rgba(255, 255, 255, 0.0); + } + + &:active { + background-color: rgba(255, 255, 255, 0.25); + } +} + +.row { + display: flex; + flex-direction: row; + justify-content: center; +} + +.column { + display: flex; + flex-direction: column; + justify-content: center; +}