From bbbaa64587abf44650308e31d7d7421d9ab71a2e Mon Sep 17 00:00:00 2001 From: Max Arnold Date: Tue, 10 Jan 2023 01:24:05 -0600 Subject: [PATCH] fix: :bug: fix nation popups so that they show up correctly every time fix nation popups, add memoizer pipe, add rankings for teams in awards tab --- README.md | 22 ++++- src/app/models/nation.model.ts | 40 +++++++- .../simulation-qualifiers.service.ts | 6 +- .../simulation/simulation.component.html | 21 +--- .../simulation/simulation.component.scss | 2 +- .../pages/simulation/simulation.component.ts | 4 +- src/app/pages/simulation/simulation.model.ts | 7 +- .../pages/simulation/simulation.service.ts | 20 +++- src/app/pages/simulation/simulation.utils.ts | 28 +++++- .../components/awards/awards.component.html | 34 +++++-- .../components/awards/awards.component.scss | 59 ++++++++--- .../components/awards/awards.component.ts | 47 ++++++++- .../knockout-stage.component.html | 56 ++++------- .../nation-dialog.component.html | 10 +- .../nation-dialog/nation-dialog.component.ts | 99 +++++++++---------- .../tournament-form.component.html | 27 ++--- .../tournament-form.component.scss | 5 + .../tournament-form.component.ts | 52 ++++------ src/app/shared/pipes/memoizer.pipe.spec.ts | 8 ++ src/app/shared/pipes/memoizer.pipe.ts | 27 +++++ src/app/shared/shared.module.ts | 3 +- src/styles.scss | 10 -- 22 files changed, 384 insertions(+), 203 deletions(-) create mode 100644 src/app/shared/pipes/memoizer.pipe.spec.ts create mode 100644 src/app/shared/pipes/memoizer.pipe.ts diff --git a/README.md b/README.md index 740588d..21d66b9 100644 --- a/README.md +++ b/README.md @@ -109,11 +109,29 @@ firebase deploy ## CSS Styling Guide -We use BEM methodology for all CSS files. Please refer to this [reference site](https://en.bem.info/methodology/key-concepts/) for help. For this project, we have some specific rules to follow over the BEM standards. +We use BEM methodology for all SCSS files. Please refer to this [reference site](https://en.bem.info/methodology/key-concepts/) for help. For this project, we have some specific rules to follow over the BEM standards. * Blocks can be contained within other blocks. * Blocks can be modified just like elements using a class like: `block--modifier`. * The class name of elements within elements should be added on. `element__element__element`. -* Some styles will have to be overwritten with `mat` or `cdk` classes. * Global styles should only be implemented in `styles.scss`. * Don't add class names to `ng-container`, `ng-template` and other specialized html tags unless necessary. + +## Project Structure + +### Core +The Core Module is where we want to put our shared singleton services. So the services that we want only one instance of while having them shared among multiple modules should live here. + +The Angular injector creates a new instance of a service for each lazily loaded module it is provided. + +Another piece of our application that should live in the Core Modules is app-level components. A good example of an app-level component would be the navigation bar. Only the app needs to know about our navigation component. + +We do not want to put, are components used throughout the application inside of the Core Module. We have the Shared Module for that and we will look at that now. +### Models + +### Pages + +### Shared +The Shared Module is where we want everything to live that is shared throughout the application. Components, directives, guards, & pipes can all live in the Shared Module. + +It is also common to import and export Angular built modules inside your Shared Module if you need to access them in multiple locations. Because Shared is imported into many of your Feature Modules, it's common to import/export Common Module or Angular Material modules. Import/Export the modules once in Shared Module and now anyone that imports Shared will have access to them. diff --git a/src/app/models/nation.model.ts b/src/app/models/nation.model.ts index 9941ca4..0acec75 100644 --- a/src/app/models/nation.model.ts +++ b/src/app/models/nation.model.ts @@ -49,6 +49,42 @@ export interface GroupTeam { midRanking: number; defRanking: number; pot?: number; - grade?: string; - tournamentFinish?: string; + reportCard: { + grade: string | null; + tournamentFinish: string | null; + gradeStyle: string | null; + gradeSummary: string | null; + }; } + +export const defaultHost: GroupTeam = { + name: 'qatar', + abbreviation: 'qat', + logo: 'https://fmdataba.com/images/n/QAT.svg', + region: 'afc', + points: 0, + gDiff: 0, + gFor: 0, + gOpp: 0, + tier: 'j', + attRating: 0, + midRating: 0, + defRating: 0, + rating: 0, + matchesPlayed: 0, + matchHistory: { + qualifiers: [], + group: [], + bracket: [], + }, + reportCard: { + grade: null, + gradeStyle: null, + gradeSummary: null, + tournamentFinish: null, + }, + ranking: 0, + attRanking: 0, + midRanking: 0, + defRanking: 0, +}; diff --git a/src/app/pages/simulation/simulation-qualifiers.service.ts b/src/app/pages/simulation/simulation-qualifiers.service.ts index 221fdb1..cd21be3 100644 --- a/src/app/pages/simulation/simulation-qualifiers.service.ts +++ b/src/app/pages/simulation/simulation-qualifiers.service.ts @@ -123,11 +123,13 @@ export class SimulationQualifiersService { const extraSpots = Math.round(nationsInRegion % ratio); console.log( region.toLocaleUpperCase(), - `\n`, + '\n', '# of nations from region', nationsInRegion, + '\n', 'Qualifying spots from this region', qualifyingSpots, + '\n', 'Extra spots available for this region', extraSpots ); @@ -163,7 +165,6 @@ export class SimulationQualifiersService { if (numQualified === 32 || extra === 0) { return; } - // console.log(numQualified); // console.log('test', extra, nations.length, auto); const qualifiers = this.autoQualifiers(extra, nations, auto, regions[index]); teamsQualified.push(...qualifiers.map(m => m.winner)); @@ -210,7 +211,6 @@ export class SimulationQualifiersService { for (let i = 0; i < matches; i++) { const wtIndex = alreadyQualified + (matches * 2 - 1 - i); qualifiers.push(matchScore(availableNations[alreadyQualified + i], availableNations[wtIndex])); - console.log(alreadyQualified + i, wtIndex); } qualifiers.forEach(match => { match.winner.matchHistory.qualifiers.push({ match, opp: match.loser }); diff --git a/src/app/pages/simulation/simulation.component.html b/src/app/pages/simulation/simulation.component.html index b2b3860..0a71038 100644 --- a/src/app/pages/simulation/simulation.component.html +++ b/src/app/pages/simulation/simulation.component.html @@ -3,29 +3,16 @@

Tournament Setup

- + diff --git a/src/app/pages/simulation/simulation.component.scss b/src/app/pages/simulation/simulation.component.scss index a56cd4f..2d23685 100644 --- a/src/app/pages/simulation/simulation.component.scss +++ b/src/app/pages/simulation/simulation.component.scss @@ -2,7 +2,7 @@ h1 { text-align: center; - margin: 3rem; + margin: 3rem 0 0; font-size: 3rem; @media screen and (max-width: 600px) { diff --git a/src/app/pages/simulation/simulation.component.ts b/src/app/pages/simulation/simulation.component.ts index 7837bf2..90db14e 100644 --- a/src/app/pages/simulation/simulation.component.ts +++ b/src/app/pages/simulation/simulation.component.ts @@ -16,9 +16,7 @@ export class SimulationComponent { constructor(service: SimulationService) { this.service = service; - service.selectedNation$ - .pipe(untilDestroyed(this)) - .subscribe((nation) => (this.selectedNation = nation)); + service.selectedNation$.pipe(untilDestroyed(this)).subscribe(nation => (this.selectedNation = nation)); } @HostListener('document:keydown.escape', ['$event']) onKeydownHandler() { diff --git a/src/app/pages/simulation/simulation.model.ts b/src/app/pages/simulation/simulation.model.ts index 73ca3e0..bf531cc 100644 --- a/src/app/pages/simulation/simulation.model.ts +++ b/src/app/pages/simulation/simulation.model.ts @@ -13,7 +13,12 @@ export interface Match { export interface Tournament32 { availableRegions?: Region[]; hostNation?: GroupTeam; - teams?: GroupTeam[]; + allTeams?: { + rankings: GroupTeam[]; + attRankings: GroupTeam[]; + midRankings: GroupTeam[]; + defRankings: GroupTeam[]; + }; groups?: GroupTeam[][]; groupWinners?: GroupTeam[]; bracket?: { diff --git a/src/app/pages/simulation/simulation.service.ts b/src/app/pages/simulation/simulation.service.ts index 06e95bf..ce4285b 100644 --- a/src/app/pages/simulation/simulation.service.ts +++ b/src/app/pages/simulation/simulation.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { compare } from '@shared/utils'; -import { matchScore } from './simulation.utils'; +import { findTeamInTournament, matchScore } from './simulation.utils'; import { GroupTeam } from 'app/models/nation.model'; import { Match, Region, Tournament32 } from './simulation.model'; import { BehaviorSubject } from 'rxjs'; @@ -24,7 +24,16 @@ export class SimulationService { } changeSelectedNation(value: null | GroupTeam) { - this.selectedNation$.next(value); + if (!this.tournament?.groups || !value) { + this.selectedNation$.next(value); + return; + } + const updatedNation = findTeamInTournament(this.tournament?.groups, value); + if (updatedNation) { + this.selectedNation$.next(updatedNation); + } else { + this.selectedNation$.next(value); + } } simulateGroups(groupGamesPerOpponent: number, groups: GroupTeam[][]): GroupTeam[][] { @@ -43,7 +52,12 @@ export class SimulationService { group: [], bracket: [], }; - g[i][j].grade = undefined; + g[i][j].reportCard = { + grade: null, + gradeStyle: null, + gradeSummary: null, + tournamentFinish: null, + }; } } // go through each group diff --git a/src/app/pages/simulation/simulation.utils.ts b/src/app/pages/simulation/simulation.utils.ts index 787daf9..c6effe0 100644 --- a/src/app/pages/simulation/simulation.utils.ts +++ b/src/app/pages/simulation/simulation.utils.ts @@ -64,6 +64,10 @@ export function extraTimeResult(match: Match) { return match.penaltyWin ? ` after winning on penalties` : match.etWin ? ` after extra time` : ''; } +export function findTeamInTournament(groups: GroupTeam[][], nation: GroupTeam) { + return groups.flat().find(t => t.name === nation.name); +} + function getResultArr(wins: number, draws: number, losses: number): string[] { return [...Array(wins).fill('win'), ...Array(losses).fill('loss'), ...Array(draws).fill('draw')]; } @@ -337,11 +341,12 @@ export function calcScore( } } -export const reportCard = ({ name: nationName, grade, matchesPlayed }: GroupTeam): string => { +export function getGradeSummary({ name: nationName, reportCard, matchesPlayed }: GroupTeam): string { const name = nationName .split(' ') .map(l => l[0].toLocaleUpperCase() + l.substring(1)) .join(' '); + const grade = reportCard.grade; if (matchesPlayed < 3) { return `${name} did not qualify for the tournament, their players had to watch from the comfort of their own homes.`; } @@ -361,7 +366,26 @@ export const reportCard = ({ name: nationName, grade, matchesPlayed }: GroupTeam default: return 'ERROR'; } -}; +} + +export function getGradeStyle(grade: string | undefined): '' | 'good-grade' | 'ok-grade' | 'bad-grade' { + if (grade) { + switch (grade) { + case 's': + case 'a': + return 'good-grade'; + case 'b': + case 'c': + return 'ok-grade'; + case 'd': + case 'f': + return 'bad-grade'; + default: + return 'bad-grade'; + } + } + return ''; +} export function matchScore(team: GroupTeam, otherTeam: GroupTeam, hostNation?: GroupTeam): Match { const tm = { diff --git a/src/app/shared/components/awards/awards.component.html b/src/app/shared/components/awards/awards.component.html index b865729..9935022 100644 --- a/src/app/shared/components/awards/awards.component.html +++ b/src/app/shared/components/awards/awards.component.html @@ -1,9 +1,27 @@ - + + diff --git a/src/app/shared/components/awards/awards.component.scss b/src/app/shared/components/awards/awards.component.scss index 8215ecb..cfe9e35 100644 --- a/src/app/shared/components/awards/awards.component.scss +++ b/src/app/shared/components/awards/awards.component.scss @@ -6,15 +6,24 @@ border: 1px solid black; background-color: $primary-light; padding: 1.5rem; + display: flex; + flex-flow: row wrap; + + >div { + display: flex; + flex-flow: column nowrap; + flex: 1; - li { - margin-bottom: 10px; } - .stat { - display: flex; - align-items: center; - cursor: pointer; + h4 { + text-align: center; + font-size: 2.5rem; + padding: 0 0 1.5rem; + } + + >ul >h4 { + padding-bottom: 1rem; } .nation-flag { @@ -22,12 +31,40 @@ height: 1.5rem; margin: 0 1rem; } -} -h1 { - font-size: 4rem; + li { + margin-bottom: 1rem; + } + + .awards { + margin: 0 0 2rem; + @media screen and (max-width: 450px) { + flex: 1; + } + } - @media screen and (max-width: 1200px) { - font-size: 2.5rem; + .stat { + display: flex; + align-items: center; + cursor: pointer; + // justify-content: center; + } + .rankings { + display: flex; + flex-flow: wrap; + padding-left: 5rem; + @media screen and (max-width: 450px) { + padding-left: 0; + } + + .heading { + padding: 1rem 1rem 1rem 0; + cursor: pointer; + // text-align: center; + } + + details { + flex: 1; + } } } diff --git a/src/app/shared/components/awards/awards.component.ts b/src/app/shared/components/awards/awards.component.ts index 2d6a16f..147d3d6 100644 --- a/src/app/shared/components/awards/awards.component.ts +++ b/src/app/shared/components/awards/awards.component.ts @@ -1,8 +1,9 @@ -import { Component } from '@angular/core'; +import { Component, HostListener } from '@angular/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { GroupTeam } from 'app/models/nation.model'; import { Tournament32 } from 'app/pages/simulation/simulation.model'; import { SimulationService } from 'app/pages/simulation/simulation.service'; +import { get as _get } from 'lodash'; @UntilDestroy() @Component({ @@ -12,7 +13,10 @@ import { SimulationService } from 'app/pages/simulation/simulation.service'; }) export class AwardsComponent { service: SimulationService; + screenWidth: number; + get = _get; tournament: Tournament32 | null = null; + rankings: { heading: string; prop: string; class: string; nations?: GroupTeam[] }[] | null = null; tournamentStats = [ { @@ -34,7 +38,46 @@ export class AwardsComponent { constructor(service: SimulationService) { this.service = service; - service.tournament$.pipe(untilDestroyed(this)).subscribe(t => (this.tournament = t)); + this.screenWidth = window.innerWidth; + this.getScreenSize(); + service.tournament$.pipe(untilDestroyed(this)).subscribe(t => { + this.tournament = t; + this.rankings = null; + if (!t?.allTeams) { + return; + } + this.rankings = [ + { + heading: 'Overall', + prop: 'r', + class: 'main', + nations: t.allTeams.rankings, + }, + { + heading: 'Attack', + prop: 'attR', + class: 'att', + nations: t.allTeams.attRankings, + }, + { + heading: 'Midfield', + prop: 'midR', + class: 'mid', + nations: t.allTeams.midRankings, + }, + { + heading: 'Defense', + prop: 'defR', + class: 'def', + nations: t.allTeams.defRankings, + }, + ]; + }); + } + + @HostListener('window:resize', ['$event']) + getScreenSize() { + this.screenWidth = window.innerWidth; } openNationStats(nation: GroupTeam | null) { diff --git a/src/app/shared/components/knockout-stage/knockout-stage.component.html b/src/app/shared/components/knockout-stage/knockout-stage.component.html index 7edb5dd..a0c4e96 100644 --- a/src/app/shared/components/knockout-stage/knockout-stage.component.html +++ b/src/app/shared/components/knockout-stage/knockout-stage.component.html @@ -5,31 +5,17 @@
  • Semi Finals
  • -
    +
    -

    - {{ i === 0 ? "Finals" : "3rd Place" }} +

    + {{ i === 0 ? 'Finals' : '3rd Place' }}

    - + - {{ - screenWidth < 400 - ? match[0].abbreviation.toLocaleUpperCase() - : match[0].name - }} - [{{ match[0].ranking }}] + {{ screenWidth < 600 ? match[0].abbreviation.toLocaleUpperCase() : match[0].name }} + {{ screenWidth > 350 ? '[' + match[0].ranking + ']' : '' }}
    @@ -37,29 +23,21 @@ >{{ match[2].goalsFor }} {{ match[2].penaltyWin && match[0].name === match[2].winner.name - ? "(P)" + ? '(P)' : match[2].etWin && match[0].name === match[2].winner.name - ? "(AET)" - : "" + ? '(AET)' + : '' }} - {{ match[0].name === match[2].winner.name ? "◀" : "" }} + {{ match[0].name === match[2].winner.name ? '◀' : '' }}
    - + - {{ - screenWidth < 400 - ? match[1].abbreviation.toLocaleUpperCase() - : match[1].name - }} - [{{ match[1].ranking }}] + {{ screenWidth < 600 ? match[1].abbreviation.toLocaleUpperCase() : match[1].name }} + {{ screenWidth > 350 ? '[' + match[1].ranking + ']' : '' }}
    @@ -67,13 +45,13 @@ >{{ match[2].goalsAg }} {{ match[2].penaltyWin && match[1].name === match[2].winner.name - ? "(P)" + ? '(P)' : match[2].etWin && match[1].name === match[2].winner.name - ? "(AET)" - : "" + ? '(AET)' + : '' }} - {{ match[1].name === match[2].winner.name ? "◀" : "" }} + {{ match[1].name === match[2].winner.name ? '◀' : '' }}
    diff --git a/src/app/shared/components/nation-dialog/nation-dialog.component.html b/src/app/shared/components/nation-dialog/nation-dialog.component.html index e92b9bf..e0a3ac7 100644 --- a/src/app/shared/components/nation-dialog/nation-dialog.component.html +++ b/src/app/shared/components/nation-dialog/nation-dialog.component.html @@ -32,7 +32,7 @@

    {{ nation.defRanking }}

    POT -

    {{ nation.pot }}

    +

    {{ nation.pot ?? 'N/A' }}

    Region @@ -72,10 +72,12 @@

    Match History

    -
      +

        Performance Grade

        -
      • {{ nation.grade }} - {{ nation.tournamentFinish }}
      • - {{ gradeSummary }} +
      • + {{ nation.reportCard.grade }} - {{ nation.reportCard.tournamentFinish }} +
      • + {{ nation.reportCard.gradeSummary }}
    diff --git a/src/app/shared/components/nation-dialog/nation-dialog.component.ts b/src/app/shared/components/nation-dialog/nation-dialog.component.ts index 1e816d9..fed15c4 100644 --- a/src/app/shared/components/nation-dialog/nation-dialog.component.ts +++ b/src/app/shared/components/nation-dialog/nation-dialog.component.ts @@ -2,7 +2,7 @@ import { Component, HostListener } from '@angular/core'; import { GroupTeam } from 'app/models/nation.model'; import { originalOrder } from '@shared/utils'; import { Tournament32 } from 'app/pages/simulation/simulation.model'; -import { reportCard } from 'app/pages/simulation/simulation.utils'; +import { findTeamInTournament, getGradeStyle, getGradeSummary } from 'app/pages/simulation/simulation.utils'; import { SimulationService } from 'app/pages/simulation/simulation.service'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { combineLatest } from 'rxjs'; @@ -19,7 +19,7 @@ export class NationDialogComponent { tournament: Tournament32 | null = null; screenWidth: number; originalOrder = originalOrder; - gradeSummary = ''; + rounds = ['Round of 16', 'Quarter Finals', 'Semi Finals', 'Finals / Third Place Match']; stages = [ { @@ -35,7 +35,6 @@ export class NationDialogComponent { prop: 'bracket', }, ]; - gradeStyle?: string; constructor(service: SimulationService) { this.screenWidth = window.innerWidth; @@ -49,9 +48,8 @@ export class NationDialogComponent { .subscribe(([tournament, nation]) => { this.tournament = tournament; this.nation = nation; - if (this.nation !== null) { - this.gradePerformance(); - this.gradeSummary = reportCard(this.nation); + if (nation && !nation.reportCard.grade) { + this.getNationReportCard(nation); } }); } @@ -68,16 +66,37 @@ export class NationDialogComponent { this.service.changeSelectedNation(nation || null); } - gradePerformance() { - const { nation, tournament } = this; - if (nation?.ranking === undefined || tournament === null) { + updateNation(nation: GroupTeam) { + if (!this.tournament || !this.tournament.groups) { + return; + } + const updatedNation = this.tournament.groups.flat().find(a => a.name === nation.name); + if (updatedNation !== undefined) { + this.service.changeSelectedNation(nation); return; } - if (nation.grade) { - this.gradeStyle = this.getGradeStyle(nation.grade); + nation.matchHistory.qualifiers.map(m => { + if (this.tournament?.groups) { + const opp = findTeamInTournament(this.tournament.groups, m.opp); + if (opp) { + m.opp = opp; + } + } + return m; + }); + this.nation = nation; + this.service.changeSelectedNation(nation); + return; + } + + getNationReportCard(team: GroupTeam) { + const { tournament } = this; + if (!team.ranking || !tournament?.groups) { return; } + const nation = findTeamInTournament(tournament.groups, team) ?? team; + let rankingTiers: Array = ['s', 'a', 'b', 'c', 'd', 'e', 'f', 'g']; if (tournament.availableRegions) { const availableRegions = tournament.availableRegions.map(r => r.value); @@ -101,30 +120,32 @@ export class NationDialogComponent { rankingTiers.every((r, i) => { if (nation.ranking && (nation.ranking <= r || nation.tier === r || nation.pot === r)) { - const { grade, result } = this.calcGrade(i); - nation.grade = grade; - nation.tournamentFinish = result; - this.gradeStyle = this.getGradeStyle(grade); + const { grade, result } = this.calcGrade(nation, i); + nation.reportCard.grade = grade; + nation.reportCard.tournamentFinish = result; + nation.reportCard.gradeStyle = getGradeStyle(grade); + nation.reportCard.gradeSummary = getGradeSummary(nation); return false; } return true; }); - - this.updateNation(nation); } - calcGrade(rankingIndex: number): { - grade: string | undefined; - result: string | undefined; + calcGrade( + nation: GroupTeam, + rankingIndex: number + ): { + grade: string; + result: string; } { - const { nation, tournament } = this; - if (!nation || tournament?.groupWinners === undefined || tournament.bracket === undefined || tournament.awards === undefined) { - return { grade: undefined, result: undefined }; + const { tournament } = this; + if (tournament?.groupWinners === undefined || tournament.bracket === undefined || tournament.awards === undefined) { + return { grade: 'n/a', result: 'Did Not Qualify' }; } let gradeArr = ['f', 'f', 'f', 'f', 'f', 'f', 'f', 'f']; let result = ''; if (nation.matchesPlayed < 3) { - gradeArr = Array(7).fill('n/a'); + gradeArr = Array(8).fill('n/a'); result = 'Did Not Qualify'; } else if (nation.points < 3 && nation.gDiff < -3) { gradeArr = ['f', 'f', 'f', 'd', 'c', 'c', 'c', 'b']; @@ -153,37 +174,13 @@ export class NationDialogComponent { } else if (tournament.awards[0] === nation) { gradeArr = ['a', 'a', 's', 's', 's', 's', 's', 's']; result = 'Winner'; + } else { + gradeArr = Array(8).fill('n/a'); + result = 'Did Not Qualify'; } return { grade: gradeArr[rankingIndex], result, }; } - - getGradeStyle(grade: string | undefined): '' | 'good-grade' | 'ok-grade' | 'bad-grade' { - if (grade) { - switch (grade) { - case 's': - case 'a': - return 'good-grade'; - case 'b': - case 'c': - return 'ok-grade'; - case 'd': - case 'f': - return 'bad-grade'; - default: - return 'bad-grade'; - } - } - return ''; - } - - updateNation(nation: GroupTeam) { - const updatedNation = this.tournament?.groups?.flat().find(a => a.name === nation.name); - if (updatedNation !== undefined) { - this.service.selectedNation$.next(updatedNation); - return; - } - } } diff --git a/src/app/shared/components/tournament-form/tournament-form.component.html b/src/app/shared/components/tournament-form/tournament-form.component.html index dfa505e..445d259 100644 --- a/src/app/shared/components/tournament-form/tournament-form.component.html +++ b/src/app/shared/components/tournament-form/tournament-form.component.html @@ -1,18 +1,6 @@
    -
    - - -
    -
    - - -
    +
    + +
    + + +
    + +
    + + +
    diff --git a/src/app/shared/components/tournament-form/tournament-form.component.scss b/src/app/shared/components/tournament-form/tournament-form.component.scss index 72fb6a9..6959755 100644 --- a/src/app/shared/components/tournament-form/tournament-form.component.scss +++ b/src/app/shared/components/tournament-form/tournament-form.component.scss @@ -60,6 +60,11 @@ width: 80%; height: 3rem; } + + @media screen and (max-width: 425px) { + width: 100%; + height: 4rem; + } } } diff --git a/src/app/shared/components/tournament-form/tournament-form.component.ts b/src/app/shared/components/tournament-form/tournament-form.component.ts index a6145dc..070d2a8 100644 --- a/src/app/shared/components/tournament-form/tournament-form.component.ts +++ b/src/app/shared/components/tournament-form/tournament-form.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { untilDestroyed, UntilDestroy } from '@ngneat/until-destroy'; -import { GroupTeam } from 'app/models/nation.model'; +import { defaultHost, GroupTeam } from 'app/models/nation.model'; import { Region, Tournament32 } from 'app/pages/simulation/simulation.model'; import { SimulationService } from 'app/pages/simulation/simulation.service'; -import { regions, regionsValidator } from 'app/pages/simulation/simulation.utils'; +import { addRankings, regions, regionsValidator } from 'app/pages/simulation/simulation.utils'; import nationsModule from 'assets/json/nations.json'; import { getRandFloat, getRandomInt, roundMax } from '@shared/utils'; import { FormBuilder, Validators } from '@angular/forms'; @@ -24,31 +24,7 @@ export class TournamentFormComponent { filteredNations: GroupTeam[]; tournament: Tournament32 | null = {}; - hostNation: GroupTeam = { - name: 'qatar', - abbreviation: 'qat', - logo: 'https://fmdataba.com/images/n/QAT.svg', - region: 'afc', - points: 0, - gDiff: 0, - gFor: 0, - gOpp: 0, - tier: 'j', - attRating: 0, - midRating: 0, - defRating: 0, - rating: 0, - matchesPlayed: 0, - matchHistory: { - qualifiers: [], - group: [], - bracket: [], - }, - ranking: 0, - attRanking: 0, - midRanking: 0, - defRanking: 0, - }; + hostNation = defaultHost; tournamentForm = this.fb.group( { @@ -110,6 +86,7 @@ export class TournamentFormComponent { } createTeams() { + this.simulator.tournament$.next(null); this.nationsList = []; this.nations.forEach(tier => { tier.nations.forEach(nation => { @@ -189,6 +166,12 @@ export class TournamentFormComponent { group: [], bracket: [], }, + reportCard: { + grade: null, + gradeStyle: null, + gradeSummary: null, + tournamentFinish: null, + }, }; this.nationsList.push(team); }); @@ -197,6 +180,13 @@ export class TournamentFormComponent { setupTournament(numOfGames: number, numOfTeams: number, availableRegions: Region[], hostNation: GroupTeam): void { this.createTeams(); + const nations = addRankings(this.nationsList); + const allTeams = { + rankings: [...nations], + attRankings: [...nations.sort((a, b) => a.attRanking - b.attRanking)], + midRankings: [...nations.sort((a, b) => a.midRanking - b.midRanking)], + defRankings: [...nations.sort((a, b) => a.defRanking - b.defRanking)], + }; const teams = this.qualifier.chooseQualifyingTeams(availableRegions, numOfTeams, this.nationsList, hostNation); const numOfGroups = teams.length / 4; const extraTeams = numOfTeams % 4; @@ -210,7 +200,7 @@ export class TournamentFormComponent { const groups = this.qualifier.organizeGroups(teams, extraTeams, teamsInGroup, numOfTeams, hostNation, availableRegions); this.simulator.tournament$.next({ - teams, + allTeams, groups, availableRegions: availableRegions, hostNation: hostNation, @@ -220,10 +210,10 @@ export class TournamentFormComponent { } simulateTournament(numOfGames: number): void { - if (!this.tournament?.teams || !this.tournament?.groups) { + if (!this.tournament?.allTeams || !this.tournament?.groups) { return; } - const teams = this.tournament.teams; + const allTeams = this.tournament.allTeams; const availableRegions = this.tournament.availableRegions; const groups = this.simulator.simulateGroups(numOfGames, this.tournament.groups); @@ -234,7 +224,7 @@ export class TournamentFormComponent { groupWinners, bracket, awards, - teams, + allTeams, availableRegions, }); } diff --git a/src/app/shared/pipes/memoizer.pipe.spec.ts b/src/app/shared/pipes/memoizer.pipe.spec.ts new file mode 100644 index 0000000..5371234 --- /dev/null +++ b/src/app/shared/pipes/memoizer.pipe.spec.ts @@ -0,0 +1,8 @@ +import { MemoizerPipe } from './memoizer.pipe'; + +describe('MemoizerPipe', () => { + it('create an instance', () => { + const pipe = new MemoizerPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/shared/pipes/memoizer.pipe.ts b/src/app/shared/pipes/memoizer.pipe.ts new file mode 100644 index 0000000..b307858 --- /dev/null +++ b/src/app/shared/pipes/memoizer.pipe.ts @@ -0,0 +1,27 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +/** + * @description This pipe is designed to be used in a template to access a component method: + * @example In a template: {{ valueA | memoizerPipe : componentMethodRef : valueB }} + * @implements {PipeTransform} + * CAUTION: Methods needs to be defined an ARROW FUNCTION inside component classes + * This is because the memoizerPipe cannot apply the proper context (uses null) when invoking the function. As such, + * we need to pre-bind it to the component so that we can use the proper "this" reference when invoked via the memoizerPipe. + */ + +@Pipe({ + name: 'memoizer', + pure: true, +}) +export class MemoizerPipe implements PipeTransform { + transform(templateValue: any, fnReference: Function, ...fnArguments: any[]): any { + // Due to the way pipes receive arguments, we may have inputs on both sides of + // the function reference. As such, let's join the two input sets. + fnArguments.unshift(templateValue); + + // CAUTION: The function reference will NOT BE INVOKED IN THE COMPONENT CONTEXT. + // As such, a component must bind the reference if it needs to use the "this" + // scope within the function body. + return fnReference.apply(null, fnArguments); + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 226b6b7..51f642d 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -9,9 +9,10 @@ import { faBars, faXmark } from '@fortawesome/free-solid-svg-icons'; import * as fromComponents from './components'; import { ClickStopPropagationDirective } from './directives/click-stop-propagation.directive'; +import { MemoizerPipe } from './pipes/memoizer.pipe'; @NgModule({ - declarations: [...fromComponents.components, ClickStopPropagationDirective], + declarations: [...fromComponents.components, ClickStopPropagationDirective, MemoizerPipe], imports: [RouterModule, ReactiveFormsModule, BrowserAnimationsModule, FontAwesomeModule, NgSelectModule], exports: [NgSelectModule, ReactiveFormsModule, ...fromComponents.components], }) diff --git a/src/styles.scss b/src/styles.scss index b153cbf..e6b4e61 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -35,16 +35,6 @@ select { text-transform: inherit; } -h1, -h2, -h3, -h4, -h5, -h6 { - margin: 0; - padding: 0; -} - li { list-style: none; }