Skip to content

Commit

Permalink
Angular v17 + Material 3 Upgrade: Coworking and Ambassadors Features (#…
Browse files Browse the repository at this point in the history
…522)

* Refactor Folder Structure of Ambassador Page

* Refactor Component Names

* Refactor Coworking Service and XL Service

* Refactor Ambassador Room

* Remove Unecessary Files

* Refactor Reservation Service

* Add Attributions, Remove Unused Imports

* Refactor all HTML in Coworking / Ambassadors to use new control flow

* Fix Consistency in Fonts In Panes
  • Loading branch information
ajaygandecha authored Jul 17, 2024
1 parent 1b10b52 commit 94d1d56
Show file tree
Hide file tree
Showing 40 changed files with 593 additions and 896 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AmbassadorXlListComponent } from './ambassador-xl/list/ambassador-xl-list.component';
import { AmbassadorXLComponent } from './ambassador-xl/ambassador-xl.component';
import { AmbassadorPageComponent } from './ambassador-home.component';
import { AmbassadorRoomListComponent } from './ambassador-room/list/ambassador-room-list.component';
import { AmbassadorRoomComponent } from './ambassador-room/ambassador-room.component';

const routes: Routes = [
{
path: '',
component: AmbassadorPageComponent,
children: [
AmbassadorXlListComponent.Route,
AmbassadorRoomListComponent.Route
]
children: [AmbassadorXLComponent.Route, AmbassadorRoomComponent.Route]
}
];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1 @@
<nav mat-tab-nav-bar [tabPanel]="tabPanel" color="accent">
<a
mat-tab-link
*ngFor="let link of links"
[routerLink]="link.path"
routerLinkActive
#rla="routerLinkActive"
[routerLinkActiveOptions]="{ exact: false }"
[active]="rla.isActive"
>{{ link.label }}</a
>
</nav>
<mat-tab-nav-panel #tabPanel>
<router-outlet></router-outlet>
</mat-tab-nav-panel>
<tab-container [links]="links" />
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* This component is the primary screen for ambassadors at the check-in desk.
*
* @author Kris Jordan <[email protected]>
* @author Kris Jordan <[email protected]>, Ajay Gandecha <[email protected]>
* @copyright 2023 - 2024
* @license MIT
*/

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
Expand All @@ -19,18 +19,19 @@ export class AmbassadorPageComponent implements OnInit {
{
label: 'XL Reservations',
path: '/coworking/ambassador/xl',
default: true
icon: 'chair_alt'
},
{ label: 'Room Reservations', path: '/coworking/ambassador/room' }
{
label: 'Room Reservations',
path: '/coworking/ambassador/room',
icon: 'meeting_room'
}
];

constructor(private router: Router) {}

ngOnInit(): void {
// Find the default link and navigate to it
const defaultLink = this.links.find((link) => link.default);
if (defaultLink) {
this.router.navigate([defaultLink.path]);
}
this.router.navigate(['/coworking/ambassador/xl']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { MatListModule } from '@angular/material/list';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSelectModule } from '@angular/material/select';
import { MatTabsModule } from '@angular/material/tabs';
// import { AmbassadorXlListComponent } from './ambassador-xl/list/ambassador-xl-list.component';
// import { AmbassadorRoomListComponent } from './ambassador-room/list/ambassador-room-list.component';
// import { AmbassadorXLComponent } from './ambassador-xl/list/ambassador-xl-list.component';
// import { AmbassadorRoomComponent } from './ambassador-room/list/ambassador-room-list.component';
import { SharedModule } from 'src/app/shared/shared.module';

@NgModule({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
@if (upcomingReservations()) { @if (upcomingReservations().length > 0) {
<mat-pane class="content" appearance="outlined">
<mat-card-header>
<mat-card-title>Upcoming Reservations</mat-card-title>
</mat-card-header>
<mat-card-content>
<table mat-table [dataSource]="upcomingReservations()">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>ID</th>
<td mat-cell *matCellDef="let reservation">{{ reservation.id }}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.users[0].first_name }}
{{ reservation.users[0].last_name }}
</td>
</ng-container>
<ng-container matColumnDef="date">
<th mat-header-cell *matHeaderCellDef>Date</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.start | date: 'dd MMM yyyy' }}
</td>
</ng-container>
<ng-container matColumnDef="start">
<th mat-header-cell *matHeaderCellDef>Start</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.start | date: 'shortTime' }}
</td>
</ng-container>
<ng-container matColumnDef="end">
<th mat-header-cell *matHeaderCellDef>End</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.end | date: 'shortTime' }}
</td>
</ng-container>
<ng-container matColumnDef="room">
<th mat-header-cell *matHeaderCellDef>Room</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.room.id }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell *matCellDef="let reservation">
<button
mat-flat-button
(click)="this.ambassadorService.checkIn(reservation)"
[disabled]="this.ambassadorService.isCheckInDisabled(reservation)">
Check-in
</button>
<button
mat-flat-button
class="tertiary-button"
(click)="this.ambassadorService.cancel(reservation)">
Cancel
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
<tr mat-row *matRowDef="let reservation; columns: columnsToDisplay"></tr>
</table>
</mat-card-content>
</mat-pane>
} @else {
<mat-pane class="content" appearance="outlined">
<mat-card-header>
<mat-card-title>Upcoming Reservations</mat-card-title>
</mat-card-header>
<mat-card-content><p>No upcoming reservations.</p></mat-card-content>
</mat-pane>
} } @if(activeReservations()) { @if(activeReservations().length > 0) {
<mat-pane class="content" appearance="outlined">
<mat-card-header>
<mat-card-title>Active Reservations</mat-card-title>
</mat-card-header>
<mat-card-content>
<table mat-table [dataSource]="activeReservations()">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>ID</th>
<td mat-cell *matCellDef="let reservation">{{ reservation.id }}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>Name</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.users[0].first_name }}
{{ reservation.users[0].last_name }}
</td>
</ng-container>
<ng-container matColumnDef="date">
<th mat-header-cell *matHeaderCellDef>Date</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.start | date: 'dd MMM yyyy' }}
</td>
</ng-container>
<ng-container matColumnDef="start">
<th mat-header-cell *matHeaderCellDef>Start</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.start | date: 'shortTime' }}
</td>
</ng-container>
<ng-container matColumnDef="end">
<th mat-header-cell *matHeaderCellDef>End</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.end | date: 'shortTime' }}
</td>
</ng-container>
<ng-container matColumnDef="room">
<th mat-header-cell *matHeaderCellDef>Room</th>
<td mat-cell *matCellDef="let reservation">
{{ reservation.room.id }}
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell *matCellDef="let reservation">
<button
mat-flat-button
class="tertiary-button"
(click)="this.ambassadorService.checkOut(reservation)">
Check Out
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
<tr mat-row *matRowDef="let reservation; columns: columnsToDisplay"></tr>
</table>
</mat-card-content>
</mat-pane>
} @else {
<mat-pane class="content" appearance="outlined">
<mat-card-header>
<mat-card-title>Active Reservations</mat-card-title>
</mat-card-header>
<mat-card-content><p>It's lonely in here.</p></mat-card-content>
</mat-pane>
} }
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @author Kris Jordan <[email protected]>, Ajay Gandecha <[email protected]>
* @copyright 2023 - 2024
* @license MIT
*/

import { Component, OnDestroy, OnInit, computed } from '@angular/core';
import { Route } from '@angular/router';
import { Subscription, timer, tap } from 'rxjs';
import { permissionGuard } from 'src/app/permission.guard';
import { AmbassadorRoomService } from './ambassador-room.service';

@Component({
selector: 'app-ambassador-room',
templateUrl: './ambassador-room.component.html',
styleUrls: ['./ambassador-room.component.css']
})
export class AmbassadorRoomComponent implements OnInit, OnDestroy {
public static Route: Route = {
path: 'room',
component: AmbassadorRoomComponent,
title: 'Room Reservations',
canActivate: [permissionGuard('coworking.reservation.*', '*')],
resolve: {}
};

upcomingReservations = computed(() => {
return this.ambassadorService.reservations().filter((r) => {
return r.state == 'CONFIRMED';
});
});

activeReservations = computed(() => {
return this.ambassadorService.reservations().filter((r) => {
return r.state == 'CHECKED_IN';
});
});

columnsToDisplay = ['id', 'name', 'room', 'date', 'start', 'end', 'actions'];

private refreshSubscription!: Subscription;

constructor(public ambassadorService: AmbassadorRoomService) {}

ngOnInit(): void {
this.refreshSubscription = timer(0, 5000)
.pipe(tap((_) => this.ambassadorService.fetchReservations()))
.subscribe();
}

ngOnDestroy(): void {
this.refreshSubscription.unsubscribe();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
/**
* @author Kris Jordan <[email protected]>, Ajay Gandecha <[email protected]>
* @copyright 2023 - 2024
* @license MIT
*/

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Injectable, WritableSignal, signal } from '@angular/core';
import {
Reservation,
ReservationJSON,
parseReservationJSON
} from '../../coworking.models';
import { RxReservations } from '../rx-reservations';

@Injectable({
providedIn: 'root'
})
export class AmbassadorRoomService {
private reservations: RxReservations = new RxReservations();
public reservations$: Observable<Reservation[]> = this.reservations.value$;
private reservationsSignal: WritableSignal<Reservation[]> = signal([]);
public reservations = this.reservationsSignal.asReadonly();

constructor(private http: HttpClient) {}

fetchReservations(): void {
this.http
.get<ReservationJSON[]>('/api/coworking/ambassador/rooms')
.subscribe((reservations) => {
this.reservations.set(reservations.map(parseReservationJSON));
this.reservationsSignal.set(reservations.map(parseReservationJSON));
});
}

Expand All @@ -37,10 +41,8 @@ export class AmbassadorRoomService {
id: reservation.id,
state: 'CHECKED_IN'
})
.subscribe((reservationJson) => {
this.reservations.updateReservation(
parseReservationJSON(reservationJson)
);
.subscribe((_) => {
this.fetchReservations();
});
}

Expand All @@ -50,10 +52,8 @@ export class AmbassadorRoomService {
id: reservation.id,
state: 'CHECKED_OUT'
})
.subscribe((reservationJson) => {
this.reservations.updateReservation(
parseReservationJSON(reservationJson)
);
.subscribe((_) => {
this.fetchReservations();
});
}

Expand All @@ -65,7 +65,7 @@ export class AmbassadorRoomService {
})
.subscribe({
next: (_) => {
this.reservations.remove(reservation);
this.fetchReservations();
},
error: (err) => {
alert(err);
Expand Down
Loading

0 comments on commit 94d1d56

Please sign in to comment.