Compare commits
3 Commits
main
...
release/20
| Author | SHA1 | Date | |
|---|---|---|---|
| a98b05f14b | |||
| 903e562e42 | |||
|
|
50efe0f310 |
|
|
@ -1,31 +0,0 @@
|
||||||
name: Build
|
|
||||||
run-name: ${{gitea.actor}} is building 🚀
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
BuildUI:
|
|
||||||
runs-on: osx
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Restoring Node Packages
|
|
||||||
run: |
|
|
||||||
cd source
|
|
||||||
cd ticketUI
|
|
||||||
yarn install --frozen-lockfile
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd source
|
|
||||||
cd ticketUI
|
|
||||||
yarn run build
|
|
||||||
BuildAPI:
|
|
||||||
runs-on: osx
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setting up .Net CLI
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: '9.0.x'
|
|
||||||
- name: Build API
|
|
||||||
run: dotnet build ./source/ticketAPI/ticketAPI.sln
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
name: Deploy
|
|
||||||
run-name: ${{gitea.actor}} is deploying 🚀
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
BuildUI:
|
|
||||||
runs-on: osx
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Restoring Node Packages
|
|
||||||
run: |
|
|
||||||
cd source
|
|
||||||
cd ticketUI
|
|
||||||
yarn install --frozen-lockfile
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd source
|
|
||||||
cd ticketUI
|
|
||||||
yarn run build
|
|
||||||
- name: Saving build artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: ticketUI
|
|
||||||
path: source/ticketUI/dist/ticket-ui
|
|
||||||
BuildAPI:
|
|
||||||
runs-on: osx
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setting up .Net CLI
|
|
||||||
uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
dotnet-version: '9.0.x'
|
|
||||||
- name: Build API
|
|
||||||
run: dotnet publish -c release -o artifacts/api /p:DebugType=None /p:DebugSymbols=false ./source/ticketAPI/ticketAPI.sln
|
|
||||||
- name: Saving build artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: ticketAPI
|
|
||||||
path: artifacts/api
|
|
||||||
PublishUI:
|
|
||||||
runs-on: osx
|
|
||||||
needs: [BuildUI, BuildAPI]
|
|
||||||
name: Publish UI Docker Image
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Getting Artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: ticketUI
|
|
||||||
path: source/ticketUI/dist/ticket-ui
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Build and push image
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: source/ticketUI/Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: ${{ secrets.DOCKERHUB_USERNAME }}/ticket-ui:${{ github.ref_name }}
|
|
||||||
PublishAPI:
|
|
||||||
runs-on: osx
|
|
||||||
needs: [BuildAPI, PublishUI]
|
|
||||||
name: Publish API Docker Image
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Getting Artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: ticketAPI
|
|
||||||
path: artifacts/api
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Build and push image
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: source/ticketAPI/Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: ${{ secrets.DOCKERHUB_USERNAME }}/ticket-api:${{ github.ref_name }}
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -47,5 +47,3 @@ testem.log
|
||||||
# System files
|
# System files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
source/ticketAPI/api/appsettings.Development.json
|
|
||||||
source/ticketAPI/api/appsettings.json
|
|
||||||
|
|
|
||||||
|
|
@ -2,61 +2,38 @@
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
classDiagram
|
classDiagram
|
||||||
|
Ticket
|
||||||
Event
|
Event
|
||||||
Patron
|
Patron
|
||||||
Season
|
Address
|
||||||
Talent
|
Order
|
||||||
Ticket
|
|
||||||
Venue
|
|
||||||
|
|
||||||
Event : Guid Id
|
|
||||||
Event : Guid SeasonId
|
|
||||||
Event : Date Date
|
|
||||||
Event : String Name
|
|
||||||
Event : String? Description
|
|
||||||
Event : Object Venue
|
|
||||||
Event : Object Talent
|
|
||||||
Event : Array TicketIds
|
|
||||||
|
|
||||||
Patron : String FirstName
|
|
||||||
Patron : String? MiddleName
|
|
||||||
Patron : String LastName
|
|
||||||
Patron : String Email
|
|
||||||
Patron : String? PhoneNumber
|
|
||||||
Patron : String AddressOne
|
|
||||||
Patron : String? AddressTwo
|
|
||||||
Patron : String City
|
|
||||||
Patron : String State
|
|
||||||
Patron : String Zip
|
|
||||||
|
|
||||||
Season : Guid Id
|
|
||||||
Season : String Name
|
|
||||||
Season : String? Description
|
|
||||||
Season : Date StartDate
|
|
||||||
Season : Date EndDate
|
|
||||||
Season : Array EventIds
|
|
||||||
|
|
||||||
Talent : String Name
|
|
||||||
Talent : String? Description
|
|
||||||
|
|
||||||
Ticket : Guid Id
|
Ticket : Guid Id
|
||||||
Ticket : Guid? SeasonId
|
Ticket : int Seats
|
||||||
Ticket : Guid EventId
|
Ticket : DateTime Date
|
||||||
Ticket : TicketType Type
|
Ticket : Patron Patron
|
||||||
Ticket : String QrCode
|
|
||||||
Ticket : Object Patron
|
|
||||||
|
|
||||||
Venue : String Name
|
Event : Guid Id
|
||||||
Venue : String? Description
|
Event : String Title
|
||||||
Venue : String AddressOne
|
Event : String Description
|
||||||
Venue : String? AddressTwo
|
Event : DateTime Date
|
||||||
Venue : String City
|
Event : Array Tickets
|
||||||
Venue : String State
|
|
||||||
Venue : String Zip
|
|
||||||
|
|
||||||
Patron --* Ticket
|
Patron : Guid Id
|
||||||
|
Patron : String EmailAddress
|
||||||
|
Patron : String PhoneNumber
|
||||||
|
Patron : Address Address
|
||||||
|
|
||||||
Venue --* Event
|
Address : Guid Id
|
||||||
Talent --* Event
|
Address : String StreetAddressOne
|
||||||
|
Address : String StreetAddressTwo
|
||||||
|
Address : String City
|
||||||
|
Address : String State
|
||||||
|
Address : String ZipCode
|
||||||
|
|
||||||
|
Order : Guid Id
|
||||||
|
Order : Patron Patron
|
||||||
|
Order : Array Tickets
|
||||||
|
|
||||||
|
Event o-- Ticket : X Tickets
|
||||||
```
|
```
|
||||||
|
|
|
||||||
12
diagrams/order-lookup.md
Normal file
12
diagrams/order-lookup.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Order Lookup
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant UI
|
||||||
|
participant API
|
||||||
|
participant DB
|
||||||
|
UI->>API: Order Id
|
||||||
|
API->>DB: Order Id
|
||||||
|
DB->>API: Order Returned
|
||||||
|
API->>UI: Order Details Returned
|
||||||
|
```
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
# Ticket Generation
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant UI
|
|
||||||
participant API
|
|
||||||
participant DB
|
|
||||||
UI ->> API: Print Ticket Request
|
|
||||||
API ->> API: Generate Ticket Id
|
|
||||||
API ->> API: Generate Qr Code
|
|
||||||
API ->> DB: Save Ticket
|
|
||||||
API ->> DB: Associate Ticket with Event
|
|
||||||
API ->> API: Build Ticket Data for UI
|
|
||||||
API ->> UI: Respond with Ticket
|
|
||||||
UI ->> UI: Builds Visual Ticket
|
|
||||||
```
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
# Ticket Lookup
|
|
||||||
|
|
||||||
## Happy Path
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant UI
|
|
||||||
participant API
|
|
||||||
participant DB
|
|
||||||
|
|
||||||
UI ->> UI: Decode QrCode
|
|
||||||
UI ->> API: Send TicketId from QrCode
|
|
||||||
API ->> DB: Query Ticket
|
|
||||||
DB -->> API: Ticket Returned, if found
|
|
||||||
API ->> DB: Query Event from Ticket
|
|
||||||
DB -->> API: Event Information Returned
|
|
||||||
API ->> API: Check for Ticket Validity
|
|
||||||
API ->> UI: Return Ticket Validity
|
|
||||||
```
|
|
||||||
|
|
||||||
## No Ticket Found
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant UI
|
|
||||||
participant API
|
|
||||||
participant DB
|
|
||||||
|
|
||||||
UI ->> UI: Decode QrCode
|
|
||||||
UI ->> API: Send TicketId from QrCode
|
|
||||||
API ->> DB: Query Ticket
|
|
||||||
DB -->> API: None Found
|
|
||||||
API ->> UI: Ticket Not Found
|
|
||||||
```
|
|
||||||
14
diagrams/ticket-minting.md
Normal file
14
diagrams/ticket-minting.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Ticket Minting
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant UI
|
||||||
|
participant API
|
||||||
|
participant DB
|
||||||
|
UI->>API: Mint Ticket(s) Request
|
||||||
|
API->>DB: Get Ticket Ids
|
||||||
|
DB->>DB: IDs Assigned
|
||||||
|
DB->>API: IDs Returned
|
||||||
|
API->>API: QR Codes Generated
|
||||||
|
API->>UI: Code Returned, Email Sent
|
||||||
|
```
|
||||||
29
diagrams/ticket-verification.md
Normal file
29
diagrams/ticket-verification.md
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Ticket Verification
|
||||||
|
|
||||||
|
## Happy Path
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant UI
|
||||||
|
participant API
|
||||||
|
participant DB
|
||||||
|
|
||||||
|
UI ->> API: Decoded Ticket Id from QR Code
|
||||||
|
API ->> DB: Query DB with Id
|
||||||
|
DB ->> API: Found Ticket Info
|
||||||
|
API ->> UI: Tickets Found, Admit X Seats
|
||||||
|
```
|
||||||
|
|
||||||
|
## Negative Path
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant UI
|
||||||
|
participant API
|
||||||
|
participant DB
|
||||||
|
|
||||||
|
UI ->> API: Decoded Ticket Id from QR Code
|
||||||
|
API ->> DB: Query DB with Id
|
||||||
|
DB ->> API: None Found
|
||||||
|
API ->> UI: Ticket Not Found
|
||||||
|
```
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
# Ticket Validity Check
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart TD
|
|
||||||
|
|
||||||
start([Ticket Found, Event Found])
|
|
||||||
eventDateIsToday(Checks if Event Date is Today)
|
|
||||||
todayIsAfterEvent(Checks if Event was before Today)
|
|
||||||
twoHoursEarly(Checks if its less than 2 hours to event start)
|
|
||||||
fiveHoursAgo(Checks if Event started less than 5 hours ago)
|
|
||||||
returnsInvalid([Ticket is Invalid])
|
|
||||||
returnsExpired([Ticket is Expired])
|
|
||||||
returnsExpired2([Ticket is Expired])
|
|
||||||
returnsEarly([Ticket is Early])
|
|
||||||
returnsValid([Ticket is Valid])
|
|
||||||
|
|
||||||
start --> eventDateIsToday -- yes --- todayIsAfterEvent
|
|
||||||
eventDateIsToday -- no --- returnsInvalid
|
|
||||||
|
|
||||||
todayIsAfterEvent -- yes --- returnsExpired2
|
|
||||||
|
|
||||||
todayIsAfterEvent --> twoHoursEarly -- no --- returnsEarly
|
|
||||||
twoHoursEarly -- yes --- fiveHoursAgo
|
|
||||||
fiveHoursAgo -- yes --- returnsValid
|
|
||||||
fiveHoursAgo -- no --- returnsExpired
|
|
||||||
```
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0
|
||||||
COPY ../../artifacts/api /app
|
COPY artifacts /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
ENTRYPOINT ["dotnet", "api.dll"]
|
ENTRYPOINT ["dotnet", "api.dll"]
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
|
|
@ -56,16 +56,15 @@ public class EventController(IEventManager eventManager) : ControllerBase
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startDate">Start date for the event search</param>
|
/// <param name="startDate">Start date for the event search</param>
|
||||||
/// <param name="endDate">End date for the event search</param>
|
/// <param name="endDate">End date for the event search</param>
|
||||||
/// <param name="seasonId">Season to find events for</param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public ActionResult<List<Event>> Get(DateTime? startDate, DateTime? endDate, string? seasonId)
|
public ActionResult<List<Event>> Get(DateTime? startDate, DateTime? endDate)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (startDate == null && endDate == null)
|
if (startDate == null && endDate == null)
|
||||||
{
|
{
|
||||||
return Ok(seasonId == null ? eventManager.GetAllEvents() : eventManager.GetBySeason(new Guid(seasonId)));
|
return Ok(eventManager.GetAllEvents());
|
||||||
}
|
}
|
||||||
return Ok(eventManager.GetEvents(startDate, endDate));
|
return Ok(eventManager.GetEvents(startDate, endDate));
|
||||||
}
|
}
|
||||||
|
|
@ -74,24 +73,4 @@ public class EventController(IEventManager eventManager) : ControllerBase
|
||||||
return BadRequest(e.Message);
|
return BadRequest(e.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes an event
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="eventId">Event to delete</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpDelete]
|
|
||||||
public IActionResult Delete(Guid eventId)
|
|
||||||
{
|
|
||||||
// TODO: Protect Endpoint
|
|
||||||
try
|
|
||||||
{
|
|
||||||
eventManager.DeleteEvent(eventId);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return BadRequest(e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -53,39 +53,18 @@ public class SeasonController(ISeasonManager seasonManager) : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates a season
|
/// Adds an event to a season
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">Updated season information</param>
|
/// <param name="seasonId">Season Id</param>
|
||||||
|
/// <param name="eventId">Event Id</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPatch]
|
[HttpPut]
|
||||||
public IActionResult Patch(PatchSeason request)
|
public IActionResult Put(Guid eventId, Guid seasonId)
|
||||||
{
|
{
|
||||||
//TODO: Protect Endpoint
|
//TODO: Protect Endpoint
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
seasonManager.PatchSeason(request);
|
seasonManager.AddEventToSeason(eventId, seasonId);
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return BadRequest(e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes a season
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="seasonId">SeasonId to delete</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpDelete]
|
|
||||||
public IActionResult Delete(Guid seasonId)
|
|
||||||
{
|
|
||||||
//TODO: Protect Endpoint
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
seasonManager.DeleteSeason(seasonId);
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,6 @@ public class TicketController(
|
||||||
Details = eventManager.GetEvent(ticket.EventId)
|
Details = eventManager.GetEvent(ticket.EventId)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ticket.Patron.Email))
|
|
||||||
{
|
|
||||||
//TODO: Hookup Email Service
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,5 @@ public static class ServiceCollectionExtensions
|
||||||
services.AddScoped<ITicketManager, TicketManager>();
|
services.AddScoped<ITicketManager, TicketManager>();
|
||||||
services.AddScoped<IEventManager, EventManager>();
|
services.AddScoped<IEventManager, EventManager>();
|
||||||
services.AddScoped<ISeasonManager, SeasonManager>();
|
services.AddScoped<ISeasonManager, SeasonManager>();
|
||||||
services.AddScoped<IEmailService, EmailService>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
using models.Core;
|
|
||||||
using models.Response;
|
|
||||||
|
|
||||||
namespace api.Interfaces;
|
|
||||||
|
|
||||||
public interface IEmailService
|
|
||||||
{
|
|
||||||
void SendEmail(Ticket ticket, EventDetails details);
|
|
||||||
}
|
|
||||||
|
|
@ -11,6 +11,4 @@ public interface IEventManager
|
||||||
List<Event> GetEvents(DateTime? startDate, DateTime? endDate);
|
List<Event> GetEvents(DateTime? startDate, DateTime? endDate);
|
||||||
List<Event> GetAllEvents();
|
List<Event> GetAllEvents();
|
||||||
EventDetails GetEvent(Guid id);
|
EventDetails GetEvent(Guid id);
|
||||||
void DeleteEvent(Guid eventId);
|
|
||||||
List<Event> GetBySeason(Guid seasonId);
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,5 @@ public interface ISeasonManager
|
||||||
{
|
{
|
||||||
void AddSeason(AddSeason season);
|
void AddSeason(AddSeason season);
|
||||||
List<Season> GetSeasons();
|
List<Season> GetSeasons();
|
||||||
void PatchSeason(PatchSeason request);
|
void AddEventToSeason(Guid eventId, Guid seasonId);
|
||||||
void DeleteSeason(Guid seasonId);
|
|
||||||
}
|
}
|
||||||
|
|
@ -62,10 +62,4 @@ Content-Type: application/json
|
||||||
|
|
||||||
GET {{api_HostAddress}}/event
|
GET {{api_HostAddress}}/event
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
GET {{api_HostAddress}}/event?seasonId={{seasonId}}
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
using System.Net;
|
|
||||||
using System.Net.Mail;
|
|
||||||
using System.Text;
|
|
||||||
using api.Interfaces;
|
|
||||||
using models.Core;
|
|
||||||
using models.Response;
|
|
||||||
|
|
||||||
namespace api.Services;
|
|
||||||
|
|
||||||
public class EmailService(IConfiguration config) : IEmailService
|
|
||||||
{
|
|
||||||
public void SendEmail(Ticket ticket, EventDetails @event)
|
|
||||||
{
|
|
||||||
using var client = new SmtpClient(config.GetSection("Email:Server").Value);
|
|
||||||
|
|
||||||
client.UseDefaultCredentials = false;
|
|
||||||
var auth = new NetworkCredential(config.GetSection("Email:UserName").Value,
|
|
||||||
config.GetSection("Email:Password").Value);
|
|
||||||
|
|
||||||
client.Credentials = auth;
|
|
||||||
var from = new MailAddress(config.GetSection("Email:From").Value ?? string.Empty);
|
|
||||||
|
|
||||||
if (from.Address != config.GetSection("Email:From").Value)
|
|
||||||
{
|
|
||||||
throw new Exception("Invalid email address");
|
|
||||||
}
|
|
||||||
|
|
||||||
var to = new MailAddress(ticket.Patron.Email);
|
|
||||||
|
|
||||||
var emailMessage = new MailMessage(from, to);
|
|
||||||
emailMessage.ReplyToList.Add(from);
|
|
||||||
emailMessage.Subject = $"Tickets - {@event.Name}";
|
|
||||||
emailMessage.SubjectEncoding = Encoding.UTF8;
|
|
||||||
|
|
||||||
emailMessage.IsBodyHtml = true;
|
|
||||||
emailMessage.Body = GenerateTicketBody(ticket, @event);
|
|
||||||
|
|
||||||
client.Send(emailMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GenerateTicketBody(Ticket ticket, EventDetails @event)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
const string cardStyle = "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;";
|
|
||||||
const string columnStyle = "display: flex; flex-direction: column; justify-content: center;";
|
|
||||||
const string rowStyle = "display: flex; flex-direction: row; justify-content: center; align-items: baseline;";
|
|
||||||
const string qrCodeStyle = "height: 150px; width: 150px; padding: 10px;";
|
|
||||||
const string imagePath = "Assets/pso-logo.png";
|
|
||||||
|
|
||||||
sb.AppendLine($"<div #ticket style=\"{cardStyle} {columnStyle}\">");
|
|
||||||
|
|
||||||
sb.AppendLine($"<div #talentLogo style=\"{cardStyle} {rowStyle}\">");
|
|
||||||
sb.AppendLine($"<img src=\"{imagePath}\" width=\"250\" height=\"100\" alt=\"Parma Symphony Orchestra Logo\"/>");
|
|
||||||
sb.AppendLine("</div>"); //closes #talentLogo
|
|
||||||
|
|
||||||
sb.AppendLine($"<div #qrCode style=\"{rowStyle}\">");
|
|
||||||
sb.AppendLine($"<img style=\"{qrCodeStyle}\" alt=\"QR Code\" src=\"data:image/png;base64,{ticket.QrCode}\"/>");
|
|
||||||
sb.AppendLine("</div>"); //closes #qrCode
|
|
||||||
|
|
||||||
sb.AppendLine($"<div #venue style=\"{rowStyle}\">");
|
|
||||||
sb.AppendLine($"<div style=\"{cardStyle}\">");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Talent.Name}</span>");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Name}</span>");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Description}</span>");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Date.ToString("F")}</span>");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Venue.Name}</span>");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Venue.Description}</span>");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Venue.AddressOne}</span>");
|
|
||||||
sb.AppendLine($"<span style=\"{rowStyle}\">{@event.Venue.City} {@event.Venue.State} {@event.Venue.Zip}</span>");
|
|
||||||
sb.AppendLine("</div>"); //closes #venue nested card
|
|
||||||
sb.AppendLine("</div>"); //closes #venue
|
|
||||||
|
|
||||||
sb.AppendLine("</div>"); //closes #ticket
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -22,6 +22,7 @@ public class EventManager : IEventManager
|
||||||
};
|
};
|
||||||
|
|
||||||
new Save().Execute(@event);
|
new Save().Execute(@event);
|
||||||
|
new data.Seasons.AddEvent().Execute(@event.Id, request.SeasonId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PatchEvent(PatchEvent request)
|
public void PatchEvent(PatchEvent request)
|
||||||
|
|
@ -43,14 +44,4 @@ public class EventManager : IEventManager
|
||||||
{
|
{
|
||||||
return new GetDetails().Execute(id);
|
return new GetDetails().Execute(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteEvent(Guid eventId)
|
|
||||||
{
|
|
||||||
new Delete().Execute(eventId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Event> GetBySeason(Guid seasonId)
|
|
||||||
{
|
|
||||||
return new GetBySeason().Execute(seasonId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ using api.Interfaces;
|
||||||
using data.Seasons;
|
using data.Seasons;
|
||||||
using models.Core;
|
using models.Core;
|
||||||
using models.Request;
|
using models.Request;
|
||||||
|
using AddEvent = data.Seasons.AddEvent;
|
||||||
|
|
||||||
namespace api.Services;
|
namespace api.Services;
|
||||||
|
|
||||||
|
|
@ -26,13 +27,8 @@ public class SeasonManager : ISeasonManager
|
||||||
return new GetAll().Execute();
|
return new GetAll().Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PatchSeason(PatchSeason request)
|
public void AddEventToSeason(Guid eventId, Guid seasonId)
|
||||||
{
|
{
|
||||||
new Update().Execute(request);
|
new AddEvent().Execute(eventId, seasonId);
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteSeason(Guid seasonId)
|
|
||||||
{
|
|
||||||
new Delete().Execute(seasonId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ public class TicketManager : ITicketManager
|
||||||
public void SaveMintedTicket(Ticket ticket)
|
public void SaveMintedTicket(Ticket ticket)
|
||||||
{
|
{
|
||||||
new data.Tickets.Save().Execute(ticket);
|
new data.Tickets.Save().Execute(ticket);
|
||||||
|
new data.Events.AddTicket().Execute(ticket.Id, ticket.EventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TicketSearch SearchTicket(Guid ticketId)
|
public TicketSearch SearchTicket(Guid ticketId)
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,4 @@
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Assets\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,16 @@ using MongoDB.Driver;
|
||||||
|
|
||||||
namespace data.Events;
|
namespace data.Events;
|
||||||
|
|
||||||
public class Delete
|
public class AddTicket
|
||||||
{
|
{
|
||||||
public void Execute(Guid eventId)
|
public void Execute(Guid ticketId, Guid eventId)
|
||||||
{
|
{
|
||||||
var database = MongoFactory.GetDatabase();
|
var database = MongoFactory.GetDatabase();
|
||||||
var filter = Builders<Event>.Filter.Eq(e => e.Id, eventId);
|
|
||||||
var collection = database.GetCollection<Event>("events");
|
var collection = database.GetCollection<Event>("events");
|
||||||
|
var filter = Builders<Event>.Filter.Eq(e => e.Id, eventId);
|
||||||
|
|
||||||
collection.DeleteOne(filter);
|
var update = Builders<Event>.Update.Push("TicketIds", ticketId);
|
||||||
|
|
||||||
|
collection.FindOneAndUpdate(filter, update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ public class Find
|
||||||
{
|
{
|
||||||
var database = MongoFactory.GetDatabase();
|
var database = MongoFactory.GetDatabase();
|
||||||
var collection = database.GetCollection<Event>("events");
|
var collection = database.GetCollection<Event>("events");
|
||||||
var filter = Builders<Event>.Filter.Eq(e => e.Id, eventId);
|
var filter = Builders<Event>.Filter.Eq("Id", eventId);
|
||||||
return collection.Find(filter).FirstOrDefault();
|
return collection.Find(filter).FirstOrDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
using models.Core;
|
|
||||||
using MongoDB.Driver;
|
|
||||||
|
|
||||||
namespace data.Events;
|
|
||||||
|
|
||||||
public class GetBySeason
|
|
||||||
{
|
|
||||||
public List<Event> Execute(Guid seasonId)
|
|
||||||
{
|
|
||||||
var database = MongoFactory.GetDatabase();
|
|
||||||
var collection = database.GetCollection<Event>("events");
|
|
||||||
var filter = Builders<Event>.Filter.Eq(e => e.SeasonId, seasonId);
|
|
||||||
|
|
||||||
return collection.Find(filter).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,24 +6,23 @@ namespace data.Events;
|
||||||
|
|
||||||
public class Update
|
public class Update
|
||||||
{
|
{
|
||||||
public void Execute(PatchEvent request)
|
public bool Execute(PatchEvent request)
|
||||||
{
|
{
|
||||||
var database = MongoFactory.GetDatabase();
|
var database = MongoFactory.GetDatabase();
|
||||||
var collection = database.GetCollection<Event>("events");
|
var collection = database.GetCollection<Event>("events");
|
||||||
|
|
||||||
var filter = Builders<Event>.Filter.Eq(e => e.Id, request.Id);
|
var filter = Builders<Event>.Filter.Eq("_id", request.Id);
|
||||||
|
|
||||||
var newEvent = new Event
|
var newEvent = new Event
|
||||||
{
|
{
|
||||||
Id = request.Id,
|
Id = request.Id,
|
||||||
SeasonId = request.SeasonId,
|
|
||||||
Date = request.Date,
|
Date = request.Date,
|
||||||
Name = request.Name,
|
Name = request.Name,
|
||||||
Description = request.Description,
|
Description = request.Description,
|
||||||
Venue = request.Venue,
|
Venue = request.Venue,
|
||||||
Talent = request.Talent,
|
Talent = request.Talent,
|
||||||
};
|
};
|
||||||
|
|
||||||
collection.ReplaceOne(filter, newEvent);
|
return collection.ReplaceOne(filter, newEvent).IsAcknowledged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,14 +3,16 @@ using MongoDB.Driver;
|
||||||
|
|
||||||
namespace data.Seasons;
|
namespace data.Seasons;
|
||||||
|
|
||||||
public class Delete
|
public class AddEvent
|
||||||
{
|
{
|
||||||
public void Execute(Guid seasonId)
|
public void Execute(Guid eventId, Guid seasonId)
|
||||||
{
|
{
|
||||||
var database = MongoFactory.GetDatabase();
|
var database = MongoFactory.GetDatabase();
|
||||||
var filter = Builders<Season>.Filter.Eq(s => s.Id, seasonId);
|
|
||||||
var collection = database.GetCollection<Season>("seasons");
|
var collection = database.GetCollection<Season>("seasons");
|
||||||
|
var filter = Builders<Season>.Filter.Eq(s => s.Id, seasonId);
|
||||||
|
|
||||||
collection.DeleteOne(filter);
|
var update = Builders<Season>.Update.Push("EventIds", eventId);
|
||||||
|
|
||||||
|
collection.FindOneAndUpdate(filter, update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
using models.Core;
|
|
||||||
using models.Request;
|
|
||||||
using MongoDB.Driver;
|
|
||||||
|
|
||||||
namespace data.Seasons;
|
|
||||||
|
|
||||||
public class Update
|
|
||||||
{
|
|
||||||
public void Execute(PatchSeason request)
|
|
||||||
{
|
|
||||||
var database = MongoFactory.GetDatabase();
|
|
||||||
var collection = database.GetCollection<Season>("seasons");
|
|
||||||
|
|
||||||
var filter = Builders<Season>.Filter.Eq(s => s.Id, request.Id);
|
|
||||||
|
|
||||||
var newSeason = new Season
|
|
||||||
{
|
|
||||||
Id = request.Id,
|
|
||||||
Name = request.Name,
|
|
||||||
Description = request.Description,
|
|
||||||
StartDate = request.StartDate,
|
|
||||||
EndDate = request.EndDate,
|
|
||||||
};
|
|
||||||
|
|
||||||
collection.ReplaceOne(filter, newSeason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,4 +9,5 @@ public class Event
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public required Venue Venue { get; set; }
|
public required Venue Venue { get; set; }
|
||||||
public required Talent Talent { get; set; }
|
public required Talent Talent { get; set; }
|
||||||
|
public List<Guid> TicketIds { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
@ -7,4 +7,5 @@ public class Season
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public DateTime StartDate { get; set; }
|
public DateTime StartDate { get; set; }
|
||||||
public DateTime EndDate { get; set; }
|
public DateTime EndDate { get; set; }
|
||||||
|
public List<Guid> EventIds { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ namespace models.Request;
|
||||||
public class PatchEvent
|
public class PatchEvent
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid SeasonId { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
public DateTime Date { get; set; }
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
namespace models.Request;
|
|
||||||
|
|
||||||
public class PatchSeason
|
|
||||||
{
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
FROM nginx:latest
|
FROM nginx:latest
|
||||||
COPY source/ticketUI/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY source/ticketUI/dist/ticket-ui /usr/share/nginx/html
|
COPY dist/ticket-ui /usr/share/nginx/html
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ticket-ui",
|
"name": "ticket-ui",
|
||||||
"version": "0.0.0",
|
"version": "2024.12.11",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export class EventBrowserComponent implements OnInit {
|
||||||
public selectedEventId$: WritableSignal<string> = signal('');
|
public selectedEventId$: WritableSignal<string> = signal('');
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.eventService.searchEvents(undefined, undefined, undefined);
|
this.eventService.searchAllEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
public click(eventId: string): void {
|
public click(eventId: string): void {
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row row-space-between row-bottom-margin">
|
<div class="row row-space-between row-bottom-margin">
|
||||||
<label class="label" for="email">Email</label>
|
<label class="label" for="email">Email</label>
|
||||||
<input id="email" [formControl]="email" type="email" class="input"
|
<input id="email" [formControl]="email" type="text" class="input"/>
|
||||||
[ngClass]="{'invalid': email.invalid && email.dirty}"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row row-space-between row-bottom-margin">
|
<div class="row row-space-between row-bottom-margin">
|
||||||
<label class="label" for="phoneNumber">Phone</label>
|
<label class="label" for="phoneNumber">Phone</label>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {FormControl, ReactiveFormsModule, Validators} from '@angular/forms';
|
import {FormControl, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {Patron} from '../../../models/core/patron';
|
import {Patron} from '../../../models/core/patron';
|
||||||
import {NgClass} from '@angular/common';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-patron-info',
|
selector: 'app-patron-info',
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule
|
||||||
NgClass
|
|
||||||
],
|
],
|
||||||
templateUrl: './patron-info.component.html',
|
templateUrl: './patron-info.component.html',
|
||||||
styleUrl: './patron-info.component.scss'
|
styleUrl: './patron-info.component.scss'
|
||||||
|
|
@ -16,9 +14,7 @@ export class PatronInfoComponent {
|
||||||
public firstName = new FormControl('', {nonNullable: true, validators: [Validators.required]});
|
public firstName = new FormControl('', {nonNullable: true, validators: [Validators.required]});
|
||||||
public middleName = new FormControl('');
|
public middleName = new FormControl('');
|
||||||
public lastName = new FormControl('', {nonNullable: true, validators: [Validators.required]});
|
public lastName = new FormControl('', {nonNullable: true, validators: [Validators.required]});
|
||||||
public email = new FormControl('', {
|
public email = new FormControl('', {nonNullable: true, validators: [Validators.required]});
|
||||||
nonNullable: true, validators: [Validators.required, Validators.email]
|
|
||||||
});
|
|
||||||
public phoneNumber = new FormControl('');
|
public phoneNumber = new FormControl('');
|
||||||
public addressOne = new FormControl('', {nonNullable: true, validators: [Validators.required]});
|
public addressOne = new FormControl('', {nonNullable: true, validators: [Validators.required]});
|
||||||
public addressTwo = new FormControl('');
|
public addressTwo = new FormControl('');
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
<div [ngClass]="{'row': ticket$() !== null}">
|
<div class="card">
|
||||||
<div class="card">
|
<h3>Generate Tickets</h3>
|
||||||
<h3>Generate Tickets</h3>
|
<div class="column">
|
||||||
<div class="column">
|
<app-ticket-type-selector #ticketType></app-ticket-type-selector>
|
||||||
<app-ticket-type-selector #ticketType></app-ticket-type-selector>
|
<app-event-browser />
|
||||||
<app-event-browser />
|
@if(IsSeasonTicket(ticketType.selectedTicketType$())) {
|
||||||
@if(IsSeasonTicket(ticketType.selectedTicketType$())) {
|
<app-season-browser />
|
||||||
<app-season-browser />
|
}
|
||||||
}
|
<app-patron-info />
|
||||||
<app-patron-info />
|
|
||||||
</div>
|
|
||||||
<button class="button" (click)="saveTicket()">Save Ticket</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<button class="button" (click)="saveTicket()">Save Ticket</button>
|
||||||
@if(ticket$() !== null) {
|
|
||||||
<app-generated-ticket />
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if(ticket$() !== null) {
|
||||||
|
<app-generated-ticket />
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ import {SeasonBrowserComponent} from '../../components/season-browser/season-bro
|
||||||
import {IsSeasonTicket} from '../../../models/enums/ticket-type.enum';
|
import {IsSeasonTicket} from '../../../models/enums/ticket-type.enum';
|
||||||
import {GeneratedTicketComponent} from '../../components/generated-ticket/generated-ticket.component';
|
import {GeneratedTicketComponent} from '../../components/generated-ticket/generated-ticket.component';
|
||||||
import {Guid} from 'guid-typescript';
|
import {Guid} from 'guid-typescript';
|
||||||
import {SnackbarService} from '../../services/snackbar.service';
|
|
||||||
import {NgClass} from '@angular/common';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-ticket',
|
selector: 'app-ticket',
|
||||||
|
|
@ -18,8 +16,7 @@ import {NgClass} from '@angular/common';
|
||||||
TicketTypeSelectorComponent,
|
TicketTypeSelectorComponent,
|
||||||
PatronInfoComponent,
|
PatronInfoComponent,
|
||||||
SeasonBrowserComponent,
|
SeasonBrowserComponent,
|
||||||
GeneratedTicketComponent,
|
GeneratedTicketComponent
|
||||||
NgClass
|
|
||||||
],
|
],
|
||||||
templateUrl: './ticket.component.html',
|
templateUrl: './ticket.component.html',
|
||||||
styleUrl: './ticket.component.scss'
|
styleUrl: './ticket.component.scss'
|
||||||
|
|
@ -31,15 +28,9 @@ export class TicketComponent {
|
||||||
@ViewChild(SeasonBrowserComponent) seasonBrowserComponent!: SeasonBrowserComponent;
|
@ViewChild(SeasonBrowserComponent) seasonBrowserComponent!: SeasonBrowserComponent;
|
||||||
|
|
||||||
public ticketService = inject(TicketService);
|
public ticketService = inject(TicketService);
|
||||||
public snackBar = inject(SnackbarService);
|
|
||||||
public ticket$ = this.ticketService.mintResponse$;
|
public ticket$ = this.ticketService.mintResponse$;
|
||||||
|
|
||||||
public saveTicket(): void {
|
public saveTicket(): void {
|
||||||
if(this.patronInfoComponent.email.invalid && this.patronInfoComponent.email.dirty) {
|
|
||||||
this.snackBar.showMessage("Please enter a valid email address");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addTicket: AddTicket = {
|
const addTicket: AddTicket = {
|
||||||
eventId: this.eventBrowserComponent.selectedEventId,
|
eventId: this.eventBrowserComponent.selectedEventId,
|
||||||
type: this.ticketTypeSelector.selectedTicketType,
|
type: this.ticketTypeSelector.selectedTicketType,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,31 @@
|
||||||
import {HttpHeaders} from '@angular/common/http';
|
import {HttpHeaders, HttpParams} from '@angular/common/http';
|
||||||
|
|
||||||
export class ApiUtils {
|
export class ApiUtils {
|
||||||
protected setHttpRequestOptions(): any {
|
protected setHttpRequestOptions(params?: any): any {
|
||||||
let headers: HttpHeaders = new HttpHeaders();
|
let headers: HttpHeaders = new HttpHeaders();
|
||||||
headers = headers.set('Content-Type', 'application/json');
|
headers = headers.set('Content-Type', 'application/json');
|
||||||
|
|
||||||
return {
|
const options: any = {
|
||||||
headers,
|
headers,
|
||||||
observe: 'response'
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import {Endpoints} from '../../models/endpoints';
|
||||||
import {catchError, map, of} from 'rxjs';
|
import {catchError, map, of} from 'rxjs';
|
||||||
import {SnackbarService} from './snackbar.service';
|
import {SnackbarService} from './snackbar.service';
|
||||||
import {Event} from '../../models/core/event';
|
import {Event} from '../../models/core/event';
|
||||||
import {PatchEvent} from '../../models/request/patch-event';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|
@ -22,6 +21,7 @@ export class EventService extends ApiUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public addEvent(request: AddEventRequest): void {
|
public addEvent(request: AddEventRequest): void {
|
||||||
|
//TODO: Remove hard coded venue and talent information
|
||||||
const options = this.setHttpRequestOptions();
|
const options = this.setHttpRequestOptions();
|
||||||
const url = environment.apiBase + Endpoints.EVENT;
|
const url = environment.apiBase + Endpoints.EVENT;
|
||||||
|
|
||||||
|
|
@ -34,30 +34,14 @@ export class EventService extends ApiUtils {
|
||||||
).subscribe(res => {
|
).subscribe(res => {
|
||||||
if(res !== null) {
|
if(res !== null) {
|
||||||
this.snackbar.showMessage('Event added successfully.');
|
this.snackbar.showMessage('Event added successfully.');
|
||||||
this.searchEvents(undefined, undefined, undefined);
|
this.searchAllEvents();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public searchAllEvents(): void {
|
||||||
* Searches for events, returns all events if parameters are not provided
|
|
||||||
* @param startDate If provided with an end date, will search in the date range
|
|
||||||
* @param endDate If provided with a start date, will search in the date range
|
|
||||||
* @param seasonId If provided, returns all events for a season
|
|
||||||
*/
|
|
||||||
public searchEvents(startDate: Date | undefined,
|
|
||||||
endDate: Date | undefined,
|
|
||||||
seasonId: string | undefined): void {
|
|
||||||
const options = this.setHttpRequestOptions();
|
const options = this.setHttpRequestOptions();
|
||||||
let url: string = '';
|
const url = environment.apiBase + Endpoints.EVENT;
|
||||||
|
|
||||||
if (startDate && endDate) {
|
|
||||||
url = environment.apiBase + Endpoints.EVENT_DATE_SEARCH(startDate, endDate);
|
|
||||||
} else if (seasonId) {
|
|
||||||
url = environment.apiBase + Endpoints.EVENT_SEASON_SEARCH(seasonId);
|
|
||||||
} else {
|
|
||||||
url = environment.apiBase + Endpoints.EVENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.httpClient.get(url, options)
|
this.httpClient.get(url, options)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
|
@ -67,47 +51,23 @@ export class EventService extends ApiUtils {
|
||||||
return of(undefined);
|
return of(undefined);
|
||||||
})
|
})
|
||||||
).subscribe(res => {
|
).subscribe(res => {
|
||||||
if(res !== null) {
|
this.events$.set(res);
|
||||||
this.events$.set(res);
|
|
||||||
} else {
|
|
||||||
this.snackbar.showError('No events found.');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateEvent(request: PatchEvent): void {
|
public searchEvents(startDate: Date, endDate: Date): void {
|
||||||
const options = this.setHttpRequestOptions();
|
const options = this.setHttpRequestOptions();
|
||||||
const url = environment.apiBase + Endpoints.EVENT;
|
const url = environment.apiBase + Endpoints.EVENT_DATE_SEARCH(startDate, endDate);
|
||||||
|
|
||||||
this.httpClient.patch(url, JSON.stringify(request), options)
|
this.httpClient.get(url, options)
|
||||||
.pipe(
|
.pipe(
|
||||||
|
map((response: any) => response.body),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
this.snackbar.showError(error.error);
|
this.snackbar.showError(error.error);
|
||||||
return of(undefined);
|
return of(undefined);
|
||||||
})
|
})
|
||||||
).subscribe(res => {
|
).subscribe(res => {
|
||||||
if(res !== null) {
|
this.events$.set(res);
|
||||||
this.snackbar.showMessage('Event updated successfully.');
|
});
|
||||||
this.searchEvents(undefined, undefined, undefined);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public deleteEvent(eventId: string): void {
|
|
||||||
const options = this.setHttpRequestOptions();
|
|
||||||
const url = environment.apiBase + Endpoints.EVENT_DELETE(eventId);
|
|
||||||
|
|
||||||
this.httpClient.delete(url, options)
|
|
||||||
.pipe(
|
|
||||||
catchError(error => {
|
|
||||||
this.snackbar.showError(error.error);
|
|
||||||
return of(undefined);
|
|
||||||
})
|
|
||||||
).subscribe(res => {
|
|
||||||
if(res !== null) {
|
|
||||||
this.snackbar.showMessage('Event deleted successfully.');
|
|
||||||
this.searchEvents(undefined, undefined, undefined);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import {environment} from '../../environments/environment';
|
||||||
import {Endpoints} from '../../models/endpoints';
|
import {Endpoints} from '../../models/endpoints';
|
||||||
import {catchError, map, of} from 'rxjs';
|
import {catchError, map, of} from 'rxjs';
|
||||||
import {AddSeason} from '../../models/request/add-season';
|
import {AddSeason} from '../../models/request/add-season';
|
||||||
import {PatchSeason} from '../../models/request/patch-season';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
|
@ -40,7 +39,7 @@ export class SeasonService extends ApiUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveSeason(request: AddSeason): void {
|
public saveSeason(request: AddSeason): void {
|
||||||
const options = this.setHttpRequestOptions();
|
const options = this.setHttpRequestOptions(request);
|
||||||
const url = environment.apiBase + Endpoints.SEASON;
|
const url = environment.apiBase + Endpoints.SEASON;
|
||||||
|
|
||||||
this.httpClient.post(url, JSON.stringify(request), options)
|
this.httpClient.post(url, JSON.stringify(request), options)
|
||||||
|
|
@ -69,40 +68,4 @@ export class SeasonService extends ApiUtils {
|
||||||
})
|
})
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateSeason(request: PatchSeason): void {
|
|
||||||
const options = this.setHttpRequestOptions();
|
|
||||||
const url = environment.apiBase + Endpoints.SEASON;
|
|
||||||
|
|
||||||
this.httpClient.patch(url, JSON.stringify(request), options)
|
|
||||||
.pipe(
|
|
||||||
catchError(error => {
|
|
||||||
this.snackbar.showError(error.error);
|
|
||||||
return of(undefined);
|
|
||||||
})
|
|
||||||
).subscribe(res => {
|
|
||||||
if (res !== null) {
|
|
||||||
this.snackbar.showMessage('Season updated successfully.');
|
|
||||||
this.getSeasons();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public deleteSeason(seasonId: string): void {
|
|
||||||
const options = this.setHttpRequestOptions();
|
|
||||||
const url = environment.apiBase + Endpoints.SEASON_DELETE(seasonId);
|
|
||||||
|
|
||||||
this.httpClient.delete(url, options)
|
|
||||||
.pipe(
|
|
||||||
catchError(error => {
|
|
||||||
this.snackbar.showError(error.error);
|
|
||||||
return of(undefined);
|
|
||||||
})
|
|
||||||
).subscribe(res => {
|
|
||||||
if (res !== null) {
|
|
||||||
this.snackbar.showMessage('Season deleted successfully.');
|
|
||||||
this.getSeasons();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
apiBase: 'http://localhost:5168/api/',
|
apiBase: 'http://localhost:5168/',
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,6 @@ export interface Event {
|
||||||
name: string,
|
name: string,
|
||||||
description: string | null,
|
description: string | null,
|
||||||
venue: Venue,
|
venue: Venue,
|
||||||
talent: Talent
|
talent: Talent,
|
||||||
|
ticketIds: Array<string>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,6 @@ export class Endpoints {
|
||||||
return `${Endpoints.EVENT}?startDate=${startDate}&endDate=${endDate}`;
|
return `${Endpoints.EVENT}?startDate=${startDate}&endDate=${endDate}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EVENT_SEASON_SEARCH(seasonId: string): string {
|
|
||||||
return `${Endpoints.EVENT}?seasonId=${seasonId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EVENT_DELETE(eventId: string): string {
|
|
||||||
return `${Endpoints.EVENT}?eventId=${eventId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Season Routes */
|
/* Season Routes */
|
||||||
public static readonly SEASON: string = 'season';
|
public static readonly SEASON: string = 'season';
|
||||||
|
|
||||||
|
|
@ -37,8 +29,4 @@ export class Endpoints {
|
||||||
public static SEASON_ADD_EVENT(eventId: string, seasonId: string): string {
|
public static SEASON_ADD_EVENT(eventId: string, seasonId: string): string {
|
||||||
return `${Endpoints.SEASON}?eventId=${eventId}&seasonId=${seasonId}`;
|
return `${Endpoints.SEASON}?eventId=${eventId}&seasonId=${seasonId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SEASON_DELETE(seasonId: string): string {
|
|
||||||
return `${Endpoints.SEASON}?seasonId=${seasonId}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
export interface PatchSeason {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
description: string | null,
|
|
||||||
startDate: Date,
|
|
||||||
endDate: Date
|
|
||||||
}
|
|
||||||
|
|
@ -58,7 +58,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
padding: 7px;
|
padding: 5px;
|
||||||
backdrop-filter: blur(25px) saturate(112%);
|
backdrop-filter: blur(25px) saturate(112%);
|
||||||
-webkit-backdrop-filter: blur(25px) saturate(112%);
|
-webkit-backdrop-filter: blur(25px) saturate(112%);
|
||||||
background-color: rgba(255, 255, 255, 0.11);
|
background-color: rgba(255, 255, 255, 0.11);
|
||||||
|
|
@ -99,7 +99,3 @@ body {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.invalid {
|
|
||||||
border: 2px solid rgba(255, 43, 43, 0.5);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user