diff --git a/client/src/app/core/layout/layout.component.html b/client/src/app/core/layout/layout.component.html index 285b7ba..5385042 100644 --- a/client/src/app/core/layout/layout.component.html +++ b/client/src/app/core/layout/layout.component.html @@ -34,11 +34,13 @@
-
+
- {{ date | date : "dd MMM yyyy" }} + {{ date | date : "dd MMM yyyy HH:mm:ss" }}

(); @@ -42,6 +43,21 @@ export class LayoutComponent implements OnInit { this._userService.currentUser .pipe(takeUntil(this.exit$)) .subscribe((user) => (this.user = user)); + + this.clockSubscription = timer(0, 1000) + .pipe( + map(() => new Date()), + share() + ) + .subscribe((time) => { + this.date = time; + }); + } + + ngOnDestroy() { + this.exit$.next(); + this.exit$.complete(); + this.clockSubscription?.unsubscribe(); } logout() { diff --git a/client/src/app/core/services/recipe.service.ts b/client/src/app/core/services/recipe.service.ts index c6a4509..21abd16 100644 --- a/client/src/app/core/services/recipe.service.ts +++ b/client/src/app/core/services/recipe.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { BehaviorSubject, Observable, distinctUntilChanged } from 'rxjs'; +import { BehaviorSubject, Observable, distinctUntilChanged, tap } from 'rxjs'; import { Recipe, Recipe01 } from '../models/recipe.model'; import { environment } from 'src/environments/environment'; @@ -20,27 +20,44 @@ export class RecipeService { take: 10, offset: 0, search: '', - version: 'coffeethai02_580.json', + version: this.getCurrentVersion(), } ): Observable<{ fileName: string; recipes: Recipe; hasMore: boolean; }> { - return this._httpClient.get<{ - fileName: string; - recipes: Recipe; - hasMore: boolean; - }>(environment.api + '/recipes', { - params: { - offset: params.offset, - take: params.take, - search: params.search, - version: params.version, - }, - withCredentials: true, - responseType: 'json', - }); + return this._httpClient + .get<{ + fileName: string; + recipes: Recipe; + hasMore: boolean; + }>(environment.api + '/recipes', { + params: { + offset: params.offset, + take: params.take, + search: params.search, + version: params.version, + }, + withCredentials: true, + responseType: 'json', + }) + .pipe( + tap((data) => { + if (data.fileName !== this.getCurrentVersion()) { + localStorage.setItem('currentRecipeVersion', data.fileName); + } + }) + ); + } + + getCurrentVersion(): string { + const currentRecipeVersion = localStorage.getItem('currentRecipeVersion'); + if (currentRecipeVersion) { + return currentRecipeVersion; + } + + return 'coffeethai02_580.json'; } getRecipesById(id: string): Observable { diff --git a/client/src/app/features/dashboard/dashboard.component.ts b/client/src/app/features/dashboard/dashboard.component.ts index effe437..ed57892 100644 --- a/client/src/app/features/dashboard/dashboard.component.ts +++ b/client/src/app/features/dashboard/dashboard.component.ts @@ -5,7 +5,7 @@ import { DatePipe, NgFor, NgIf } from '@angular/common'; import { Recipe, Recipe01 } from 'src/app/core/models/recipe.model'; import { RecipeService } from 'src/app/core/services/recipe.service'; import { environment } from 'src/environments/environment'; -import { RecipeModalComponent } from 'src/app/shared/modal/recipe-modal.component'; +import { RecipeModalComponent } from 'src/app/shared/modal/recipe-details/recipe-modal.component'; import { BehaviorSubject } from 'rxjs'; import * as lodash from 'lodash'; @@ -32,7 +32,6 @@ export class DashboardComponent implements OnInit { private offset = 0; private take = 20; private recipeVersionData: string[] = []; - private currentRecipeVersion: string = 'coffeethai02_580.json'; recipeVersion: BehaviorSubject = new BehaviorSubject(''); recipeVersion$ = this.recipeVersion.asObservable(); @@ -61,7 +60,7 @@ export class DashboardComponent implements OnInit { offset: this.offset, take: this.take, search: this.oldSearchStr, - version: this.currentRecipeVersion, + version: this._recipeService.getCurrentVersion(), }) .subscribe(({ recipes, hasMore, fileName }) => { const { Recipe01, ...recipesWithoutRecipe01 } = recipes; @@ -100,7 +99,7 @@ export class DashboardComponent implements OnInit { offset: this.offset, take: this.take, search: this.oldSearchStr, - version: this.currentRecipeVersion, + version: this._recipeService.getCurrentVersion(), }) .subscribe(({ recipes, hasMore, fileName }) => { const { Recipe01, ...recipesWithoutRecipe01 } = recipes; @@ -139,7 +138,7 @@ export class DashboardComponent implements OnInit { offset: this.offset, take: this.take, search: this.searchStr, - version: this.currentRecipeVersion, + version: this._recipeService.getCurrentVersion(), }) .subscribe(({ recipes, hasMore, fileName }) => { const { Recipe01, ...recipesWithoutRecipe01 } = recipes; @@ -165,7 +164,6 @@ export class DashboardComponent implements OnInit { this.isHasMore = true; this.isLoadMore = false; this.oldSearchStr = ''; - this.currentRecipeVersion = recipeVersion; this._recipeService .getRecipes({ @@ -206,7 +204,8 @@ export class DashboardComponent implements OnInit { openJsonTab() { window.open( - environment.api + `/recipes/${this.currentRecipeVersion}/json`, + environment.api + + `/recipes/${this._recipeService.getCurrentVersion()}/json`, '_blank' ); } diff --git a/client/src/app/shared/modal/confirm/confirm-modal.component.ts b/client/src/app/shared/modal/confirm/confirm-modal.component.ts new file mode 100644 index 0000000..cd3d570 --- /dev/null +++ b/client/src/app/shared/modal/confirm/confirm-modal.component.ts @@ -0,0 +1,57 @@ +import { + Component, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, +} from '@angular/core'; + +@Component({ + selector: 'confirm-modal', + template: `

+ + `, + standalone: true, +}) +export class ConfirmModal implements OnInit { + @Input() show: EventEmitter = new EventEmitter(); + @Input() title: string = 'Confirm Dialog'; + @Input() message: string = + 'This is Confirm Modal. You can change message by using message input'; + @Input() confirmCallBack: () => void = () => {}; + + private confirmModal: ElementRef | null = null; + + @ViewChild('confirmModal', { static: false }) set setConfirmModal( + modal: ElementRef + ) { + this.confirmModal = modal; + } + + constructor() {} + + ngOnInit() { + this.show.subscribe((show) => { + if (show) { + this.confirmModal?.nativeElement.showModal(); + } else { + this.confirmModal?.nativeElement.close(); + } + }); + } + + close() { + this.confirmModal?.nativeElement.close(); + } +} diff --git a/client/src/app/shared/modal/recipe-modal.component.html b/client/src/app/shared/modal/recipe-details/recipe-modal.component.html similarity index 86% rename from client/src/app/shared/modal/recipe-modal.component.html rename to client/src/app/shared/modal/recipe-details/recipe-modal.component.html index 01e3870..7ef38fb 100644 --- a/client/src/app/shared/modal/recipe-modal.component.html +++ b/client/src/app/shared/modal/recipe-details/recipe-modal.component.html @@ -97,23 +97,22 @@
- -
-
- + - - diff --git a/client/src/app/shared/modal/recipe-modal.component.ts b/client/src/app/shared/modal/recipe-details/recipe-modal.component.ts similarity index 70% rename from client/src/app/shared/modal/recipe-modal.component.ts rename to client/src/app/shared/modal/recipe-details/recipe-modal.component.ts index ef58e3c..ca7439e 100644 --- a/client/src/app/shared/modal/recipe-modal.component.ts +++ b/client/src/app/shared/modal/recipe-details/recipe-modal.component.ts @@ -1,7 +1,14 @@ -import { Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { + Component, + ElementRef, + EventEmitter, + Input, + ViewChild, +} from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { isEqual } from 'lodash'; import { RecipeService } from 'src/app/core/services/recipe.service'; +import { ConfirmModal } from '../confirm/confirm-modal.component'; interface RecipeDetail { productCode: string; @@ -18,7 +25,7 @@ interface RecipeDetail { @Component({ selector: 'recipe-modal', templateUrl: './recipe-modal.component.html', - imports: [ReactiveFormsModule], + imports: [ReactiveFormsModule, ConfirmModal], standalone: true, }) export class RecipeModalComponent { @@ -48,14 +55,6 @@ export class RecipeModalComponent { this.detailModal = modal; } - private confirmModal: ElementRef | null = null; - - @ViewChild('confirmModal', { static: false }) set setConfirmModal( - modal: ElementRef - ) { - this.confirmModal = modal; - } - constructor(private recipeService: RecipeService) {} openModal() { @@ -93,14 +92,38 @@ export class RecipeModalComponent { } } - save() { - console.log(this.recipeDetail.value); - this.detailModal?.nativeElement.close(); + showConfirmSaveModal: EventEmitter = new EventEmitter(); + showConfirmCloseModal: EventEmitter = new EventEmitter(); + + confirmSave = { + title: 'The changes detected!', + message: 'Do you want to save changes?', + confirmCallBack: () => { + console.log('confirm save'); + this.detailModal?.nativeElement.close(); + }, + }; + + confirmClose = { + title: 'The changes will be lost!', + message: 'Do you want to close without saving?', + confirmCallBack: () => { + console.log('confirm close'); + this.detailModal?.nativeElement.close(); + }, + }; + + onConfirmSave() { + if (!isEqual(this.recipeDetail.value, this.originalRecipeDetail)) { + this.showConfirmSaveModal.emit(true); + } else { + this.detailModal?.nativeElement.close(); + } } - close() { + onConfirmClose() { if (!isEqual(this.recipeDetail.value, this.originalRecipeDetail)) { - this.confirmModal?.nativeElement.showModal(); + this.showConfirmCloseModal.emit(true); } else { this.detailModal?.nativeElement.close(); } diff --git a/server/routers/recipe.go b/server/routers/recipe.go index bcb08d5..c6a5f69 100644 --- a/server/routers/recipe.go +++ b/server/routers/recipe.go @@ -5,6 +5,7 @@ import ( "net/http" "recipe-manager/data" "recipe-manager/models" + "recipe-manager/services/sheet" "sort" "strconv" "strings" @@ -13,12 +14,14 @@ import ( ) type RecipeRouter struct { - data *data.Data + data *data.Data + sheetService sheet.SheetService } -func NewRecipeRouter(data *data.Data) *RecipeRouter { +func NewRecipeRouter(data *data.Data, sheetService sheet.SheetService) *RecipeRouter { return &RecipeRouter{ - data: data, + data: data, + sheetService: sheetService, } } @@ -99,5 +102,10 @@ func (rr *RecipeRouter) Route(r chi.Router) { r.Get("/versions", func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(rr.data.AllVersions) }) + + r.Get("/test/sheet", func(w http.ResponseWriter, r *http.Request) { + result := rr.sheetService.GetSheet(r.Context(), "1rSUKcc5POR1KeZFGoeAZIoVoI7LPGztBhPw5Z_ConDE") + json.NewEncoder(w).Encode(result) + }) }) } diff --git a/server/server.go b/server/server.go index e98b0bd..3156892 100644 --- a/server/server.go +++ b/server/server.go @@ -17,6 +17,7 @@ import ( "recipe-manager/services/cli" "recipe-manager/services/logger" "recipe-manager/services/oauth" + "recipe-manager/services/sheet" "strings" "sync" "time" @@ -437,7 +438,14 @@ func (s *Server) createHandler() { }) // Recipe Router - rr := routers.NewRecipeRouter(database) + sheetService, err := sheet.NewSheetService(context.Background()) + + if err != nil { + Log.Fatal("Error while trying to create sheet service: ", zap.Error(err)) + return + } + + rr := routers.NewRecipeRouter(database, sheetService) rr.Route(r) }) diff --git a/server/services/sheet/sheet.go b/server/services/sheet/sheet.go index f2cdc48..184b7ec 100644 --- a/server/services/sheet/sheet.go +++ b/server/services/sheet/sheet.go @@ -10,6 +10,7 @@ import ( ) type SheetService interface { + GetSheet(ctx context.Context, sheetID string) [][]interface{} } type sheetService struct { @@ -42,28 +43,35 @@ func NewSheetService(ctx context.Context) (SheetService, error) { }, nil } -func (s *sheetService) GetSheet(ctx context.Context, sheetID string) { +func (s *sheetService) GetSheet(ctx context.Context, sheetID string) [][]interface{} { spreadSheet, err := s.service.Spreadsheets.Get(sheetID).Do() if err != nil { panic(err) } - for _, sheet := range spreadSheet.Sheets { + var sheetIndex int + for i, sheet := range spreadSheet.Sheets { // print data collumn C - readRange := sheet.Properties.Title + "!C:C" - resp, err := s.service.Spreadsheets.Values.Get(sheetID, readRange).Do() - - if err != nil { - panic(err) - } - - if len(resp.Values) == 0 { - println("No data found") - } else { - for _, row := range resp.Values { - println(row[0].(string)) - } + if sheet.Properties.Title == "menu-name" { + sheetIndex = i + break } } + + sheet, err := s.service.Spreadsheets.Values.Get(sheetID, spreadSheet.Sheets[sheetIndex].Properties.Title).Do() + + if err != nil { + panic(err) + } + + result := [][]interface{}{} + + for _, row := range sheet.Values { + if len(row) >= 6 { + result = append(result, row) + } + } + + return result }