This commit is contained in:
pakintada@gmail.com 2023-10-04 15:42:12 +07:00
commit b7b4bca78e
10 changed files with 214 additions and 77 deletions

View file

@ -34,11 +34,13 @@
<div class="flex items-center"> <div class="flex items-center">
<div class="flex items-center ml-3"> <div class="flex items-center ml-3">
<div class="flex flex-row"> <div class="flex flex-row">
<div class="flex justify-center sm:flex-row md:flex-col sm:px-5"> <div
class="flex justify-center items-center sm:flex-row md:flex-col sm:px-5 w-[250px]"
>
<span <span
class="flex items-center text-primary max-sm:text-xs sm:text-sm md:text-xl font-normal font-kanit" class="flex items-center text-primary max-sm:text-xs sm:text-sm md:text-xl font-normal font-kanit"
> >
{{ date | date : "dd MMM yyyy" }} {{ date | date : "dd MMM yyyy HH:mm:ss" }}
</span> </span>
<p <p
class="md:hidden px-1 flex items-center text-primary font-normal font-kanit" class="md:hidden px-1 flex items-center text-primary font-normal font-kanit"

View file

@ -1,10 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { DatePipe, NgFor, NgIf } from '@angular/common'; import { DatePipe, NgFor, NgIf } from '@angular/common';
import { GoogleButtonComponent } from 'src/app/shared/googleButton/googleButton.component'; import { GoogleButtonComponent } from 'src/app/shared/googleButton/googleButton.component';
import { UserService } from '../services/user.service'; import { UserService } from '../services/user.service';
import { User } from '../models/user.model'; import { User } from '../models/user.model';
import { Subject, takeUntil } from 'rxjs'; import { Subject, Subscription, map, share, takeUntil, timer } from 'rxjs';
interface MenuItem { interface MenuItem {
name: string; name: string;
@ -18,7 +18,7 @@ interface MenuItem {
standalone: true, standalone: true,
imports: [RouterModule, NgFor, NgIf, GoogleButtonComponent, DatePipe], imports: [RouterModule, NgFor, NgIf, GoogleButtonComponent, DatePipe],
}) })
export class LayoutComponent implements OnInit { export class LayoutComponent implements OnInit, OnDestroy {
menuItems: MenuItem[] = [ menuItems: MenuItem[] = [
{ {
name: 'Dashboard', name: 'Dashboard',
@ -32,6 +32,7 @@ export class LayoutComponent implements OnInit {
}, },
]; ];
date = new Date(); date = new Date();
clockSubscription: Subscription | null = null;
isMenuOpen: boolean = false; isMenuOpen: boolean = false;
user: User | null = null; user: User | null = null;
exit$ = new Subject<void>(); exit$ = new Subject<void>();
@ -42,6 +43,21 @@ export class LayoutComponent implements OnInit {
this._userService.currentUser this._userService.currentUser
.pipe(takeUntil(this.exit$)) .pipe(takeUntil(this.exit$))
.subscribe((user) => (this.user = user)); .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() { logout() {

View file

@ -1,6 +1,6 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; 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 { Recipe, Recipe01 } from '../models/recipe.model';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@ -20,27 +20,44 @@ export class RecipeService {
take: 10, take: 10,
offset: 0, offset: 0,
search: '', search: '',
version: 'coffeethai02_580.json', version: this.getCurrentVersion(),
} }
): Observable<{ ): Observable<{
fileName: string; fileName: string;
recipes: Recipe; recipes: Recipe;
hasMore: boolean; hasMore: boolean;
}> { }> {
return this._httpClient.get<{ return this._httpClient
fileName: string; .get<{
recipes: Recipe; fileName: string;
hasMore: boolean; recipes: Recipe;
}>(environment.api + '/recipes', { hasMore: boolean;
params: { }>(environment.api + '/recipes', {
offset: params.offset, params: {
take: params.take, offset: params.offset,
search: params.search, take: params.take,
version: params.version, search: params.search,
}, version: params.version,
withCredentials: true, },
responseType: 'json', 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<Recipe01> { getRecipesById(id: string): Observable<Recipe01> {

View file

@ -5,7 +5,7 @@ import { DatePipe, NgFor, NgIf } from '@angular/common';
import { Recipe, Recipe01 } from 'src/app/core/models/recipe.model'; import { Recipe, Recipe01 } from 'src/app/core/models/recipe.model';
import { RecipeService } from 'src/app/core/services/recipe.service'; import { RecipeService } from 'src/app/core/services/recipe.service';
import { environment } from 'src/environments/environment'; 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 { BehaviorSubject } from 'rxjs';
import * as lodash from 'lodash'; import * as lodash from 'lodash';
@ -32,7 +32,6 @@ export class DashboardComponent implements OnInit {
private offset = 0; private offset = 0;
private take = 20; private take = 20;
private recipeVersionData: string[] = []; private recipeVersionData: string[] = [];
private currentRecipeVersion: string = 'coffeethai02_580.json';
recipeVersion: BehaviorSubject<string> = new BehaviorSubject<string>(''); recipeVersion: BehaviorSubject<string> = new BehaviorSubject<string>('');
recipeVersion$ = this.recipeVersion.asObservable(); recipeVersion$ = this.recipeVersion.asObservable();
@ -61,7 +60,7 @@ export class DashboardComponent implements OnInit {
offset: this.offset, offset: this.offset,
take: this.take, take: this.take,
search: this.oldSearchStr, search: this.oldSearchStr,
version: this.currentRecipeVersion, version: this._recipeService.getCurrentVersion(),
}) })
.subscribe(({ recipes, hasMore, fileName }) => { .subscribe(({ recipes, hasMore, fileName }) => {
const { Recipe01, ...recipesWithoutRecipe01 } = recipes; const { Recipe01, ...recipesWithoutRecipe01 } = recipes;
@ -100,7 +99,7 @@ export class DashboardComponent implements OnInit {
offset: this.offset, offset: this.offset,
take: this.take, take: this.take,
search: this.oldSearchStr, search: this.oldSearchStr,
version: this.currentRecipeVersion, version: this._recipeService.getCurrentVersion(),
}) })
.subscribe(({ recipes, hasMore, fileName }) => { .subscribe(({ recipes, hasMore, fileName }) => {
const { Recipe01, ...recipesWithoutRecipe01 } = recipes; const { Recipe01, ...recipesWithoutRecipe01 } = recipes;
@ -139,7 +138,7 @@ export class DashboardComponent implements OnInit {
offset: this.offset, offset: this.offset,
take: this.take, take: this.take,
search: this.searchStr, search: this.searchStr,
version: this.currentRecipeVersion, version: this._recipeService.getCurrentVersion(),
}) })
.subscribe(({ recipes, hasMore, fileName }) => { .subscribe(({ recipes, hasMore, fileName }) => {
const { Recipe01, ...recipesWithoutRecipe01 } = recipes; const { Recipe01, ...recipesWithoutRecipe01 } = recipes;
@ -165,7 +164,6 @@ export class DashboardComponent implements OnInit {
this.isHasMore = true; this.isHasMore = true;
this.isLoadMore = false; this.isLoadMore = false;
this.oldSearchStr = ''; this.oldSearchStr = '';
this.currentRecipeVersion = recipeVersion;
this._recipeService this._recipeService
.getRecipes({ .getRecipes({
@ -206,7 +204,8 @@ export class DashboardComponent implements OnInit {
openJsonTab() { openJsonTab() {
window.open( window.open(
environment.api + `/recipes/${this.currentRecipeVersion}/json`, environment.api +
`/recipes/${this._recipeService.getCurrentVersion()}/json`,
'_blank' '_blank'
); );
} }

View file

@ -0,0 +1,57 @@
import {
Component,
ElementRef,
EventEmitter,
Input,
OnInit,
Output,
ViewChild,
} from '@angular/core';
@Component({
selector: 'confirm-modal',
template: `<dialog class="modal" #confirmModal>
<div class="modal-box w-3/4">
<h3 class="font-bold text-2xl mb-3">{{ title }}</h3>
<p class="text-md">{{ message }}</p>
<div class="modal-action">
<div class="modal-action">
<a class="btn px-10" (click)="confirmCallBack(); close()">Yes</a>
<a class="btn" (click)="close()">No</a>
</div>
</div>
</div>
</dialog>`,
standalone: true,
})
export class ConfirmModal implements OnInit {
@Input() show: EventEmitter<boolean> = new EventEmitter<boolean>();
@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<HTMLDialogElement> | 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();
}
}

View file

@ -97,23 +97,22 @@
</div> </div>
</div> </div>
<div class="modal-action"> <div class="modal-action">
<a class="btn px-10" (click)="save()">Save</a> <a class="btn px-10" (click)="onConfirmSave()">Save</a>
<a class="btn" (click)="close()">Close</a> <a class="btn" (click)="onConfirmClose()">Close</a>
</div> </div>
</form> <confirm-modal
</div> [title]="confirmSave.title"
</div> [message]="confirmSave.message"
</dialog> [confirmCallBack]="confirmSave.confirmCallBack"
[show]="showConfirmSaveModal"
></confirm-modal>
<dialog class="modal" #confirmModal> <confirm-modal
<div class="modal-box w-11/12 max-w-5xl"> [title]="confirmClose.title"
<h3 class="font-bold text-lg mb-3">Confirm Dialog</h3> [message]="confirmClose.message"
<p>Are you sure you want to delete this recipe?</p> [confirmCallBack]="confirmClose.confirmCallBack"
<div class="modal-action"> [show]="showConfirmCloseModal"
<button type="submit" class="btn btn-success">Confirm</button> ></confirm-modal>
<form method="dialog">
<!-- if there is a button, it will close the modal -->
<button class="btn btn-error">Close</button>
</form> </form>
</div> </div>
</div> </div>

View file

@ -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 { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { RecipeService } from 'src/app/core/services/recipe.service'; import { RecipeService } from 'src/app/core/services/recipe.service';
import { ConfirmModal } from '../confirm/confirm-modal.component';
interface RecipeDetail { interface RecipeDetail {
productCode: string; productCode: string;
@ -18,7 +25,7 @@ interface RecipeDetail {
@Component({ @Component({
selector: 'recipe-modal', selector: 'recipe-modal',
templateUrl: './recipe-modal.component.html', templateUrl: './recipe-modal.component.html',
imports: [ReactiveFormsModule], imports: [ReactiveFormsModule, ConfirmModal],
standalone: true, standalone: true,
}) })
export class RecipeModalComponent { export class RecipeModalComponent {
@ -48,14 +55,6 @@ export class RecipeModalComponent {
this.detailModal = modal; this.detailModal = modal;
} }
private confirmModal: ElementRef<HTMLDialogElement> | null = null;
@ViewChild('confirmModal', { static: false }) set setConfirmModal(
modal: ElementRef
) {
this.confirmModal = modal;
}
constructor(private recipeService: RecipeService) {} constructor(private recipeService: RecipeService) {}
openModal() { openModal() {
@ -93,14 +92,38 @@ export class RecipeModalComponent {
} }
} }
save() { showConfirmSaveModal: EventEmitter<boolean> = new EventEmitter<boolean>();
console.log(this.recipeDetail.value); showConfirmCloseModal: EventEmitter<boolean> = new EventEmitter<boolean>();
this.detailModal?.nativeElement.close();
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)) { if (!isEqual(this.recipeDetail.value, this.originalRecipeDetail)) {
this.confirmModal?.nativeElement.showModal(); this.showConfirmCloseModal.emit(true);
} else { } else {
this.detailModal?.nativeElement.close(); this.detailModal?.nativeElement.close();
} }

View file

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"recipe-manager/data" "recipe-manager/data"
"recipe-manager/models" "recipe-manager/models"
"recipe-manager/services/sheet"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -13,12 +14,14 @@ import (
) )
type RecipeRouter struct { 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{ 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) { r.Get("/versions", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(rr.data.AllVersions) 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)
})
}) })
} }

View file

@ -17,6 +17,7 @@ import (
"recipe-manager/services/cli" "recipe-manager/services/cli"
"recipe-manager/services/logger" "recipe-manager/services/logger"
"recipe-manager/services/oauth" "recipe-manager/services/oauth"
"recipe-manager/services/sheet"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -437,7 +438,14 @@ func (s *Server) createHandler() {
}) })
// Recipe Router // 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) rr.Route(r)
}) })

View file

@ -10,6 +10,7 @@ import (
) )
type SheetService interface { type SheetService interface {
GetSheet(ctx context.Context, sheetID string) [][]interface{}
} }
type sheetService struct { type sheetService struct {
@ -42,28 +43,35 @@ func NewSheetService(ctx context.Context) (SheetService, error) {
}, nil }, 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() spreadSheet, err := s.service.Spreadsheets.Get(sheetID).Do()
if err != nil { if err != nil {
panic(err) panic(err)
} }
for _, sheet := range spreadSheet.Sheets { var sheetIndex int
for i, sheet := range spreadSheet.Sheets {
// print data collumn C // print data collumn C
readRange := sheet.Properties.Title + "!C:C" if sheet.Properties.Title == "menu-name" {
resp, err := s.service.Spreadsheets.Values.Get(sheetID, readRange).Do() sheetIndex = i
break
if err != nil {
panic(err)
}
if len(resp.Values) == 0 {
println("No data found")
} else {
for _, row := range resp.Values {
println(row[0].(string))
}
} }
} }
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
} }