diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 12c7e255986f949e4228d227e1039e1a0f10d232..6ff88d19869478a72145ef9647d2516e431d6601 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -24,9 +24,11 @@ import { environment } from 'src/environments/environment'; import { reducersMap } from './share/store'; import { AuthEffects } from './share/store/effects/auth.effects'; import { BarEffects } from './share/store/effects/bar.effects'; +import { DrinksComponent } from './components/drinks/drinks.component'; +import { ToDegreePipe } from './share/pipes/to-degree.pipe'; @NgModule({ - declarations: [AppComponent], + declarations: [AppComponent, ToDegreePipe], imports: [ BrowserModule, BrowserAnimationsModule, diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index c19269af79029ea223475624fa8b5bd95d2058fb..39f04cba899ef14ddc43157f4352891aa81610d3 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -1,5 +1,6 @@ import { Route } from "@angular/router"; import { BarsComponent } from "./components/bars/bars.component"; +import { DrinksComponent } from "./components/drinks/drinks.component"; import { SigninComponent } from "./components/signin/signin.component"; import { SignupComponent } from "./components/signup/signup.component"; import { WelcomeComponent } from "./components/welcome/welcome.component"; @@ -11,6 +12,7 @@ export const APP_ROUTING: Route[] = [ { path: 'photos', canActivate: [AuthGuard], loadChildren: () => import('./photos/photos.module').then(m => m.PhotosModule) }, { path: 'profile', canActivate: [AuthGuard], loadChildren: () => import('./profile/profile.module').then(m => m.ProfileModule) }, { path: 'bars', canActivate: [AuthGuard], component: BarsComponent }, + { path: 'menu/:id', canActivate: [AuthGuard], component: DrinksComponent }, { path: 'signup', canActivate: [AlreadyLoggedInGuard], component: SignupComponent }, { path: 'signin', canActivate: [AlreadyLoggedInGuard], component: SigninComponent }, { path: '', redirectTo: '', pathMatch: 'full' }, diff --git a/src/app/components/bars/bars.component.html b/src/app/components/bars/bars.component.html index 01a41e2bd17a8dcab408ab3b5c07a760ab4256d7..c662c94e66c87bf635e4081da1cc2e8c6743207a 100644 --- a/src/app/components/bars/bars.component.html +++ b/src/app/components/bars/bars.component.html @@ -13,21 +13,7 @@ <div>{{ bar.address }}</div> </div> <div class="card-action"> - <button mat-raised-button color='accent'>choisir</button> + <button mat-raised-button routerLink="/menu/{{bar._id}}" color='accent'>choisir</button> </div> </div> -</section> -<!-- <section> - <div class=" card row-container center"> - <div class="card-header"> - <img class="logo-img" src="https://fakeimg.pl/300/" alt="fake-img"> - </div> - <div class="card-content"> - <p>Déliriuem Café clermont-ferrand</p> - <div>{{ bar.address }}</div> - </div> - <div class="card-action"> - <button mat-raised-button color='accent'>choisir</button> - </div> - </div> -</section> --> +</section> \ No newline at end of file diff --git a/src/app/components/bars/bars.component.ts b/src/app/components/bars/bars.component.ts index 8f2c600af103c5aeca634ba7cab5ccc763632d47..22aa225dca4674103c7407b52dfbf62ba3ea4930 100644 --- a/src/app/components/bars/bars.component.ts +++ b/src/app/components/bars/bars.component.ts @@ -4,8 +4,7 @@ import { Bar } from '../../share/models/bar.model'; import { Store } from '@ngrx/store'; import { State } from '../../share/store'; import { allBarsSelector } from '../../share/store/selectors/bar.selectors'; -import { BarActionsTypes, FetchBars, FetchBarsSuccess } from 'src/app/share/store/actions/bar.actions'; -import { BarService } from 'src/app/share/services/bar.service'; +import { FetchBars } from 'src/app/share/store/actions/bar.actions'; @Component({ selector: 'app-bars', diff --git a/src/app/components/drinks/drinks.component.css b/src/app/components/drinks/drinks.component.css new file mode 100644 index 0000000000000000000000000000000000000000..b06396fde05b448eb00a9a8bdf7c7bda5d1e3b72 --- /dev/null +++ b/src/app/components/drinks/drinks.component.css @@ -0,0 +1,145 @@ +.row-container { + /* display: flex; + flex-flow: row wrap; */ + grid-template-columns: 1fr 1fr 1fr 1fr; +} + +/* Au dessus de 430px de large, on place deux cards par ligne */ +.band { + width: 90%; + margin: 10px auto; + + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto; + grid-gap: 20px; + + justify-content: center; +} + +@media only screen and (min-width: 430px) { + .band { + grid-template-columns: 1fr 1fr; + } +} + +@media only screen and (min-width: 700px) { + .band { + grid-template-columns: 1fr 1fr 1fr; + } +} + +@media only screen and (min-width: 950px) { + .band { + grid-template-columns: 1fr 1fr 1fr 1fr; + } +} + +.drink-card { + min-height: 100%; + max-width: 200px; + background: white; + padding: 10px; + + box-shadow: 0 2px 2px rgba(0,0,0,0.1); + border-radius: 4px; + + display: flex; + flex-flow: column nowrap; + + color: #222222; + + top: 0; + position: relative; + transition: all .1s ease-in; +} + +article .xp-number { + color: #EAE000; + font-size: 1.8em; + font-weight: bolder; +} + +article .xp { + font-size: 1.3em; + font-weight: bolder; + text-transform: uppercase; + color: #000000; +} + +.drink-card:hover { + top: -2px; + box-shadow: 0 4px 5px rgba(0,0,0,0.2); +} + +.title { + color: #6E2424; + font-size: 1.1em; + font-weight: bold; +} + +.thumb { + padding: 5% 5% 60% 5%; + background-size: cover; + background-position: center center; +} + +.drink-card > hr { + margin-top: 0; + margin-bottom: 3px; +} + +.description { + font-size: 0.7em; + color: #95A5A6; +} + +.drink-card button { + align-self: flex-end; + margin: 0 auto +} + + + +/*a moi*/ + +.card-header { + background-color: transparent; + align-self: flex-start; +} + +.card-content { + align-self: center; +} + +.card-action { + align-self: flex-end; +} + +.card-header > img { + height: 130px; + width: 130px; +} + +.card-header > p { + color: #6E2424; + font-size: 1.1em; + font-weight: bold; +} + +.card-header > p, .card-header > img { + margin: 5px auto; +} + +.column-container { + display: flex; + flex-flow: column nowrap; +} + +.h-center { + text-align: center; +} + +.v-center { + text-align: center; +} \ No newline at end of file diff --git a/src/app/components/drinks/drinks.component.html b/src/app/components/drinks/drinks.component.html new file mode 100644 index 0000000000000000000000000000000000000000..543fc7d1627ed6c586198320f00d5e91106721b5 --- /dev/null +++ b/src/app/components/drinks/drinks.component.html @@ -0,0 +1,13 @@ +<div class="band"> + <div *ngFor="let drink of (menu$ | async).drinks" class="drink-card"> + <h3 class="title">{{ drink.shortname }}</h3> + <div class="thumb" style="background-image: url(https://fakeimg.pl/250x250/);"></div> + <hr> + <article> + <p>degrée : <strong>{{ drink.abv }}°</strong> / Prix : <strong>7.5€</strong></p> + <span class="xp-number">1200</span><span class="xp">xp</span> + <p class="description">{{ drink.description }}</p> + </article> + <button mat-raised-button routerLink="/menu" color='accent'>commander</button> + </div> +</div> \ No newline at end of file diff --git a/src/app/components/drinks/drinks.component.ts b/src/app/components/drinks/drinks.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e42be5030e4c2f45d60565e984b7418b222bb8ee --- /dev/null +++ b/src/app/components/drinks/drinks.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; + +import { currentMenuSelector } from '../../share/store/selectors/bar.selectors'; +import { FetchBarMenu } from '../../share/store/actions/bar.actions'; +import { Menu } from '../../share/models/menu.model'; +import { State } from '../../share/store'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'app-drinks', + templateUrl: './drinks.component.html', + styleUrls: ['./drinks.component.css'] +}) +export class DrinksComponent implements OnInit { + public menu$: Observable<Menu> = this.store.select(currentMenuSelector); + public tests: number[] = [0, 1, 2, 3, 4]; + + constructor( + private activatedRoute: ActivatedRoute, + private store: Store<State>, + ) {} + + ngOnInit(): void { + this.activatedRoute.params.subscribe(params => { + const barId = params['id']; + + if (barId) { + this.store.dispatch(new FetchBarMenu(barId)); + } + }); + + } +} diff --git a/src/app/share/components/topbar/topbar.component.html b/src/app/share/components/topbar/topbar.component.html index 0e858e7f3e199a23f298a968d143ea8c790e1934..e59ca75111ab28476a4de4ecddb41e4ceeb06426 100644 --- a/src/app/share/components/topbar/topbar.component.html +++ b/src/app/share/components/topbar/topbar.component.html @@ -12,9 +12,9 @@ <!-- <span *ngIf="!(isLoggedIn$ | async)" fxLayoutGap="15px" fxLayout="row" fxLayoutAlign="center center"> <mat-icon class="link" routerLink="/signin">login</mat-icon> <mat-icon class="link" routerLink="/signup">launch</mat-icon> - </span> + </span> --> <span *ngIf="isLoggedIn$ | async" fxLayoutGap="15px" fxLayout="row" fxLayoutAlign="center center"> - <mat-icon class="link" routerLink="/profile">account_circle</mat-icon> + <!-- <mat-icon class="link" routerLink="/profile">account_circle</mat-icon> --> <mat-icon class="link" routerLink="/" (click)="onLogout()">power_settings_new</mat-icon> </span> --> </mat-toolbar> diff --git a/src/app/share/models/bar.model.ts b/src/app/share/models/bar.model.ts index b4d59963b72d777f81de243368982aee613cfb0b..e760487bd2011306ecc5e750c16d643259012cf1 100644 --- a/src/app/share/models/bar.model.ts +++ b/src/app/share/models/bar.model.ts @@ -1,4 +1,5 @@ export interface Bar { + _id: string; shortname: string; longname: string; description: string; diff --git a/src/app/share/models/drink.model.ts b/src/app/share/models/drink.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..97419d94c75df77e0da129573f303ebf8a19577c --- /dev/null +++ b/src/app/share/models/drink.model.ts @@ -0,0 +1,10 @@ +export interface Drink { + id: number, + shortname: string, + longname: string, + description: string, + idFamilyDrink: number, + abv: string, + img: string, + sizes: number[], +} \ No newline at end of file diff --git a/src/app/share/models/menu.model.ts b/src/app/share/models/menu.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..4833831b5dd35d1cfdcd897657e2a7823672c254 --- /dev/null +++ b/src/app/share/models/menu.model.ts @@ -0,0 +1,7 @@ +import { Bar } from "./bar.model"; +import { Drink } from "./drink.model"; + +export interface Menu { + bar: Bar; + drinks: Drink[]; +} \ No newline at end of file diff --git a/src/app/share/modules/core.module.ts b/src/app/share/modules/core.module.ts index 887ac0288c58ae8afd8ed02016faf60a0ead3b4c..8518e41bf4a869c53c3b26c2522d707f367113ca 100644 --- a/src/app/share/modules/core.module.ts +++ b/src/app/share/modules/core.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule } from '@angular/forms'; // Modules import { LayoutModule } from './layout.module'; @@ -17,19 +18,20 @@ import { AuthGuard } from '../guards/auth.guard'; import { SignupComponent } from '../../components/signup/signup.component'; import { SigninComponent } from '../../components/signin/signin.component'; import { TopbarComponent } from '../components/topbar/topbar.component'; +import { WelcomeComponent } from '../../components/welcome/welcome.component'; +import { BarsComponent } from '../../components/bars/bars.component'; +import { DrinksComponent } from '../../components/drinks/drinks.component'; // Interceptors import { AuthInterceptor } from '../interceptors/auth.interceptor'; -import { ReactiveFormsModule } from '@angular/forms'; -import { WelcomeComponent } from 'src/app/components/welcome/welcome.component'; -import { BarsComponent } from 'src/app/components/bars/bars.component'; const COMPONENTS = [ SignupComponent, SigninComponent, WelcomeComponent, BarsComponent, - TopbarComponent + TopbarComponent, + DrinksComponent ]; @NgModule({ diff --git a/src/app/share/pipes/to-degree.pipe.ts b/src/app/share/pipes/to-degree.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..adaaf69752bd614774677d7918ed853fe9f816a5 --- /dev/null +++ b/src/app/share/pipes/to-degree.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'toDegree' +}) +export class ToDegreePipe implements PipeTransform { + + transform(value: string, ...args: unknown[]): number { + + return (+value) * 100; + } + +} diff --git a/src/app/share/services/bar.service.ts b/src/app/share/services/bar.service.ts index 59a0c0a9c27f9f1a9b05b34213009ec0e99d592c..1812bd12910e8667a7b4561d8bdffb1ec7887e89 100644 --- a/src/app/share/services/bar.service.ts +++ b/src/app/share/services/bar.service.ts @@ -1,6 +1,8 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; + +import { Menu } from '../models/menu.model'; import { Bar } from '../models/bar.model'; @Injectable({ @@ -13,5 +15,9 @@ export class BarService { // authInterceptor add the token in the HTTP request public getAll(): Observable<Bar[]> { return this.http.get<Bar[]>('/api/v1/bars'); - } + } + + public getMenu(barId: string): Observable<Menu> { + return this.http.get<Menu>(`/api/v1/menu/${barId}`) + } } diff --git a/src/app/share/store/actions/bar.actions.ts b/src/app/share/store/actions/bar.actions.ts index 91ac24d702a973b0dbc2bf77c406964849366ab4..1c4863cbdbc0df6d5d019588696a45fc49a9adb0 100644 --- a/src/app/share/store/actions/bar.actions.ts +++ b/src/app/share/store/actions/bar.actions.ts @@ -1,10 +1,14 @@ import { Action } from "@ngrx/store"; import { Bar } from "../../models/bar.model"; +import { Menu } from "../../models/menu.model"; export enum BarActionsTypes { FETCH_BARS = '[bars/api] fetch all bars', FETCH_BARS_SUCCESS = '[bars/api] fetch bars success', - FETCH_BARS_FAILED = '[bars/api] fetch bars failed' + FETCH_BARS_FAILED = '[bars/api] fetch bars failed', + FETCH_BAR_MENU = '[bars/menu/api] fetch bar menu', + FETCH_BAR_MENU_SUCCESS = '[bar/menu/api] fetch bar menu success', + FETCH_BAR_MENU_FAILED = '[bars/menu/api] fetch bar menu failed', } export class FetchBars implements Action { @@ -21,4 +25,24 @@ export class FetchBarsFailed implements Action { constructor(public payload: string) {} } -export type BarsActions = FetchBars | FetchBarsSuccess | FetchBarsFailed; \ No newline at end of file +export class FetchBarMenu implements Action { + readonly type = BarActionsTypes.FETCH_BAR_MENU; + constructor(public payload: string) {} +} + +export class FetchBarMenuSuccess implements Action { + readonly type = BarActionsTypes.FETCH_BAR_MENU_SUCCESS; + constructor(public payload: Menu) {} +} + +export class FetchBarMenuFailed implements Action { + readonly type = BarActionsTypes.FETCH_BAR_MENU_FAILED; + constructor(public payload: string) {} +} + +export type BarsActions = FetchBars | + FetchBarsSuccess | + FetchBarsFailed | + FetchBarMenu | + FetchBarMenuSuccess | + FetchBarMenuFailed; \ No newline at end of file diff --git a/src/app/share/store/effects/bar.effects.ts b/src/app/share/store/effects/bar.effects.ts index 634e67047b5e7892e0c78418ec3f48777e9e1dfd..0bb5c0ffc34b29739d8175332fc7b0fa38066c5e 100644 --- a/src/app/share/store/effects/bar.effects.ts +++ b/src/app/share/store/effects/bar.effects.ts @@ -1,17 +1,22 @@ import { Actions, createEffect, ofType } from "@ngrx/effects"; import { Injectable } from "@angular/core"; -import { catchError, map, mergeMap } from "rxjs/operators"; +import { catchError, map, mergeMap, withLatestFrom } from "rxjs/operators"; import { EMPTY, of } from "rxjs"; -import { BarActionsTypes, FetchBarsFailed, FetchBarsSuccess } from "../actions/bar.actions"; +import { BarActionsTypes, FetchBarMenuFailed, FetchBarMenuSuccess, FetchBarsFailed, FetchBarsSuccess } from "../actions/bar.actions"; import { BarService } from "../../services/bar.service"; import { Bar } from "../../models/bar.model"; +import { Menu } from "../../models/menu.model"; +import { Store } from "@ngrx/store"; +import { State } from ".."; +import { currentBarIdSelector } from "../selectors/bar.selectors"; @Injectable() export class BarEffects { constructor( private actions$: Actions, - private barService: BarService + private barService: BarService, + private store: Store<State> ) {} loadBars$ = createEffect(() => this.actions$.pipe( @@ -22,4 +27,14 @@ export class BarEffects { catchError(() => of(new FetchBarsFailed('failed fetching all bars'))) )) )); + + loadMenu$ = createEffect(() => this.actions$.pipe( + ofType(BarActionsTypes.FETCH_BAR_MENU), + withLatestFrom(this.store.select(currentBarIdSelector)), + mergeMap(([_, barId]) => this.barService.getMenu(barId) + .pipe( + map((menu: Menu) => (new FetchBarMenuSuccess(menu))), + catchError(() => of(new FetchBarMenuFailed(`failed fetch bar's menu`))) + )) + )); } \ No newline at end of file diff --git a/src/app/share/store/reducers/bar.reducers.ts b/src/app/share/store/reducers/bar.reducers.ts index fe643afdb904aea43c5ec93f08767afd1dba5f49..1d399dfb5423e46625e328aa02921470e87637e1 100644 --- a/src/app/share/store/reducers/bar.reducers.ts +++ b/src/app/share/store/reducers/bar.reducers.ts @@ -1,21 +1,31 @@ import { Bar } from "../../models/bar.model"; +import { Menu } from "../../models/menu.model"; import { BarsActions, BarActionsTypes } from "../actions/bar.actions"; export interface BarState { bars: Bar[]; - currentBar: Bar; + currentMenu: Menu; + currentBarId: string; error: string; } const defaultBarState = { bars: [], - currentBar: null, + currentMenu: null, + currentBarId: '', error: '', } export function barReducer(state: BarState = defaultBarState, action: BarsActions): BarState { switch (action.type) { + case BarActionsTypes.FETCH_BAR_MENU: { + return { + ...state, + currentBarId: action.payload + }; + } + case BarActionsTypes.FETCH_BARS_SUCCESS: { return { ...state, @@ -23,6 +33,14 @@ export function barReducer(state: BarState = defaultBarState, action: BarsAction }; } + case BarActionsTypes.FETCH_BAR_MENU_SUCCESS: { + return { + ...state, + currentMenu: action.payload + }; + } + + case BarActionsTypes.FETCH_BAR_MENU_FAILED: case BarActionsTypes.FETCH_BARS_FAILED: { return { ...state, diff --git a/src/app/share/store/selectors/bar.selectors.ts b/src/app/share/store/selectors/bar.selectors.ts index e4f7a626459aa476ee0cb4c7795cf161f87f20f9..be1d0a1d69f8cba4c2230d2d23a06cf1b455bba1 100644 --- a/src/app/share/store/selectors/bar.selectors.ts +++ b/src/app/share/store/selectors/bar.selectors.ts @@ -5,4 +5,6 @@ export const baseSelector = createFeatureSelector('bars'); export const allBarsSelector = createSelector(baseSelector, (barState: BarState) => barState?.bars); -export const currentBarSelector = createSelector(baseSelector, (barState: BarState) => barState?.currentBar); \ No newline at end of file +export const currentMenuSelector = createSelector(baseSelector, (barState: BarState) => barState?.currentMenu); + +export const currentBarIdSelector = createSelector(baseSelector, (barState: BarState) => barState?.currentBarId); \ No newline at end of file diff --git a/src/styles.css b/src/styles.css index 28c4700ee95277b0c666fe08b202f1438796c8ba..36a4f8ae8f9f42a1ae69cc127bb8edcc4631893a 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,8 +1,17 @@ /* You can add global styles to this file, and also import other style files */ @import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; -html, body { height: 100%; } -body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; background-color: #f1f1f1; text-align: center; color: #222222} +html, body { + height: 100%; +} + +body { + margin: 0; + font-family: Roboto, "Helvetica Neue", sans-serif; + background-color: #f1f1f1; + text-align: center; + color: #222222; +} .link { outline: 0;