Browse Source

Ability to add a new Race to a season

old-project-state
Dan 2 years ago
parent
commit
f690417438
  1. 167
      server.js
  2. 4
      src/app/app.module.ts
  3. 2
      src/app/components/login/login.component.ts
  4. 12
      src/app/components/new-race-dialog/new-race-dialog.component.html
  5. 0
      src/app/components/new-race-dialog/new-race-dialog.component.less
  6. 21
      src/app/components/new-race-dialog/new-race-dialog.component.spec.ts
  7. 32
      src/app/components/new-race-dialog/new-race-dialog.component.ts
  8. 6
      src/app/components/season-card/season-card.component.html
  9. 17
      src/app/components/season-details/season-details.component.html
  10. 48
      src/app/components/season-details/season-details.component.ts
  11. 1
      src/app/components/seasons/seasons.component.ts
  12. 15
      src/app/models/season.ts
  13. 9
      src/app/services/api.service.ts

167
server.js

@ -21,9 +21,9 @@ class Season extends Document {
constructor() {
super();
this.name = String;
this.subtitle = String;
this.seasonStartDate = Date;
this.title = String;
this.subTitle = String;
this.seasonStartDate = String;
}
}
@ -33,8 +33,8 @@ class Race extends Document {
super();
this.weekNumber = Number;
this.raceStartDate = Date;
this.raceEndDate = Date;
this.startDate = Date;
this.endDate = Date;
this.mapName = String;
this.mapLink = String;
this.season = Season;
@ -53,7 +53,7 @@ class RaceResult extends Document {
}
}
connect('nedb://./nedb-database.db');
connect('nedb://./nedb-database');
const fs = require('fs');
const app = express();
@ -70,77 +70,26 @@ app.use(bodyParser.json());
const RSA_PRIVATE_KEY = "Secret_KeY";
const RSA_PUBLIC_KEY = fs.readFileSync('./public.key');
const users = [
{ id: 1, username: 'user1', password: 'password1' },
{ id: 2, username: 'user2', password: 'password2' }
];
const seasons = [
{ id: 1, seasonName: "Season 1", seasonTag: "Post Winter Blues", seasonCardImage: "", seasonHeaderImage: "", seasonStartDate: "", seasonEndDate: "", seasonDesc: "",},
{ id: 2, seasonName: "Season 2", seasonTag: "Post Winter Blues", seasonCardImage: "", seasonHeaderImage: "", seasonStartDate: "", seasonEndDate: "", seasonDesc: "",}
]
const season_details = {
details: {
id: 1,
seasonName: "Season 1",
seasonTag: "Post Winter Blues",
seasonCardImage: "",
seasonHeaderImage: "",
seasonStartDate: "",
seasonSendDate: "",
seasonId: "",
seasonDesc: "",
},
standings: [{
position: 1,
points: 4,
user: {
realName: "Dan H",
gamerHandle: "Quildra",
}
},
{
position: 2,
points: 2,
user: {
realName: "Dan Mc",
gamerHandle: "Mini-Quildra",
}
},
],
weeks:[{
id: "1",
map: "bob",
mapImg: "bob.jpg",
entries: [
{
position: 1,
runTime: 4.0,
user: {
realName: "Dan H",
gamerHandle: "Quildra",
}
}
]
}]
}
function verifyToken(req, res, next) {
const token = req.headers['authorization'];
console.log(token);
if (!token) {
return res.status(403).json({ message: 'Token not provided' });
if (!token || !token.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Unauthorized' });
}
jwt.verify(token, RSA_PRIVATE_KEY, (err, decoded) => {
const tokenWithoutBearer = token.slice(7); // Remove 'Bearer ' prefix
jwt.verify(tokenWithoutBearer, RSA_PRIVATE_KEY, (err, decoded) => {
if (err) {
return res.status(401).json({ message: 'Failed to authenticate token',
error: err});
console.error(err);
return res.status(401).json({ message: 'Invalid token' });
}
req.userId = decoded.userId;
req.user = decoded.user;
next();
});
}
@ -156,33 +105,99 @@ app.post('/api/login', (req, res) => {
}
// Generate and return a JWT token
const token = jwt.sign({ userId: user.id, username: user.username }, RSA_PRIVATE_KEY, { expiresIn: '1h' });
const token = jwt.sign({ userId: user.id }, RSA_PRIVATE_KEY, { expiresIn: '1h', algorithm: 'HS256' });
console.log(token);
res.json({ token });
});
// Protected route example
app.get('/api/profile', verifyToken, (req, res) => {
const user = users.find(u => u.id === req.userId);
res.json({ username: user.username, userId: user.id });
});
app.get('/api/seasons', async (req, res) => {
const seasons = await Season.find();
res.json({seasons: seasons});
});
app.get('/api/seasons/:id', (req, res) => {
res.json({data: season_details});
function remapRaceInfo(raceRecord) {
modableRaceObject = {
weekNumber: raceRecord.weekNumber,
startDate: raceRecord.startDate,
endDate: raceRecord.endDate,
mapName: raceRecord.mapName,
mapLink: raceRecord.mapLink,
season: raceRecord.season,
results: []
}
return modableRaceObject;
}
app.get('/api/seasons/:id', async (req, res) => {
const { id } = req.params;
try {
let races = []
let racersInSeason = [];
const seasonDetails = await Season.findOne({_id: id});
const seasonRaceDetails = await Race.find({season: id});
for (let i = 0; i < seasonRaceDetails.length; i++) {
let raceRecord = seasonRaceDetails[i];
let modableRace = remapRaceInfo(raceRecord);
const fastestTimesMap = new Map();
const raceResults = await RaceResult.find('racer', { 'race._id': raceRecord._id });
raceResults.forEach(result => {
const racerId = result.racer._id;
const timeInSeconds = result.timeInSeconds;
if (!fastestTimesMap.has(racerId) || timeInSeconds < fastestTimesMap.get(racerId)) {
fastestTimesMap.set(racerId, timeInSeconds);
}
});
modableRace.results = Array.from(fastestTimesMap.entries()).map(([racerId, timeInSeconds]) => ({
racerId,
timeInSeconds
}));
// This is wrong it will get only the racers the in last race it processes.
racersInSeason = Array.from(new Set(raceResults.map(result => result.racer)));
races.push(modableRace);
}
let i = 1;
let standings = []
racersInSeason.forEach((racer) => {
let entry = {}
entry._id = i;
entry.position = i;
entry.points = 100-i;
entry.user = racer;
standings.push(entry);
});
res.json({season: seasonDetails, races: races, standings: standings});
}
catch( error ) {
console.error(error)
res.status(500).send('Internal Server Error');
}
});
app.post('/api/seasons/add', verifyToken, async (req, res) => {
console.log("New Seaosn Time");
const { name, subtitle, seasonStartDate } = req.body;
const season = await Season.create({ name, subtitle, seasonStartDate });
const season = await Season.create({ name, subtitle, seasonStartDate }).save();
console.log(season);
res.json(season);
});
app.post('/api/race/add', verifyToken, async (req, res) => {
const { seasonId, startDate, endDate, mapName, mapLink, weekNumber } = req.body;
const season = await Season.findOne({_id: seasonId});
const race = await Race.create({ weekNumber, startDate, endDate, mapName, mapLink, season })
console.log(race);
race.save();
res.json(race);
});
// route for handling requests from the Angular client
app.post('/api/upload-replay', upload.single('file'), (req, res) => {
let file = req.file;

4
src/app/app.module.ts

@ -31,6 +31,7 @@ import { NewSeasonDialogComponent } from './components/new-season-dialog/new-sea
import { MatMenuModule } from '@angular/material/menu';
import { FormsModule } from '@angular/forms';
import { MatDialogModule } from '@angular/material/dialog';
import { NewRaceDialogComponent } from './components/new-race-dialog/new-race-dialog.component';
@NgModule({
declarations: [
@ -47,7 +48,8 @@ import { MatDialogModule } from '@angular/material/dialog';
LoginComponent,
AdminComponent,
NewSeasonCardComponent,
NewSeasonDialogComponent
NewSeasonDialogComponent,
NewRaceDialogComponent
],
imports: [
BrowserModule,

2
src/app/components/login/login.component.ts

@ -23,7 +23,7 @@ export class LoginComponent {
(response) => {
if (response.token) {
localStorage.setItem('token', response.token);
this.router.navigate(['/profile']);
this.router.navigate(['/']);
}
},
(error) => console.error(error)

12
src/app/components/new-race-dialog/new-race-dialog.component.html

@ -0,0 +1,12 @@
<h1 mat-dialog-title>New Season</h1>
<div mat-dialog-content>
<p>Map Name: <input [(ngModel)]="data.mapName" /></p>
<p>Link: <input [(ngModel)]="data.mapLink" /></p>
<p>Starting Date: <input type="date" [(ngModel)]="data.startDate" /></p>
<p>End Date: <input type="date" [(ngModel)]="data.endDate" /></p>
<p>Week#: <input type="number" [(ngModel)]="data.weekNumber" /></p>
</div>
<div mat-dialog-actions>
<button mat-button (click)="onCancelClick()">Cancel</button>
<button mat-button color="primary" (click)="onSaveClick()">Save</button>
</div>

0
src/app/components/new-race-dialog/new-race-dialog.component.less

21
src/app/components/new-race-dialog/new-race-dialog.component.spec.ts

@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NewRaceDialogComponent } from './new-race-dialog.component';
describe('NewRaceDialogComponent', () => {
let component: NewRaceDialogComponent;
let fixture: ComponentFixture<NewRaceDialogComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [NewRaceDialogComponent]
});
fixture = TestBed.createComponent(NewRaceDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

32
src/app/components/new-race-dialog/new-race-dialog.component.ts

@ -0,0 +1,32 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { ApiService } from 'src/app/services/api.service';
@Component({
selector: 'app-new-race-dialog',
templateUrl: './new-race-dialog.component.html',
styleUrls: ['./new-race-dialog.component.less']
})
export class NewRaceDialogComponent {
constructor(
public dialogRef: MatDialogRef<NewRaceDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private apiService: ApiService
) { }
ngOnInit(): void {
}
onCancelClick(): void {
this.dialogRef.close();
}
onSaveClick(): void {
this.apiService.addRace(
this.data.seasonId, this.data.startDate, this.data.endDate, this.data.mapName, this.data.mapLink, this.data.weekNumber
).subscribe(data => {
console.log(data);
});
this.dialogRef.close(this.data);
}
}

6
src/app/components/season-card/season-card.component.html

@ -1,8 +1,8 @@
<mat-card class="example-card" routerLink="/seasons/{{season?.id}}">
<mat-card class="example-card" routerLink="/seasons/{{season?._id}}">
<mat-card-header>
<div mat-card-avatar class="example-header-image"></div>
<mat-card-title>{{season?.seasonName}}</mat-card-title>
<mat-card-subtitle>{{season?.seasonTag}}</mat-card-subtitle>
<mat-card-title>{{season?.title}}</mat-card-title>
<mat-card-subtitle>{{season?.subTitle}}</mat-card-subtitle>
</mat-card-header>
<img mat-card-image src="https://material.angular.io/assets/img/examples/shiba2.jpg" alt="Photo of a Shiba Inu">
<mat-card-content>

17
src/app/components/season-details/season-details.component.html

@ -1,12 +1,17 @@
<div *ngIf="seasonInfo && seasonStandings">
<h1>{{seasonInfo.seasonName}}</h1>
<h3>{{seasonInfo.seasonTag}}</h3>
<div *ngIf="seasonInfo">
<h1>{{seasonInfo.title}}</h1>
<h3>{{seasonInfo.subTitle}}</h3>
<div *ngIf="isAuthenticated">
<button (click)="openNewRaceDialog()">New Race</button>
</div>
<mat-divider></mat-divider>
<br/>
<mat-tab-group>
<mat-tab label="Standings">
<app-season-standings-table [dataSource]="seasonStandings"></app-season-standings-table>
</mat-tab>
<div *ngIf="seasonStandings">
<mat-tab label="Standings">
<app-season-standings-table [dataSource]="seasonStandings"></app-season-standings-table>
</mat-tab>
</div>
<div *ngFor="let week of weeks">
<mat-tab label="Second">
<app-weekly-standings-table [dataSource]="week.entries"></app-weekly-standings-table>

48
src/app/components/season-details/season-details.component.ts

@ -1,7 +1,10 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { ApiService } from '../../services/api.service';
import { Season, SeasonWeek, SeasonStandingsEntry } from '../../models/season';
import { AuthService } from 'src/app/services/auth-service';
import { NewRaceDialogComponent } from '../new-race-dialog/new-race-dialog.component';
@Component({
selector: 'app-season-details',
@ -13,23 +16,58 @@ export class SeasonDetailsComponent {
seasonStandings?: SeasonStandingsEntry[];
weeks?: SeasonWeek[];
newRaceDetails = {
seasonId: '',
mapName: '',
mapImgUrl: '',
startDate: '',
endDate: '',
};
constructor(
private route: ActivatedRoute,
private apiService: ApiService,
//private location: Location
private authService: AuthService,
public dialog: MatDialog
)
{
}
isAuthenticated: boolean = false;
ngOnInit(): void {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.isAuthenticated = this.authService.isAuthenticated();
const id = String(this.route.snapshot.paramMap.get('id'));
this.apiService.getSeasonDetails(id).subscribe(data => {
this.seasonInfo = (data as any).data.details;
this.weeks = (data as any).data.weeks;
this.seasonStandings = (data as any).data.standings;
this.seasonInfo = (data as any).season;
this.weeks = (data as any).races;
this.seasonStandings = (data as any).standings;
console.log(data);
console.log(this.seasonInfo);
console.log(this.seasonStandings);
console.log(this.weeks);
});
}
openNewRaceDialog(): void {
if(this.seasonInfo)
{
this.newRaceDetails.seasonId = this.seasonInfo._id;
}
const dialogRef = this.dialog.open(NewRaceDialogComponent, {
width: '400px',
data: { ...this.newRaceDetails }
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
console.log('Follow details saved:', result);
// You can update your data or perform other actions here
} else {
console.log('Dialog canceled');
}
});
}
}

1
src/app/components/seasons/seasons.component.ts

@ -23,6 +23,7 @@ export class SeasonsComponent
ngOnInit() {
this.apiService.getSeasons().subscribe(data => {
this.seasons = (data as any).seasons;
console.log(this.seasons);
});
this.isAuthenticated = this.authService.isAuthenticated();
}

15
src/app/models/season.ts

@ -1,31 +1,32 @@
export interface Season {
id: string,
seasonName: string;
seasonTag: string;
seasonCardImage: string;
seasonHeaderImage: string;
_id: string,
title: string;
subTitle: string;
startDate: string;
}
export interface BridgeUser {
id: string;
_id: string;
realName: string;
gameHandle: string;
}
export interface SeasonStandingsEntry {
_id: string;
position: string;
points: number;
user: BridgeUser;
}
export interface SeasonWeekEntry {
_id: string;
position: string;
runTime: string;
user: BridgeUser;
}
export interface SeasonWeek {
id: string;
_id: string;
seasonId: string;
mapName: string;
mapImg: string;

9
src/app/services/api.service.ts

@ -30,7 +30,7 @@ export class ApiService
this.server_route+'/api/seasons');
}
getSeasonDetails(id: number)
getSeasonDetails(id: string)
{
return this.http.get(
this.server_route+'/api/seasons/'+id);
@ -38,8 +38,13 @@ export class ApiService
addSeason(title: string, subtitle: string, startDate: Date)
{
console.log("hullo");
return this.http.post<{title: string, subtitle: string, startDate: Date}>(this.server_route+'/api/seasons/add', { title, subtitle, startDate },
{headers: { authorization: `Bearer ${localStorage.getItem('token')}` },});
}
addRace(seasonId: string, startDate: Date, endDate: Date, mapName: string, mapLink: string, weekNumber: Number)
{
return this.http.post<{title: string, subtitle: string, startDate: Date}>(this.server_route+'/api/race/add', { seasonId, startDate, endDate, mapName, mapLink, weekNumber },
{headers: { authorization: `Bearer ${localStorage.getItem('token')}` },});
}
}
Loading…
Cancel
Save