Merge branch 'main' of https://github.com/Poomipat-Ch/taobin_recipe_manager
This commit is contained in:
commit
54b1aa42ef
16 changed files with 411 additions and 61 deletions
|
|
@ -13,6 +13,9 @@
|
|||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"allowedCommonJsDependencies": [
|
||||
"lodash"
|
||||
],
|
||||
"outputPath": "dist/client",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
|
|
@ -101,4 +104,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
client/package-lock.json
generated
11
client/package-lock.json
generated
|
|
@ -17,6 +17,7 @@
|
|||
"@angular/platform-browser-dynamic": "^16.2.0",
|
||||
"@angular/router": "^16.2.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.13.0"
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
"@angular/compiler-cli": "^16.2.0",
|
||||
"@types/google.accounts": "^0.0.9",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"daisyui": "^3.7.7",
|
||||
"jasmine-core": "~4.6.0",
|
||||
|
|
@ -3383,6 +3385,12 @@
|
|||
"integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.199",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz",
|
||||
"integrity": "sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
|
||||
|
|
@ -8026,8 +8034,7 @@
|
|||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"@angular/platform-browser-dynamic": "^16.2.0",
|
||||
"@angular/router": "^16.2.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash": "^4.17.21",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.13.0"
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
"@angular/compiler-cli": "^16.2.0",
|
||||
"@types/google.accounts": "^0.0.9",
|
||||
"@types/jasmine": "~4.3.0",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"daisyui": "^3.7.7",
|
||||
"jasmine-core": "~4.6.0",
|
||||
|
|
@ -41,4 +43,4 @@
|
|||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "~5.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@
|
|||
</aside>
|
||||
|
||||
<div class="pt-10 sm:ml-64">
|
||||
<div class="p-4 sm:mt-5 md:mt-12">
|
||||
<div class="p-2 sm:mt-5 md:mt-12">
|
||||
<div class="grid grid-cols-1 gap-2 mb-2">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, distinctUntilChanged } from 'rxjs';
|
||||
import { Recipe } from '../models/recipe.model';
|
||||
import { Recipe, Recipe01 } from '../models/recipe.model';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
interface RecipeParams {
|
||||
|
|
@ -33,4 +33,11 @@ export class RecipeService {
|
|||
responseType: 'json',
|
||||
});
|
||||
}
|
||||
|
||||
getRecipesById(id: string): Observable<Recipe01> {
|
||||
return this._httpClient.get<Recipe01>(environment.api + '/recipes/' + id, {
|
||||
withCredentials: true,
|
||||
responseType: 'json',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<main
|
||||
class="relative overflow-auto max-h-[700px] shadow-md sm:rounded-lg"
|
||||
class="relative overflow-auto max-h-[610px] shadow-md sm:rounded-lg"
|
||||
#table
|
||||
>
|
||||
<table *ngIf="isLoaded" class="table">
|
||||
|
|
@ -118,23 +118,7 @@
|
|||
{{ recipe.LastChange | date : "dd-MMM-yyyy hh:mm:ss" }}
|
||||
</td>
|
||||
<td class="px-4 py-4 flex">
|
||||
<div class="cursor-pointer">
|
||||
<svg
|
||||
class="w-6 h-6 text-gray-800 dark:text-white"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
d="M7.75 4H19M7.75 4a2.25 2.25 0 0 1-4.5 0m4.5 0a2.25 2.25 0 0 0-4.5 0M1 4h2.25m13.5 6H19m-2.25 0a2.25 2.25 0 0 1-4.5 0m4.5 0a2.25 2.25 0 0 0-4.5 0M1 10h11.25m-4.5 6H19M7.75 16a2.25 2.25 0 0 1-4.5 0m4.5 0a2.25 2.25 0 0 0-4.5 0M1 16h2.25"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<recipe-modal id="{{ recipe.id }}"></recipe-modal>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -1,25 +1,16 @@
|
|||
import {
|
||||
AfterContentChecked,
|
||||
AfterRenderRef,
|
||||
AfterViewChecked,
|
||||
AfterViewInit,
|
||||
Component,
|
||||
ElementRef,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { UserService } from 'src/app/core/services/user.service';
|
||||
import { User } from 'src/app/core/models/user.model';
|
||||
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 { BehaviorSubject } from 'rxjs';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { RecipeModalComponent } from 'src/app/shared/modal/recipe-modal.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
standalone: true,
|
||||
imports: [NgIf, NgFor, DatePipe],
|
||||
imports: [NgIf, NgFor, DatePipe, RecipeModalComponent],
|
||||
templateUrl: './dashboard.component.html',
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
|
|
|
|||
77
client/src/app/shared/modal/recipe-modal.component.html
Normal file
77
client/src/app/shared/modal/recipe-modal.component.html
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<button class="btn btn-sm btn-circle btn-ghost" (click)="openModal()">
|
||||
<svg
|
||||
class="w-6 h-6 text-gray-800 dark:text-white"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
d="M7.75 4H19M7.75 4a2.25 2.25 0 0 1-4.5 0m4.5 0a2.25 2.25 0 0 0-4.5 0M1 4h2.25m13.5 6H19m-2.25 0a2.25 2.25 0 0 1-4.5 0m4.5 0a2.25 2.25 0 0 0-4.5 0M1 10h11.25m-4.5 6H19M7.75 16a2.25 2.25 0 0 1-4.5 0m4.5 0a2.25 2.25 0 0 0-4.5 0M1 16h2.25"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<dialog class="modal" #detailModal>
|
||||
<div class="modal-box w-11/12 max-w-5xl">
|
||||
<h3 class="font-bold text-lg mb-3">{{ title }}</h3>
|
||||
<form class="flex flex-col gap-2" [formGroup]="recipeDetail">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label>
|
||||
<span class="text-base label-text">Product Code: </span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="w-full input input-sm input-bordered input-ghost"
|
||||
formControlName="productCode"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label><span class="text-base label-text">Name: </span> </label>
|
||||
<input
|
||||
type="text"
|
||||
class="w-full input input-sm input-bordered input-ghost"
|
||||
formControlName="name"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label><span class="text-base label-text"> Other Name:</span> </label>
|
||||
<input
|
||||
type="text"
|
||||
class="w-full input input-sm input-bordered input-ghost"
|
||||
formControlName="otherName"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label><span class="text-base label-text"> Description:</span> </label>
|
||||
<input
|
||||
type="text"
|
||||
class="w-full input input-sm input-bordered input-ghost"
|
||||
formControlName="description"
|
||||
/>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<a class="btn px-10" (click)="save()">Save</a>
|
||||
<a class="btn" (click)="close()">Close</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog class="modal" #confirmModal>
|
||||
<div class="modal-box w-11/12 max-w-5xl">
|
||||
<h3 class="font-bold text-lg mb-3">Confirm Dialog</h3>
|
||||
<p>Are you sure you want to delete this recipe?</p>
|
||||
<div class="modal-action">
|
||||
<button type="submit" class="btn btn-success">Confirm</button>
|
||||
<form method="dialog">
|
||||
<!-- if there is a button, it will close the modal -->
|
||||
<button class="btn btn-error">Close</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
88
client/src/app/shared/modal/recipe-modal.component.ts
Normal file
88
client/src/app/shared/modal/recipe-modal.component.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { Component, ElementRef, 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';
|
||||
|
||||
interface RecipeDetail {
|
||||
productCode: string;
|
||||
name: string;
|
||||
otherName: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'recipe-modal',
|
||||
templateUrl: './recipe-modal.component.html',
|
||||
imports: [ReactiveFormsModule],
|
||||
standalone: true,
|
||||
})
|
||||
export class RecipeModalComponent {
|
||||
@Input({ required: true }) id!: string;
|
||||
|
||||
title: string = 'Recipe Detail';
|
||||
|
||||
recipeDetail = new FormGroup({
|
||||
productCode: new FormControl<string>(''),
|
||||
name: new FormControl<string>(''),
|
||||
otherName: new FormControl<string>(''),
|
||||
description: new FormControl<string>(''),
|
||||
});
|
||||
|
||||
originalRecipeDetail: RecipeDetail | null = null;
|
||||
|
||||
private detailModal: ElementRef<HTMLDialogElement> | null = null;
|
||||
|
||||
@ViewChild('detailModal', { static: false }) set setDetailModal(
|
||||
modal: ElementRef
|
||||
) {
|
||||
this.detailModal = modal;
|
||||
}
|
||||
|
||||
private confirmModal: ElementRef<HTMLDialogElement> | null = null;
|
||||
|
||||
@ViewChild('confirmModal', { static: false }) set setConfirmModal(
|
||||
modal: ElementRef
|
||||
) {
|
||||
this.confirmModal = modal;
|
||||
}
|
||||
|
||||
constructor(private recipeService: RecipeService) {}
|
||||
|
||||
openModal() {
|
||||
this.detailModal?.nativeElement.showModal();
|
||||
|
||||
if (this.detailModal?.nativeElement.open) {
|
||||
this.recipeService.getRecipesById(this.id).subscribe((recipe) => {
|
||||
this.title = recipe.name + ' | ' + recipe.productCode;
|
||||
this.recipeDetail.patchValue(
|
||||
{
|
||||
productCode: recipe.productCode,
|
||||
name: recipe.name,
|
||||
otherName: recipe.otherName,
|
||||
description: recipe.Description,
|
||||
},
|
||||
{ emitEvent: false }
|
||||
);
|
||||
this.originalRecipeDetail = {
|
||||
productCode: recipe.productCode,
|
||||
name: recipe.name,
|
||||
otherName: recipe.otherName,
|
||||
description: recipe.Description,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
console.log(this.recipeDetail.value);
|
||||
this.detailModal?.nativeElement.close();
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!isEqual(this.recipeDetail.value, this.originalRecipeDetail)) {
|
||||
this.confirmModal?.nativeElement.showModal();
|
||||
} else {
|
||||
this.detailModal?.nativeElement.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue