Skip to content

Commit

Permalink
feat: Added Venus front-end #78 #104
Browse files Browse the repository at this point in the history
  • Loading branch information
rbento1096 committed Feb 28, 2024
1 parent 3792373 commit 2cfb232
Show file tree
Hide file tree
Showing 14 changed files with 559 additions and 3 deletions.
72 changes: 72 additions & 0 deletions front-end/src/app/tabs/rooms/roomCard.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';

import { IDEATranslationsModule } from '@idea-ionic/common';

import { AppService } from 'src/app/app.service';

import { Room } from '@models/room.model';

@Component({
standalone: true,
imports: [CommonModule, FormsModule, IonicModule, IDEATranslationsModule],
selector: 'app-room-card',
template: `
<ng-container *ngIf="room; else skeletonTemplate">
<ion-card *ngIf="preview" [color]="preview ? 'white' : ''">
<ion-card-header>
<ion-card-title>{{ room.name }}</ion-card-title>
<ion-card-subtitle>{{ room.internalLocation }}</ion-card-subtitle>
</ion-card-header>
</ion-card>
<ion-card *ngIf="!preview" color="white">
<ion-img [src]="app.getImageURLByURI(room.imageURI)"></ion-img>
<ion-card-header>
<ion-card-subtitle style="font-weight: 300;" class="ion-text-right">
<ion-button fill="clear" (click)="app.goToInTabs(['venues', room.venue.venueId])">
<ion-icon name="location-outline"></ion-icon>
<ion-label>
{{ room.venue.name }}
</ion-label>
</ion-button>
</ion-card-subtitle>
<ion-card-title>{{ room.name }}</ion-card-title>
<ion-card-subtitle>{{ room.internalLocation }}</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<div class="divDescription" *ngIf="room.description">
<ion-textarea readonly [rows]="4" [(ngModel)]="room.description"></ion-textarea>
</div>
</ion-card-content>
</ion-card>
</ng-container>
<ng-template #skeletonTemplate>
<ion-card color="white">
<ion-skeleton-text animated style="height: 200px;"></ion-skeleton-text>
<ion-card-header>
<ion-card-title>
<ion-skeleton-text animated style="width: 60%;"></ion-skeleton-text>
</ion-card-title>
<ion-card-subtitle>
<ion-skeleton-text animated style="width: 50%;"></ion-skeleton-text>
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-skeleton-text animated style="width: 80%;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 70%;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 60%;"></ion-skeleton-text>
</ion-card-content>
</ion-card>
</ng-template>
`
})
export class RoomCardStandaloneComponent {
@Input() room: Room;
@Input() preview: boolean;

constructor(public app: AppService) {}
}
8 changes: 5 additions & 3 deletions front-end/src/app/tabs/rooms/rooms.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,24 @@ export class RoomsService {

constructor(private api: IDEAApiService) {}

private async loadList(): Promise<void> {
this.rooms = (await this.api.getResource(['rooms'])).map(r => new Room(r));
private async loadList(venue?: string): Promise<void> {
this.rooms = (await this.api.getResource(['rooms'], { params: { venue } })).map(r => new Room(r));
}

/**
* Get (and optionally filter) the list of rooms.
* Note: it can be paginated.
* Note: it's a slice of the array.
* Note: if venue id is passed, it will filter rooms for that venue.
*/
async getList(options: {
force?: boolean;
withPagination?: boolean;
startPaginationAfterId?: string;
search?: string;
venue?: string;
}): Promise<Room[]> {
if (!this.rooms || options.force) await this.loadList();
if (!this.rooms || options.force) await this.loadList(options.venue);
if (!this.rooms) return null;

options.search = options.search ? String(options.search).toLowerCase() : '';
Expand Down
4 changes: 4 additions & 0 deletions front-end/src/app/tabs/tabs.routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const routes: Routes = [
{
path: 'manage',
loadChildren: (): Promise<any> => import('./manage/manage.module').then(m => m.ManageModule)
},
{
path: 'venues',
loadChildren: (): Promise<any> => import('./venues/venues.module').then(m => m.VenuesModule)
}
]
}
Expand Down
36 changes: 36 additions & 0 deletions front-end/src/app/tabs/venues/venue.page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<ion-header>
<ion-toolbar>
<ion-title>{{ 'VENUES.DETAILS' | translate }}</ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-grid class="contentGrid">
<ion-row class="ion-justify-content-center">
<ion-col size="12" size-md="6">
<app-venue-card [venue]="venue"></app-venue-card>
</ion-col>
<ion-col *ngIf="rooms?.length" size="12" size-md="6">
<ion-list>
<ion-list-header>
<ion-label class="ion-text-center">
<h2>{{ 'VENUES.ROOMS' | translate }}</h2>
</ion-label>
</ion-list-header>
<ion-searchbar #searchbar color="light" (ionInput)="filterRooms($event.target.value)"></ion-searchbar>
<ion-col *ngIf="!rooms">
<app-room-card [preview]="true"></app-room-card>
</ion-col>
<ion-col *ngIf="rooms && rooms.length === 0">
<ion-item lines="none" color="white">
<ion-label class="ion-text-center">{{ 'COMMON.NO_ELEMENT_FOUND' | translate }}</ion-label>
</ion-item>
</ion-col>
<ion-col *ngFor="let room of rooms">
<app-room-card [room]="room" [preview]="true" (click)="app.goToInTabs(['rooms', room.roomId])"></app-room-card>
</ion-col>
</ion-list>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
Empty file.
51 changes: 51 additions & 0 deletions front-end/src/app/tabs/venues/venue.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { IDEALoadingService, IDEAMessageService } from '@idea-ionic/common';

import { AppService } from 'src/app/app.service';
import { VenuesService } from './venues.service';
import { RoomsService } from '../rooms/rooms.service';

import { Room } from '@models/room.model';
import { Venue } from '@models/venue.model';

@Component({
selector: 'app-venue',
templateUrl: './venue.page.html',
styleUrls: ['./venue.page.scss']
})
export class VenuePage implements OnInit {
venue: Venue;
rooms: Room[];

constructor(
private route: ActivatedRoute,
private loading: IDEALoadingService,
private message: IDEAMessageService,
private _venues: VenuesService,
private _rooms: RoomsService,
public app: AppService
) {}

async ngOnInit() {
await this.loadData();
}

async loadData() {
try {
await this.loading.show();
const venueId = this.route.snapshot.paramMap.get('venueId');
this.venue = await this._venues.getById(venueId);
this.rooms = await this._rooms.getList({ venue: this.venue.venueId, force: true });
} catch (err) {
this.message.error('COMMON.NOT_FOUND');
} finally {
await this.loading.hide();
}
}

async filterRooms(search: string = ''): Promise<void> {
this.rooms = await this._rooms.getList({ search, venue: this.venue.venueId });
}
}
64 changes: 64 additions & 0 deletions front-end/src/app/tabs/venues/venueCard.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';

import { IDEATranslationsModule } from '@idea-ionic/common';

import { AppService } from 'src/app/app.service';

import { Venue } from '@models/venue.model';

@Component({
standalone: true,
imports: [CommonModule, FormsModule, IonicModule, IDEATranslationsModule],
selector: 'app-venue-card',
template: `
<ion-card *ngIf="venue" color="white">
<ion-img [src]="app.getImageURLByURI(venue.imageURI)"></ion-img>
<ion-card-header>
<ion-card-title>{{ venue.name }}</ion-card-title>
<ion-card-subtitle>{{ venue.address }}</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<!-- @todo add map #61 -->
<!-- <div *ngIf="venue.address" class="ion-text-right" style="margin-bottom: 30px;">
<ion-button (click)="openMap(venue.latitude, venue.longitude)">
Navigate to the venue
<ion-icon slot="end" name="navigate"></ion-icon>
</ion-button>
</div> -->
<div class="divDescription" *ngIf="venue.description">
<ion-textarea readonly [rows]="4" [(ngModel)]="venue.description"></ion-textarea>
</div>
</ion-card-content>
</ion-card>
<ion-card *ngIf="!venue" color="white">
<ion-skeleton-text animated style="height: 200px;"></ion-skeleton-text>
<ion-card-header>
<ion-card-title>
<ion-skeleton-text animated style="width: 60%;"></ion-skeleton-text>
</ion-card-title>
<ion-card-subtitle>
<ion-skeleton-text animated style="width: 50%;"></ion-skeleton-text>
</ion-card-subtitle>
</ion-card-header>
<ion-card-content>
<ion-skeleton-text animated style="width: 80%;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 70%;"></ion-skeleton-text>
<ion-skeleton-text animated style="width: 60%;"></ion-skeleton-text>
</ion-card-content>
</ion-card>
`
})
export class VenueCardStandaloneComponent {
@Input() venue: Venue;

constructor(public app: AppService) {}

openMap(latitude: number, longitude: number): void {
return; // @todo add map #61
// openGeoLocationInMap(latitude, longitude);
}
}
16 changes: 16 additions & 0 deletions front-end/src/app/tabs/venues/venues-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { VenuesPage } from './venues.page';
import { VenuePage } from './venue.page';

const routes: Routes = [
{ path: '', component: VenuesPage },
{ path: ':venueId', component: VenuePage }
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class VenuesRoutingModule {}
25 changes: 25 additions & 0 deletions front-end/src/app/tabs/venues/venues.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { IDEATranslationsModule } from '@idea-ionic/common';

import { VenuesRoutingModule } from './venues-routing.module';
import { VenuesPage } from './venues.page';
import { VenuePage } from './venue.page';
import { VenueCardStandaloneComponent } from './venueCard.component';
import { RoomCardStandaloneComponent } from '../rooms/roomCard.component';

@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
IDEATranslationsModule,
VenuesRoutingModule,
VenueCardStandaloneComponent,
RoomCardStandaloneComponent
],
declarations: [VenuesPage, VenuePage]
})
export class VenuesModule {}
69 changes: 69 additions & 0 deletions front-end/src/app/tabs/venues/venues.page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<ion-header>
<ion-toolbar *ngIf="app.isInMobileMode()" color="ideaToolbar">
<ion-segment [(ngModel)]="segment">
<ion-segment-button value="map">
{{ 'VENUES.MAP' | translate }}
</ion-segment-button>
<ion-segment-button value="list">
{{ 'VENUES.LIST' | translate }}
</ion-segment-button>
</ion-segment>
</ion-toolbar>
</ion-header>

<ion-content>
<div *ngIf="venues">
<div *ngIf="app.isInMobileMode(); else desktopContent">
<div [style.display]="segment !== 'list' ? 'none' : 'inherit'">
<ion-list lines="inset" style="padding: 0; max-width: 500px; margin: 0 auto;">
<ion-searchbar #searchbar color="light" (ionInput)="filter($event.target.value)"></ion-searchbar>
<ion-item color="white" class="noElements" *ngIf="venues && !venues.length">
<ion-label>{{ 'COMMON.NO_ELEMENT_FOUND' | translate }}</ion-label>
</ion-item>
<ion-item color="white" *ngIf="!venues">
<ion-label><ion-skeleton-text animated></ion-skeleton-text></ion-label>
</ion-item>
<ion-item
color="white"
*ngFor="let venue of venues"
button
detail
(click)="selectVenue(venue)"
>
<ion-label class="ion-text-wrap">{{ venue.name }}</ion-label>
</ion-item>
</ion-list>
</div>
<div [style.display]="segment !== 'map' ? 'none' : 'inherit'">
<!-- Map View -->
<div id="main-map" #mapRef></div>
</div>
</div>
<ng-template #desktopContent>
<div style="width: 30%; float: left;">
<ion-list lines="inset" style="padding: 0; max-width: 500px; margin: 0 auto;">
<ion-searchbar #searchbar color="light" (ionInput)="filter($event.target.value)"></ion-searchbar>
<ion-item color="white" class="noElements" *ngIf="venues && !venues.length">
<ion-label>{{ 'COMMON.NO_ELEMENT_FOUND' | translate }}</ion-label>
</ion-item>
<ion-item color="white" *ngIf="!venues">
<ion-label><ion-skeleton-text animated></ion-skeleton-text></ion-label>
</ion-item>
<ion-item
color="white"
*ngFor="let venue of venues"
button
detail
(click)="selectVenue(venue)"
>
<ion-label class="ion-text-wrap">{{ venue.name }}</ion-label>
</ion-item>
</ion-list>
</div>
<div style="width: 70%; float: right; right: 0; position: fixed;">
<!-- Desktop Map View -->
<div id="main-map" #mapRef></div>
</div>
</ng-template>
</div>
</ion-content>
Empty file.
Loading

0 comments on commit 2cfb232

Please sign in to comment.