MVP Reached
This commit is contained in:
parent
35ddedb5c3
commit
19529085e7
|
|
@ -15,7 +15,8 @@ namespace api.Controllers;
|
|||
[Route("[controller]")]
|
||||
public class TicketController(
|
||||
IQrCodeGenerator qr,
|
||||
ITicketManager ticketManager) : ControllerBase
|
||||
ITicketManager ticketManager,
|
||||
IEventManager eventManager) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a Base64 String Qr Code and Saves Qr Code and Ticket to DB
|
||||
|
|
@ -41,7 +42,7 @@ public class TicketController(
|
|||
QrCode = qrCode,
|
||||
Type = request.Type,
|
||||
EventId = request.EventId,
|
||||
SeasonId = request.SeasonId != Guid.Empty ? request.SeasonId : Guid.Empty,
|
||||
SeasonId = request.SeasonId,
|
||||
Patron = request.Patron,
|
||||
};
|
||||
|
||||
|
|
@ -51,8 +52,8 @@ public class TicketController(
|
|||
//return
|
||||
var response = new MintResponse
|
||||
{
|
||||
QrCode = ticket.QrCode,
|
||||
Type = ticket.Type
|
||||
Ticket = ticket,
|
||||
Details = eventManager.GetEvent(ticket.EventId)
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using models.Core;
|
||||
using models.Request;
|
||||
using models.Response;
|
||||
|
||||
namespace api.Interfaces;
|
||||
|
||||
|
|
@ -9,4 +10,5 @@ public interface IEventManager
|
|||
void PatchEvent(PatchEvent request);
|
||||
List<Event> GetEvents(DateTime? startDate, DateTime? endDate);
|
||||
List<Event> GetAllEvents();
|
||||
EventDetails GetEvent(Guid id);
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ using api.Interfaces;
|
|||
using data.Events;
|
||||
using models.Core;
|
||||
using models.Request;
|
||||
using models.Response;
|
||||
|
||||
namespace api.Services;
|
||||
|
||||
|
|
@ -38,4 +39,9 @@ public class EventManager : IEventManager
|
|||
{
|
||||
return new GetAll().Execute();
|
||||
}
|
||||
|
||||
public EventDetails GetEvent(Guid id)
|
||||
{
|
||||
return new GetDetails().Execute(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -49,20 +49,20 @@ public class TicketManager : ITicketManager
|
|||
|
||||
return new MintResponse()
|
||||
{
|
||||
QrCode = ticket.QrCode,
|
||||
Type = ticket.Type,
|
||||
Ticket = ticket,
|
||||
Details = new data.Events.GetDetails().Execute(ticket.EventId),
|
||||
};
|
||||
}
|
||||
|
||||
private static TicketValidity DetermineValidity(Event @event)
|
||||
{
|
||||
if (@event.Date < DateTime.Now)
|
||||
if (@event.Date.Day != DateTime.Today.Day) return TicketValidity.Invalid;
|
||||
|
||||
if (@event.Date.Day < DateTime.Now.Day)
|
||||
{
|
||||
return TicketValidity.Expired;
|
||||
}
|
||||
|
||||
if (@event.Date.Day != DateTime.Today.Day) return TicketValidity.Invalid;
|
||||
|
||||
if (DateTime.Now.Hour < @event.Date.Hour - 2)
|
||||
{
|
||||
return TicketValidity.Early;
|
||||
|
|
|
|||
27
source/ticketAPI/data/Events/GetDetails.cs
Normal file
27
source/ticketAPI/data/Events/GetDetails.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using models.Core;
|
||||
using models.Response;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace data.Events;
|
||||
|
||||
public class GetDetails
|
||||
{
|
||||
public EventDetails Execute(Guid eventId)
|
||||
{
|
||||
var database = MongoFactory.GetDatabase();
|
||||
var collection = database.GetCollection<Event>("events");
|
||||
var filter = Builders<Event>.Filter.Eq(e => e.Id, eventId);
|
||||
var @event = collection.Find(filter).FirstOrDefault();
|
||||
|
||||
var details = new EventDetails
|
||||
{
|
||||
Name = @event.Name,
|
||||
Description = @event.Description,
|
||||
Date = @event.Date,
|
||||
Venue = @event.Venue,
|
||||
Talent = @event.Talent,
|
||||
};
|
||||
|
||||
return details;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ namespace models.Core;
|
|||
public class Ticket
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid SeasonId { get; set; }
|
||||
public Guid? SeasonId { get; set; }
|
||||
public Guid EventId { get; set; }
|
||||
public TicketType Type { get; set; }
|
||||
public required string QrCode { get; set; }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ public class AddTicket
|
|||
{
|
||||
public TicketType Type { get; set; }
|
||||
public Guid EventId { get; set; }
|
||||
public Guid SeasonId { get; set; } = Guid.Empty;
|
||||
public Guid? SeasonId { get; set; } = Guid.Empty;
|
||||
public required Patron Patron { get; set; }
|
||||
}
|
||||
12
source/ticketAPI/models/Response/EventDetails.cs
Normal file
12
source/ticketAPI/models/Response/EventDetails.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using models.Core;
|
||||
|
||||
namespace models.Response;
|
||||
|
||||
public class EventDetails
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public required Venue Venue { get; set; }
|
||||
public required Talent Talent { get; set; }
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
using models.Enumerations;
|
||||
using models.Core;
|
||||
|
||||
namespace models.Response;
|
||||
|
||||
public class MintResponse
|
||||
{
|
||||
public required string QrCode { get; set; }
|
||||
public TicketType Type { get; set; }
|
||||
public required Ticket Ticket { get; set; }
|
||||
public required EventDetails Details { get; set; }
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
"@zxing/browser": "^0.1.5",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"@zxing/ngx-scanner": "^19.0.0",
|
||||
"guid-typescript": "^1.0.9",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
|
|
|
|||
BIN
source/ticketUI/public/pso-logo.png
Normal file
BIN
source/ticketUI/public/pso-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
|
|
@ -1,7 +1,22 @@
|
|||
@if(qrCode().length > 0) {
|
||||
<div class="card">
|
||||
@if (ticket$() !== null) {
|
||||
<div class="card column">
|
||||
<div class="card row">
|
||||
<img src="pso-logo.png" width="250" height="100" alt="Parma Symphony Orchestra Logo"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<img class="qrcode" alt="Embedded QR Code" src="data:image/png;base64,{{qrCode().toString()}}" />
|
||||
<img class="qrcode" alt="Embedded QR Code" src="data:image/png;base64,{{qrCode}}"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="card">
|
||||
<span class="row">{{talent?.name}}</span>
|
||||
<span class="row">{{details?.name}}</span>
|
||||
<span class="row">{{details?.description}}</span>
|
||||
<span class="row">{{details?.date | date: 'medium'}}</span>
|
||||
<span class="row">{{venue?.name}}</span>
|
||||
<span class="row">{{venue?.description}}</span>
|
||||
<span class="row">{{venue?.addressOne}}</span>
|
||||
<span class="row">{{venue?.city}}, {{venue?.state}} {{venue?.zip}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
import {Component, inject} from '@angular/core';
|
||||
import {TicketService} from '../../services/ticket.service';
|
||||
import {DatePipe} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-generated-ticket',
|
||||
imports: [],
|
||||
imports: [
|
||||
DatePipe
|
||||
],
|
||||
templateUrl: './generated-ticket.component.html',
|
||||
styleUrl: './generated-ticket.component.scss'
|
||||
})
|
||||
export class GeneratedTicketComponent {
|
||||
private ticket = inject(TicketService);
|
||||
public qrCode = this.ticket.dataSignal$;
|
||||
public ticket$ = this.ticket.mintResponse$;
|
||||
public qrCode = this.ticket$()?.ticket.qrCode;
|
||||
public details = this.ticket$()?.details;
|
||||
public talent = this.ticket$()?.details.talent;
|
||||
public venue = this.ticket$()?.details.venue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,29 @@
|
|||
@if (ticketSearch$() != null) {
|
||||
<div class="card">
|
||||
<!-- Show Validity, If Valid Show Ticket Type and Indicate Season -->
|
||||
<div class="column">
|
||||
<div class="column">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img [src]="getValidityImage()" width="100px" height="100px" [alt]="getValidityText()"/>
|
||||
<span class="row">{{getValidityText()}}</span>
|
||||
</div>
|
||||
@if(ticketValidity() === TicketValidity.Valid) {
|
||||
<div class="column">
|
||||
<img [src]="getTypeImage()" width="50px" height="50px" [alt]="getTypeText()"/>
|
||||
<span class="row">{{getTypeText()}}</span>
|
||||
</div>
|
||||
@if(IsSeasonTicket(ticketType())) {
|
||||
<img src="season.svg" width="50px" height="50px" alt="Season Ticket"/>
|
||||
<span class="row">Seasonal</span>
|
||||
</td>
|
||||
<td>{{ getValidityText() }}</td>
|
||||
</tr>
|
||||
@if (ticketValidity() === TicketValidity.Valid) {
|
||||
<tr>
|
||||
<td>
|
||||
<img [src]="getTypeImage()" width="100px" height="100px" [alt]="getTypeText()"/>
|
||||
</td>
|
||||
<td>{{ getTypeText() }}</td>
|
||||
</tr>
|
||||
@if (IsSeasonTicket(ticketType())) {
|
||||
<tr>
|
||||
<td>
|
||||
<img src="season.svg" width="100px" height="100px" alt="Season Ticket"/>
|
||||
</td>
|
||||
<td>Seasonal</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {Component, inject} from '@angular/core';
|
|||
import {ZXingScannerModule} from '@zxing/ngx-scanner';
|
||||
import {ScanResultComponent} from '../../components/scan-result/scan-result.component';
|
||||
import {ScanService} from '../../services/scan.service';
|
||||
import {TicketTypeEnum} from '../../../models/enums/ticket-type.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-scan',
|
||||
|
|
@ -22,7 +23,7 @@ export class ScanComponent {
|
|||
public click(): void {
|
||||
this.hideScanner = !this.hideScanner;
|
||||
this.scan.ticketValid$.set(null);
|
||||
this.scan.ticketType$.set(null);
|
||||
this.scan.ticketType$.set(TicketTypeEnum.Null);
|
||||
this.scan.ticketSearch$.set(null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@
|
|||
<button class="button" (click)="saveTicket()">Save Ticket</button>
|
||||
</div>
|
||||
|
||||
@if(qrCode().length > 0) {
|
||||
@if(ticket$() !== null) {
|
||||
<app-generated-ticket />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {AddTicket} from '../../../models/request/add-ticket';
|
|||
import {SeasonBrowserComponent} from '../../components/season-browser/season-browser.component';
|
||||
import {IsSeasonTicket} from '../../../models/enums/ticket-type.enum';
|
||||
import {GeneratedTicketComponent} from '../../components/generated-ticket/generated-ticket.component';
|
||||
import {Guid} from 'guid-typescript';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ticket',
|
||||
|
|
@ -27,7 +28,7 @@ export class TicketComponent {
|
|||
@ViewChild(SeasonBrowserComponent) seasonBrowserComponent!: SeasonBrowserComponent;
|
||||
|
||||
public ticketService = inject(TicketService);
|
||||
public qrCode = this.ticketService.dataSignal$;
|
||||
public ticket$ = this.ticketService.mintResponse$;
|
||||
|
||||
public saveTicket(): void {
|
||||
const addTicket: AddTicket = {
|
||||
|
|
@ -35,7 +36,7 @@ export class TicketComponent {
|
|||
type: this.ticketTypeSelector.selectedTicketType,
|
||||
seasonId:
|
||||
IsSeasonTicket(this.ticketTypeSelector.selectedTicketType)
|
||||
? this.seasonBrowserComponent.selectedSeasonId : '',
|
||||
? this.seasonBrowserComponent.selectedSeasonId : null,
|
||||
patron: this.patronInfoComponent.buildPatron()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {SnackbarService} from './snackbar.service';
|
|||
})
|
||||
export class ScanService extends ApiUtils {
|
||||
public ticketValid$: WritableSignal<TicketValidity | null> = signal(TicketValidity.Invalid);
|
||||
public ticketType$: WritableSignal<TicketTypeEnum | null> = signal(TicketTypeEnum.Null);
|
||||
public ticketType$: WritableSignal<TicketTypeEnum> = signal(TicketTypeEnum.Null);
|
||||
public ticketSearch$: WritableSignal<TicketSearch | null> = signal(null);
|
||||
private httpClient = inject(HttpClient);
|
||||
private snackbar = inject(SnackbarService);
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@ import {AddTicket} from '../../models/request/add-ticket';
|
|||
import {ApiUtils} from './api-utils';
|
||||
import {TicketSearch} from '../../models/response/ticket-search';
|
||||
import {SnackbarService} from './snackbar.service';
|
||||
import {Ticket} from '../../models/core/ticket';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TicketService extends ApiUtils {
|
||||
public dataSignal$: WritableSignal<string> = signal('');
|
||||
public mintResponse$: WritableSignal<MintResponse | null> = signal(null);
|
||||
private httpClient = inject(HttpClient);
|
||||
private snackbar = inject(SnackbarService);
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ export class TicketService extends ApiUtils {
|
|||
})
|
||||
).subscribe((res: MintResponse) => {
|
||||
if (res !== undefined) {
|
||||
this.dataSignal$.set(res.qrCode);
|
||||
this.mintResponse$.set(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
10
source/ticketUI/src/models/response/event-details.ts
Normal file
10
source/ticketUI/src/models/response/event-details.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import {Venue} from '../core/venue';
|
||||
import {Talent} from '../core/talent';
|
||||
|
||||
export interface EventDetails {
|
||||
date: Date,
|
||||
name: string,
|
||||
description: string | null,
|
||||
venue: Venue,
|
||||
talent: Talent
|
||||
}
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
import {Ticket} from '../core/ticket';
|
||||
import {EventDetails} from './event-details';
|
||||
|
||||
export interface MintResponse {
|
||||
qrCode: string,
|
||||
ticket: Ticket,
|
||||
details: EventDetails
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user