Remove redundant unit tests for CommunityComponent and RanksComponent, enhance community.component with dynamic team member display and toggle functionality, update routing for community links, and set stricter field constraints in team schema.

This commit is contained in:
akastijn 2025-11-08 18:45:03 +01:00
parent 042a6450c2
commit a05a751628
8 changed files with 162 additions and 70 deletions

View File

@ -9,34 +9,74 @@
<main> <main>
<section class="darkmodeSection"> <section class="darkmodeSection">
<div class="customContainer"> <div class="container teamContainer">
<h2>Current Nitro Boosters</h2> <h2 class="sectionTitle">Current Nitro Boosters</h2>
@for (member of getTeamMembers('discord') | async; track member) {
<div class="member">
<img [ngSrc]="getAvatarUrl(member)" alt="{{member.name}}'s Minecraft skin"
height="160" width="160" style="width: 160px;">
<h2>{{ member.name }}</h2>
<p>Nitro booster</p>
</div>
}
</div> </div>
</section> </section>
<section id="social" class="darkmodeSectionThree"> <section id="social" class="darkmodeSectionThree">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;"> <div class="container teamContainer">
<h2 class="sectionTitle">Social Media</h2> <h2 class="sectionTitle">Social Media</h2>
@for (member of getTeamMembers('socialmedia') | async; track member) {
<div class="member">
<img [ngSrc]="getAvatarUrl(member)" alt="{{member.name}}'s Minecraft skin"
height="160" width="160" style="width: 160px;">
<h2>{{ member.name }}</h2>
<p>Social media</p>
</div>
}
</div> </div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;"> <div style="display: flex; justify-content: center; padding-bottom: 30px;">
<p style="text-align: center;">We're currently not looking for more people to help manage our socials.</p> <p style="text-align: center;">We're currently not looking for more people to help manage our socials.</p>
</div> </div>
</section> </section>
<section id="crateTeam" class="darkmodeSection"> <section id="crateTeam" class="darkmodeSection">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;"> <div class="container teamContainer">
<h2 class="sectionTitle">Crate Team</h2> <h2 class="sectionTitle">Crate Team</h2>
@for (member of getTeamMembers('crate') | async; track member) {
<div class="member">
<img [ngSrc]="getAvatarUrl(member)" alt="{{member.name}}'s Minecraft skin"
height="160" width="160" style="width: 160px;">
<h2>{{ member.name }}</h2>
<p>Crate team</p>
</div>
}
</div> </div>
</section> </section>
<section class="darkmodeSectionThree"> <section class="darkmodeSectionThree">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;"> <div class="container teamContainer">
<h2 class="sectionTitle">Event Leaders</h2> <h2 class="sectionTitle">Event Leaders</h2>
@for (member of getTeamMembers('eventleader') | async; track member) {
<div class="member">
<img [ngSrc]="getAvatarUrl(member)" alt="{{member.name}}'s Minecraft skin"
height="160" width="160" style="width: 160px;">
<h2>{{ member.name }}</h2>
<p>Event leaders</p>
</div>
}
</div> </div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;"> <div style="display: flex; justify-content: center; padding-bottom: 30px;">
<p style="text-align: center;">We're currently not looking for more Event Leaders.</p> <p style="text-align: center;">We're currently not looking for more Event Leaders.</p>
</div> </div>
</section> </section>
<section class="darkmodeSection"> <section class="darkmodeSection">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;"> <div class="container teamContainer">
<h2 class="sectionTitle">Event Team</h2> <h2 class="sectionTitle">Event Team</h2>
@for (member of getTeamMembers('eventteam') | async; track member) {
<div class="member">
<img [ngSrc]="getAvatarUrl(member)" alt="{{member.name}}'s Minecraft skin"
height="160" width="160" style="width: 160px;">
<h2>{{ member.name }}</h2>
<p>Event team</p>
</div>
}
</div> </div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;"> <div style="display: flex; justify-content: center; padding-bottom: 30px;">
<div style="flex-direction: column;"> <div style="flex-direction: column;">
@ -47,15 +87,32 @@
</div> </div>
</section> </section>
<section class="darkmodeSectionThree"> <section class="darkmodeSectionThree">
<div class="container" style="padding: 50px 0 0 0; justify-content: center;"> <div class="container teamContainer">
<h2 class="sectionTitle">YouTubers & Streamers</h2> <h2 class="sectionTitle">YouTubers & Streamers</h2>
@for (member of getTeamMembers('youtube') | async; track member) {
<div class="member">
<img [ngSrc]="getAvatarUrl(member)" alt="{{member.name}}'s Minecraft skin"
height="160" width="160" style="width: 160px;">
<h2>{{ member.name }}</h2>
<p>Youtuber</p>
</div>
}
@for (member of getTeamMembers('twitch') | async; track member) {
<div class="member">
<img [ngSrc]="getAvatarUrl(member)" alt="{{member.name}}'s Minecraft skin"
height="160" width="160" style="width: 160px;">
<h2>{{ member.name }}</h2>
<p>Streamer</p>
</div>
}
</div> </div>
<div style="display: flex; justify-content: center; padding-bottom: 30px;"> <div style="display: flex; justify-content: center; padding-bottom: 30px;">
<div style="flex-direction: column;"> <div style="flex-direction: column;">
<p style="text-align: center;"><a style="cursor: pointer;" id="reqButton">Show Requirements...</a></p> <p style="text-align: center;"><a style="cursor: pointer;" (click)="toggleSection('yt-stream-req')"
id="reqButton">{{ getTextForSection('yt-stream-req') }}</a></p>
</div> </div>
</div> </div>
<div id="req" class="hide" style="display: flex; justify-content: center; padding-bottom: 30px;"> <div [hidden]="!isToggled('yt-stream-req')" [class.requirementSection]="isToggled('yt-stream-req')">
<div style="flex-direction: column; justify-content: center; max-width: 800px;"> <div style="flex-direction: column; justify-content: center; max-width: 800px;">
<p style="text-align: center;"><span style="font-family: 'opensans-bold', sans-serif;">Requirements:</span> <p style="text-align: center;"><span style="font-family: 'opensans-bold', sans-serif;">Requirements:</span>
</p> </p>

View File

@ -1,12 +1,32 @@
.customContainer { .sectionTitle {
width: 80%; flex: 0 0 100%;
max-width: 1020px; text-align: center;
margin: auto; padding-bottom: 20px;
padding: 80px 0; font-size: 2em;
}
.member {
width: 33%;
min-width: 250px;
padding-bottom: 50px;
text-align: center; text-align: center;
} }
.hide { .member img {
display: none !important; padding-bottom: 15px;
} }
.member p {
font-family: 'opensans-bold', sans-serif;
}
.teamContainer {
padding: 50px 0 0 0;
justify-content: center;
}
.requirementSection {
display: flex;
justify-content: center;
padding-bottom: 30px;
}

View File

@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CommunityComponent } from './community.component';
describe('CommunityComponent', () => {
let component: CommunityComponent;
let fixture: ComponentFixture<CommunityComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CommunityComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CommunityComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,14 +1,66 @@
import {Component} from '@angular/core'; import {Component, inject} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "@header/header.component";
import {map, Observable, shareReplay} from 'rxjs';
import {Player, TeamService} from '@api';
import {ScrollService} from '@services/scroll.service';
import {AsyncPipe, NgOptimizedImage} from '@angular/common';
@Component({ @Component({
selector: 'app-community', selector: 'app-community',
imports: [ imports: [
HeaderComponent HeaderComponent,
AsyncPipe,
NgOptimizedImage
], ],
templateUrl: './community.component.html', templateUrl: './community.component.html',
styleUrl: './community.component.scss' styleUrl: './community.component.scss'
}) })
export class CommunityComponent { export class CommunityComponent {
private teamMembersCache: { [key: string]: Observable<Player[]> } = {};
protected scrollService: ScrollService = inject(ScrollService)
protected teamService: TeamService = inject(TeamService)
public getTeamMembers(team: string): Observable<Player[]> {
if (!this.teamMembersCache[team]) {
this.teamMembersCache[team] = this.teamService.getTeamMembers(team).pipe(
map(res => this.removeDuplicates(res)),
shareReplay(1)
);
}
return this.teamMembersCache[team];
}
private removeDuplicates(array: Player[]): Player[] {
return array.filter((player, index, self) =>
index === self.findIndex((p) => p.uuid === player.uuid)
);
}
public getAvatarUrl(entry: Player): string {
let uuid = entry.uuid.replace('-', '');
return `https://crafatar.com/avatars/${uuid}?overlay`;
}
public toggledSections: string[] = [];
public isToggled(section: string) {
return this.toggledSections.includes(section);
}
public toggleSection(section: string) {
if (this.isToggled(section)) {
this.toggledSections = this.toggledSections.filter(s => s !== section);
} else {
this.toggledSections.push(section);
}
}
public getTextForSection(section: string) {
if (this.isToggled(section)) {
return 'Hide Requirements...';
} else {
return 'Show Requirements...';
}
}
} }

View File

@ -197,27 +197,32 @@
<tbody> <tbody>
<tr> <tr>
<td data-label="Rank" class="rankTitle">[<span class="rank5">Social Media</span>]</td> <td data-label="Rank" class="rankTitle">[<span class="rank5">Social Media</span>]</td>
<td data-label="Requirements">See requirements on the <a href="/community.php">Community</a> page</td> <td data-label="Requirements">See requirements on the <a [routerLink]="['/community']">Community</a> page
</td>
<td data-label="Perks" class="perks">/record<br>/ptime<br>/pweather</td> <td data-label="Perks" class="perks">/record<br>/ptime<br>/pweather</td>
</tr> </tr>
<tr> <tr>
<td data-label="Rank" class="rankTitle">[<span class="rank5">Streamer</span>]</td> <td data-label="Rank" class="rankTitle">[<span class="rank5">Streamer</span>]</td>
<td data-label="Requirements">See requirements on the <a href="/community.php">Community</a> page</td> <td data-label="Requirements">See requirements on the <a [routerLink]="['/community']">Community</a> page
</td>
<td data-label="Perks" class="perks">/record<br>/ptime<br>/pweather</td> <td data-label="Perks" class="perks">/record<br>/ptime<br>/pweather</td>
</tr> </tr>
<tr> <tr>
<td data-label="Rank" class="rankTitle">[<span class="rank5">YouTube</span>]</td> <td data-label="Rank" class="rankTitle">[<span class="rank5">YouTube</span>]</td>
<td data-label="Requirements">See requirements on the <a href="/community.php">Community</a> page</td> <td data-label="Requirements">See requirements on the <a [routerLink]="['/community']">Community</a> page
</td>
<td data-label="Perks" class="perks">/record<br>/ptime<br>/pweather</td> <td data-label="Perks" class="perks">/record<br>/ptime<br>/pweather</td>
</tr> </tr>
<tr> <tr>
<td data-label="Rank" class="rankTitle">[<span class="rank5">Event Leader</span>]</td> <td data-label="Rank" class="rankTitle">[<span class="rank5">Event Leader</span>]</td>
<td data-label="Requirements">See requirements on the <a href="/community.php">Community</a> page</td> <td data-label="Requirements">See requirements on the <a [routerLink]="['/community']">Community</a> page
</td>
<td data-label="Perks" class="perks">Build perms on event server</td> <td data-label="Perks" class="perks">Build perms on event server</td>
</tr> </tr>
<tr> <tr>
<td data-label="Rank" class="rankTitle">[<span class="rank5">Event Team</span>]</td> <td data-label="Rank" class="rankTitle">[<span class="rank5">Event Team</span>]</td>
<td data-label="Requirements">See requirements on the <a href="/community.php">Community</a> page</td> <td data-label="Requirements">See requirements on the <a [routerLink]="['/community']">Community</a> page
</td>
<td data-label="Perks" class="perks">Build perms on event server</td> <td data-label="Perks" class="perks">Build perms on event server</td>
</tr> </tr>
</tbody> </tbody>

View File

@ -1,23 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RanksComponent } from './ranks.component';
describe('RanksComponent', () => {
let component: RanksComponent;
let fixture: ComponentFixture<RanksComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RanksComponent]
})
.compileComponents();
fixture = TestBed.createComponent(RanksComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,10 +1,12 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {HeaderComponent} from "@header/header.component"; import {HeaderComponent} from "@header/header.component";
import {RouterLink} from '@angular/router';
@Component({ @Component({
selector: 'app-ranks', selector: 'app-ranks',
imports: [ imports: [
HeaderComponent HeaderComponent,
RouterLink
], ],
templateUrl: './ranks.component.html', templateUrl: './ranks.component.html',
styleUrl: './ranks.component.scss' styleUrl: './ranks.component.scss'

View File

@ -12,6 +12,8 @@ getTeam:
description: The group name of the team description: The group name of the team
schema: schema:
type: string type: string
example: owner
maxLength: 32
responses: responses:
'200': '200':
description: successful operation description: successful operation