diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..e87668e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "cSpell.words": [
+ "heroicon"
+ ]
+}
diff --git a/package-lock.json b/package-lock.json
index 031e05d..6aac596 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,8 +17,10 @@
"@angular/platform-browser": "^14.1.0",
"@angular/platform-browser-dynamic": "^14.1.0",
"@angular/router": "^14.1.0",
- "bootstrap": "^4.6.1",
+ "@heroicons/react": "^2.0.16",
+ "@ngxs/store": "^3.7.5",
"firebase": "^9.9.1",
+ "ng-heroicon": "^2.1.0",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
@@ -3206,6 +3208,14 @@
"node": ">=10"
}
},
+ "node_modules/@heroicons/react": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.16.tgz",
+ "integrity": "sha512-x89rFxH3SRdYaA+JCXwfe+RkE1SFTo9GcOkZettHer71Y3T7V+ogKmfw5CjTazgS3d0ClJ7p1NA+SP7VQLQcLw==",
+ "peerDependencies": {
+ "react": ">= 16"
+ }
+ },
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -3318,6 +3328,22 @@
"webpack": "^5.54.0"
}
},
+ "node_modules/@ngxs/store": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.8.0.tgz",
+ "integrity": "sha512-ZIVM2ftudeB3VSjULJjb31RS3WKYVgs/67IHdDjukn1S0X8ZRk07QNH6cUCfUB5h345sMLCwliwVuqh+IXbYSw==",
+ "dependencies": {
+ "tslib": "^2.2.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ngxs"
+ },
+ "peerDependencies": {
+ "@angular/core": ">=6.1.0 <16.0.0",
+ "rxjs": ">=6.5.5"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -4483,19 +4509,6 @@
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"dev": true
},
- "node_modules/bootstrap": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
- "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/bootstrap"
- },
- "peerDependencies": {
- "jquery": "1.9.1 - 3",
- "popper.js": "^1.16.1"
- }
- },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -7970,17 +7983,10 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/jquery": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz",
- "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==",
- "peer": true
- },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
"version": "3.14.1",
@@ -8610,6 +8616,18 @@
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "peer": true,
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
"node_modules/lru-cache": {
"version": "7.13.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.2.tgz",
@@ -9090,6 +9108,18 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
+ "node_modules/ng-heroicon": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ng-heroicon/-/ng-heroicon-2.1.0.tgz",
+ "integrity": "sha512-M9U2ee+80hVg452eQEjF7AJIa2v78YkA5l/Zozq/eDFYJzo6/R84P+m0tPU6zmuJkh0gB9iSN15s96eTsVkugw==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^13.2.1",
+ "@angular/core": "^13.2.1"
+ }
+ },
"node_modules/nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
@@ -9900,17 +9930,6 @@
"node": ">=8"
}
},
- "node_modules/popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
- "peer": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
"node_modules/postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
@@ -10849,6 +10868,18 @@
"node": ">= 0.8"
}
},
+ "node_modules/react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "peer": true,
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -15433,6 +15464,12 @@
}
}
},
+ "@heroicons/react": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.16.tgz",
+ "integrity": "sha512-x89rFxH3SRdYaA+JCXwfe+RkE1SFTo9GcOkZettHer71Y3T7V+ogKmfw5CjTazgS3d0ClJ7p1NA+SP7VQLQcLw==",
+ "requires": {}
+ },
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -15520,6 +15557,14 @@
"dev": true,
"requires": {}
},
+ "@ngxs/store": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.8.0.tgz",
+ "integrity": "sha512-ZIVM2ftudeB3VSjULJjb31RS3WKYVgs/67IHdDjukn1S0X8ZRk07QNH6cUCfUB5h345sMLCwliwVuqh+IXbYSw==",
+ "requires": {
+ "tslib": "^2.2.0"
+ }
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -16494,12 +16539,6 @@
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"dev": true
},
- "bootstrap": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
- "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
- "requires": {}
- },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -18994,17 +19033,10 @@
}
}
},
- "jquery": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz",
- "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==",
- "peer": true
- },
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.14.1",
@@ -19489,6 +19521,15 @@
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "peer": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
"lru-cache": {
"version": "7.13.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.2.tgz",
@@ -19852,6 +19893,14 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
+ "ng-heroicon": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ng-heroicon/-/ng-heroicon-2.1.0.tgz",
+ "integrity": "sha512-M9U2ee+80hVg452eQEjF7AJIa2v78YkA5l/Zozq/eDFYJzo6/R84P+m0tPU6zmuJkh0gB9iSN15s96eTsVkugw==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
"nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
@@ -20452,12 +20501,6 @@
"find-up": "^4.0.0"
}
},
- "popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "peer": true
- },
"postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
@@ -21019,6 +21062,15 @@
}
}
},
+ "react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "peer": true,
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
diff --git a/package.json b/package.json
index 340e0ec..33fd048 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,10 @@
"@angular/platform-browser": "^14.1.0",
"@angular/platform-browser-dynamic": "^14.1.0",
"@angular/router": "^14.1.0",
- "bootstrap": "^4.6.1",
+ "@heroicons/react": "^2.0.16",
+ "@ngxs/store": "^3.7.5",
"firebase": "^9.9.1",
+ "ng-heroicon": "^2.1.0",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 242250e..6219802 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,10 +1,56 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
-import { AuthComponent } from './core/auth/auth.component';
+import { SignInComponent } from './modules/auth/sign-in/sign-in.component';
+import { AddDataComponent } from './modules/user/add-data/add-data.component';
+import { CardComponent } from './modules/user/card/card.component';
+import { DashboardComponent } from './modules/user/dashboard/dashboard.component';
+import { ShowDataComponent } from './modules/user/show-data/show-data.component';
+import { CardListComponent } from './modules/user/card/card-list/card-list.component';
+import { CardDetailComponent } from './modules/user/card/card-detail/card-detail.component';
+import { FormProjectComponent } from './modules/user/form-project/form-project.component';
+import { FormInputComponent } from './modules/user/form-input/form-input.component';
const routes: Routes = [
- { path: '', redirectTo: 'auth', pathMatch: 'full' },
- { path: 'auth', component: AuthComponent },
+ { path: '', redirectTo: 'sign-in', pathMatch: 'full' },
+ { path: 'sign-in', component: SignInComponent },
+ {
+ path: 'dashboard',
+ component: DashboardComponent,
+ },
+ {
+ path: 'card',
+ component: CardComponent,
+ children: [
+ {
+ path: '',
+ component: CardListComponent,
+ children: [{ path: ':id', component: CardDetailComponent }],
+ },
+ ],
+ },
+ {
+ path: 'show-data',
+ component: ShowDataComponent,
+ },
+ {
+ path: 'add-data',
+ component: AddDataComponent,
+ },
+ {
+ path: 'reactive-forms',
+ component: FormProjectComponent,
+ },
+ {
+ path: 'form-input',
+ component: FormInputComponent,
+ },
+ {
+ path: 'routes-compo',
+ loadChildren: () =>
+ import('./modules/learning/routes-compo/routes-compo.module').then(
+ (m) => m.RoutesCompoModule
+ ),
+ },
];
@NgModule({
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 25fda51..93ac8a7 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,8 +1,8 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { Component } from '@angular/core';
@Component({
selector: 'app-root',
- template: ``,
- changeDetection: ChangeDetectionStrategy.OnPush,
+ // template: ``,
+ template: ``,
})
export class AppComponent {}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 9c68202..ffbdbb6 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -1,20 +1,11 @@
import { NgModule } from '@angular/core';
-import { BrowserModule } from '@angular/platform-browser';
-import { AngularFireModule } from '@angular/fire/compat';
-import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
-import { environment } from '../environments/environment';
-import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
+import { components } from './shared/config/components';
+import { imports } from './shared/config/modules';
@NgModule({
- declarations: [AppComponent],
- imports: [
- BrowserModule,
- AppRoutingModule,
- AngularFireModule.initializeApp(environment.firebase),
- AngularFirestoreModule, // for firestore
- ],
- providers: [],
+ declarations: [...components],
+ imports: [...imports],
bootstrap: [AppComponent],
})
export class AppModule {}
diff --git a/src/app/core/auth/auth.component.html b/src/app/core/auth/auth.component.html
deleted file mode 100644
index 78982bf..0000000
--- a/src/app/core/auth/auth.component.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
auth works!
-ทำงาน
diff --git a/src/app/core/auth/auth.component.ts b/src/app/core/auth/auth.component.ts
deleted file mode 100644
index 69c0424..0000000
--- a/src/app/core/auth/auth.component.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-auth',
- templateUrl: './auth.component.html',
- styleUrls: ['./auth.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class AuthComponent implements OnInit {
- constructor() {}
-
- ngOnInit() {}
-}
diff --git a/src/app/modules/auth/sign-in/sign-in.component.html b/src/app/modules/auth/sign-in/sign-in.component.html
new file mode 100644
index 0000000..fc7f7fc
--- /dev/null
+++ b/src/app/modules/auth/sign-in/sign-in.component.html
@@ -0,0 +1,34 @@
+
diff --git a/src/app/core/auth/auth.component.scss b/src/app/modules/auth/sign-in/sign-in.component.scss
similarity index 100%
rename from src/app/core/auth/auth.component.scss
rename to src/app/modules/auth/sign-in/sign-in.component.scss
diff --git a/src/app/modules/auth/sign-in/sign-in.component.ts b/src/app/modules/auth/sign-in/sign-in.component.ts
new file mode 100644
index 0000000..57f7b39
--- /dev/null
+++ b/src/app/modules/auth/sign-in/sign-in.component.ts
@@ -0,0 +1,42 @@
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
+import { AngularFireAuth } from '@angular/fire/compat/auth';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'app-sign-in',
+ templateUrl: './sign-in.component.html',
+ styleUrls: ['./sign-in.component.scss'],
+})
+export class SignInComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* Variable */
+ /* -------------------------------------------------------------------------- */
+ private isActiveAuth: boolean = false;
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor(private auth: AngularFireAuth, private router: Router) {}
+ /* -------------------------------------------------------------------------- */
+ /* Life Circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {}
+ /* -------------------------------------------------------------------------- */
+ /* Function */
+ /* -------------------------------------------------------------------------- */
+ SignIn(email: string, password: string) {
+ return this.auth
+ .signInWithEmailAndPassword(email, password)
+ .then(() => {
+ // SignUp
+ this.isActiveAuth = true;
+ alert('เข้าสู่ระบบสำเร็จ !');
+ this.router.navigate(['dashboard']);
+ // ...
+ })
+ .catch(() => {
+ // SignUp Failed
+ this.isActiveAuth = false;
+ alert('กรุณากรอกข้อมูลที่ถูกต้อง !');
+ });
+ }
+}
diff --git a/src/app/modules/auth/sign-in/store/models/state.model.ts b/src/app/modules/auth/sign-in/store/models/state.model.ts
new file mode 100644
index 0000000..9d2b408
--- /dev/null
+++ b/src/app/modules/auth/sign-in/store/models/state.model.ts
@@ -0,0 +1,7 @@
+/* -------------------------------------------------------------------------- */
+/* State */
+/* -------------------------------------------------------------------------- */
+
+export interface SignInStateModels {
+ is_active: false;
+}
diff --git a/src/app/modules/auth/sign-in/store/selectors/sign-in.selector.ts b/src/app/modules/auth/sign-in/store/selectors/sign-in.selector.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.html b/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.html
new file mode 100644
index 0000000..c309b09
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.html
@@ -0,0 +1,3 @@
+
+ routes-compo-detail works!
+
diff --git a/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.scss b/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.ts b/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.ts
new file mode 100644
index 0000000..de07722
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo-detail/routes-compo-detail.component.ts
@@ -0,0 +1,12 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-routes-compo-detail',
+ templateUrl: './routes-compo-detail.component.html',
+ styleUrls: ['./routes-compo-detail.component.scss'],
+})
+export class RoutesCompoDetailComponent implements OnInit {
+ constructor() {}
+
+ ngOnInit() {}
+}
diff --git a/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.html b/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.html
new file mode 100644
index 0000000..2403528
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.html
@@ -0,0 +1,45 @@
+
+
diff --git a/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.scss b/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.ts b/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.ts
new file mode 100644
index 0000000..971fbf5
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo-list/routes-compo-list.component.ts
@@ -0,0 +1,36 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+@Component({
+ selector: 'app-routes-compo-list',
+ templateUrl: './routes-compo-list.component.html',
+ styleUrls: ['./routes-compo-list.component.scss'],
+})
+export class RoutesCompoListComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor(private _router: Router, private _route: ActivatedRoute) {}
+ /* -------------------------------------------------------------------------- */
+ /* Life circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {}
+ /* -------------------------------------------------------------------------- */
+ /* Variables */
+ /* -------------------------------------------------------------------------- */
+ myData = [
+ { id: 1, name: 'John', age: 25 },
+ { id: 2, name: 'Jane', age: 30 },
+ { id: 3, name: 'Bob', age: 40 },
+ ];
+ /* -------------------------------------------------------------------------- */
+ /* Functions */
+ /* -------------------------------------------------------------------------- */
+ click(): void {
+ this._router.navigate(['./add'], {
+ relativeTo: this._route,
+ replaceUrl: true,
+ });
+ // this._cdr.detectChanges();
+ }
+}
diff --git a/src/app/modules/learning/routes-compo/routes-compo-routing.module.ts b/src/app/modules/learning/routes-compo/routes-compo-routing.module.ts
new file mode 100644
index 0000000..b43bb5c
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo-routing.module.ts
@@ -0,0 +1,23 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { RoutesCompoComponent } from './routes-compo.component';
+import { RoutesCompoListComponent } from './routes-compo-list/routes-compo-list.component';
+import { RoutesCompoDetailComponent } from './routes-compo-detail/routes-compo-detail.component';
+
+export const routes: Routes = [
+ {
+ path: '',
+ component: RoutesCompoComponent,
+ children: [
+ {
+ path: '',
+ component: RoutesCompoListComponent,
+ // children: [{ path: 'add', component: RoutesCompoDetailComponent }],
+ },
+ {
+ path: 'add',
+ component: RoutesCompoDetailComponent,
+ },
+ ],
+ },
+];
diff --git a/src/app/modules/learning/routes-compo/routes-compo.component.html b/src/app/modules/learning/routes-compo/routes-compo.component.html
new file mode 100644
index 0000000..0680b43
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo.component.html
@@ -0,0 +1 @@
+
diff --git a/src/app/modules/learning/routes-compo/routes-compo.component.scss b/src/app/modules/learning/routes-compo/routes-compo.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/learning/routes-compo/routes-compo.component.ts b/src/app/modules/learning/routes-compo/routes-compo.component.ts
new file mode 100644
index 0000000..99c9112
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo.component.ts
@@ -0,0 +1,12 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-routes-compo',
+ templateUrl: './routes-compo.component.html',
+ styleUrls: ['./routes-compo.component.scss'],
+})
+export class RoutesCompoComponent implements OnInit {
+ constructor() {}
+
+ ngOnInit(): void {}
+}
diff --git a/src/app/modules/learning/routes-compo/routes-compo.module.ts b/src/app/modules/learning/routes-compo/routes-compo.module.ts
new file mode 100644
index 0000000..ca9e907
--- /dev/null
+++ b/src/app/modules/learning/routes-compo/routes-compo.module.ts
@@ -0,0 +1,22 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+// import { RoutesCompoRoutingModule } from './routes-compo-routing.module';
+import { RoutesCompoComponent } from './routes-compo.component';
+import { RouterModule } from '@angular/router';
+import { HTTP_INTERCEPTORS } from '@angular/common/http';
+import { RoutesCompoListComponent } from './routes-compo-list/routes-compo-list.component';
+import { RoutesCompoDetailComponent } from './routes-compo-detail/routes-compo-detail.component';
+import { routes } from './routes-compo-routing.module';
+
+const components = [
+ RoutesCompoComponent,
+ RoutesCompoListComponent,
+ RoutesCompoDetailComponent,
+];
+
+@NgModule({
+ declarations: [...components],
+ imports: [CommonModule, RouterModule, RouterModule.forChild(routes)],
+})
+export class RoutesCompoModule {}
diff --git a/src/app/modules/user/add-data/add-data.component.html b/src/app/modules/user/add-data/add-data.component.html
new file mode 100644
index 0000000..6edac9f
--- /dev/null
+++ b/src/app/modules/user/add-data/add-data.component.html
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Num. |
+ Vocabulary. |
+ Description. |
+ Category. |
+ Manager. |
+
+
+
+
+ | {{ i + 1 }} |
+ {{ row.Gesture }} |
+ {{ row.Description }} |
+ {{ row.Category }} |
+
+
+ |
+
+
+
+
+
+
diff --git a/src/app/modules/user/add-data/add-data.component.scss b/src/app/modules/user/add-data/add-data.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/add-data/add-data.component.ts b/src/app/modules/user/add-data/add-data.component.ts
new file mode 100644
index 0000000..d65e981
--- /dev/null
+++ b/src/app/modules/user/add-data/add-data.component.ts
@@ -0,0 +1,215 @@
+import { Component, OnInit } from '@angular/core';
+import { AngularFirestore } from '@angular/fire/compat/firestore';
+import { DATA_TABLE } from '../show-data/show-data.data';
+import { DropdownModels } from './store/models/add-data.model';
+import { FormControl, Validators } from '@angular/forms';
+import { ToastService } from 'src/app/shared/services/toast.service';
+
+@Component({
+ selector: 'app-add-data',
+ templateUrl: './add-data.component.html',
+ styleUrls: ['./add-data.component.scss'],
+})
+export class AddDataComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor(
+ private firestore: AngularFirestore,
+ private _toastService: ToastService
+ ) {}
+ /* -------------------------------------------------------------------------- */
+ /* life circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {
+ this.GetAll();
+ this.dropdownData.valueChanges.subscribe((res) => {
+ if (res !== '') {
+ this.isCheckValidator = false;
+ }
+ this.GetAll(res);
+ this.Delete(res);
+ });
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* variable */
+ /* -------------------------------------------------------------------------- */
+ Category: any | null = '';
+ Description: any | null = '';
+ Gesture: any | null = '';
+ Vdo: any | null = '';
+ isCheckEditMode: boolean = false;
+ isCheckValidator: boolean = false;
+
+ _data: any = [];
+ dropdownData = new FormControl('', [Validators.required]);
+
+ /* -------------------------------------------------------------------------- */
+ /* functions */
+ /* -------------------------------------------------------------------------- */
+ /* --------------------------------- // Get --------------------------------- */
+ // ---- Get All Document
+ GetAll(category?: any): void {
+ this.firestore
+ .collection('Vocabularies')
+ .doc(category)
+ .collection('vocab')
+ .valueChanges()
+ .subscribe((values) => {
+ values.filter((item: any) => {
+ console.log(item.Description);
+ });
+ console.log(values);
+ this._data = values;
+ });
+ }
+
+ /* -------------------------------- // Delete ------------------------------- */
+ // ---- Delete Document
+ Delete(item?: any, category?: any): void {
+ this.firestore
+ .collection('Vocabularies')
+ .doc(category)
+ .collection('vocab')
+ .doc(item.Gesture)
+ .delete()
+ .then(() => {
+ this.resetForm();
+ this.isCheckEditMode = false;
+ // alert('Data delete successfully!');
+ })
+ .catch((error) => {
+ alert(`Error adding data: ${error}`);
+ });
+ }
+
+ /* ----------------------------- // Add Document ---------------------------- */
+ // ---- Add Document With Auto Generated ID
+ // add(): void {
+ // const data = {
+ // Category: this.Category,
+ // Description: this.Description,
+ // Gesture: this.Gesture,
+ // Vdo: this.Vdo,
+ // };
+ // this.firestore
+ // .collection('Vocabularies')
+ // .doc('สัตว์')
+ // .collection('vocab')
+ // .add(data)
+ // .then((res) => {
+ // this.data();
+ // this.resetForm();
+ // })
+ // .catch((e) => {
+ // });
+ // }
+
+ // ---- Add Document With Customer ID
+ add(category?: any): void {
+ if (category === '') {
+ // alert('กรุณาเลือก Category');
+ this.checkValidator();
+ }
+ const data: any = {
+ Category: this.dropdownData.value?.trim(),
+ Description: this.Description.trim(),
+ Gesture: this.Gesture.trim(),
+ Vdo: this.Vdo.trim(),
+ };
+ this.firestore
+ .collection('Vocabularies')
+ .doc(category)
+ .collection('vocab')
+ .doc(data.Gesture)
+ .set(data)
+ .then(() => {
+ console.log(data);
+ this.resetForm();
+ this.isCheckEditMode = false;
+ // alert('Data added successfully!');
+ })
+ .catch((error) => {
+ alert(`Error adding data: ${error}`);
+ });
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* // Pull Data */
+ /* -------------------------------------------------------------------------- */
+ // ---- Pull Fields inside a Document
+ pull(item: any, category?: any): void {
+ this.firestore
+ .collection('Vocabularies')
+ .doc(category)
+ .collection('vocab')
+ .doc(item.Gesture)
+ .get()
+ .subscribe((res) => {
+ this.isCheckEditMode = true;
+ if (res.exists) {
+ this.Gesture = item.Gesture;
+ this.Description = item.Description;
+ this.Vdo = item.Vdo;
+ this.Category = item.Category;
+ }
+ });
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* // Search Data */
+ /* -------------------------------------------------------------------------- */
+ // ---- Search Fields inside a Document
+ Search(category?: any): void {
+ if (this.Gesture === '') {
+ return this.GetAll(category);
+ }
+ this.firestore
+ .collection('Vocabularies')
+ .doc(category)
+ .collection('vocab')
+ .doc(this.Gesture)
+ .valueChanges()
+ .subscribe((res) => {
+ if (res === undefined) {
+ alert('ไม่พบข้อมูล');
+ }
+ this._data = [res];
+ this.resetForm();
+ });
+ }
+
+ /* ------------------------------ // Reset Form ----------------------------- */
+ // ---- Reset Form After Add Document
+ resetForm(): void {
+ this.Category = '';
+ this.Description = '';
+ this.Gesture = '';
+ this.Vdo = '';
+ }
+
+ checkValidator() {
+ this.isCheckValidator = true;
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* Fake */
+ /* -------------------------------------------------------------------------- */
+ data = DATA_TABLE;
+
+ dropdown: DropdownModels[] = [
+ { id: 1, value: 'ครอบครัว' },
+ { id: 2, value: 'ความรู้สึก' },
+ { id: 3, value: 'ตัวเลข' },
+ { id: 4, value: 'ผลไม้' },
+ { id: 5, value: 'ผัก' },
+ { id: 6, value: 'ร่างกาย' },
+ { id: 7, value: 'สระ วรรณยุกต์' },
+ { id: 8, value: 'สัตว์' },
+ { id: 9, value: 'สี' },
+ { id: 10, value: 'อักษรไทย' },
+ { id: 11, value: 'เวลา' },
+ { id: 12, value: 'โควิด 19' },
+ ];
+}
diff --git a/src/app/modules/user/add-data/store/models/add-data.model.ts b/src/app/modules/user/add-data/store/models/add-data.model.ts
new file mode 100644
index 0000000..8319c13
--- /dev/null
+++ b/src/app/modules/user/add-data/store/models/add-data.model.ts
@@ -0,0 +1,11 @@
+export interface AddData {
+ Category: string | null;
+ Description: string | null;
+ Gesture: string | null;
+ Vdo: string | null;
+}
+
+export interface DropdownModels {
+ id: number | string;
+ value: string;
+}
diff --git a/src/app/modules/user/card/card-detail/card-detail.component.html b/src/app/modules/user/card/card-detail/card-detail.component.html
new file mode 100644
index 0000000..1a48da3
--- /dev/null
+++ b/src/app/modules/user/card/card-detail/card-detail.component.html
@@ -0,0 +1 @@
+card-detail works!
diff --git a/src/app/modules/user/card/card-detail/card-detail.component.scss b/src/app/modules/user/card/card-detail/card-detail.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/card/card-detail/card-detail.component.ts b/src/app/modules/user/card/card-detail/card-detail.component.ts
new file mode 100644
index 0000000..50d9803
--- /dev/null
+++ b/src/app/modules/user/card/card-detail/card-detail.component.ts
@@ -0,0 +1,12 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-card-detail',
+ templateUrl: './card-detail.component.html',
+ styleUrls: ['./card-detail.component.scss'],
+})
+export class CardDetailComponent implements OnInit {
+ constructor() {}
+
+ ngOnInit() {}
+}
diff --git a/src/app/modules/user/card/card-list/card-list.component.html b/src/app/modules/user/card/card-list/card-list.component.html
new file mode 100644
index 0000000..1d5191b
--- /dev/null
+++ b/src/app/modules/user/card/card-list/card-list.component.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/user/card/card-list/card-list.component.scss b/src/app/modules/user/card/card-list/card-list.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/card/card-list/card-list.component.ts b/src/app/modules/user/card/card-list/card-list.component.ts
new file mode 100644
index 0000000..f9f1e7c
--- /dev/null
+++ b/src/app/modules/user/card/card-list/card-list.component.ts
@@ -0,0 +1,40 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-card-list',
+ templateUrl: './card-list.component.html',
+ styleUrls: ['./card-list.component.scss'],
+})
+export class CardListComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor() {}
+ /* -------------------------------------------------------------------------- */
+ /* variables */
+ /* -------------------------------------------------------------------------- */
+ public index = [
+ { id: 1 },
+ { id: 2 },
+ { id: 5 },
+ { id: 3 },
+ { id: 4 },
+ { id: 6 },
+ { id: 7 },
+ { id: 8 },
+ { id: 9 },
+ { id: 10 },
+ { id: 11 },
+ { id: 12 },
+ { id: 13 },
+ { id: 14 },
+ { id: 15 },
+ ];
+ /* -------------------------------------------------------------------------- */
+ /* life circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {}
+ /* -------------------------------------------------------------------------- */
+ /* functions */
+ /* -------------------------------------------------------------------------- */
+}
diff --git a/src/app/modules/user/card/card.component.html b/src/app/modules/user/card/card.component.html
new file mode 100644
index 0000000..0680b43
--- /dev/null
+++ b/src/app/modules/user/card/card.component.html
@@ -0,0 +1 @@
+
diff --git a/src/app/modules/user/card/card.component.ts b/src/app/modules/user/card/card.component.ts
new file mode 100644
index 0000000..3fa42ce
--- /dev/null
+++ b/src/app/modules/user/card/card.component.ts
@@ -0,0 +1,16 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-card',
+ templateUrl: './card.component.html',
+})
+export class CardComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor() {}
+ /* -------------------------------------------------------------------------- */
+ /* life circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {}
+}
diff --git a/src/app/modules/user/dashboard/dashboard.component.html b/src/app/modules/user/dashboard/dashboard.component.html
new file mode 100644
index 0000000..7bf822f
--- /dev/null
+++ b/src/app/modules/user/dashboard/dashboard.component.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/user/dashboard/dashboard.component.scss b/src/app/modules/user/dashboard/dashboard.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/dashboard/dashboard.component.ts b/src/app/modules/user/dashboard/dashboard.component.ts
new file mode 100644
index 0000000..b5f02ea
--- /dev/null
+++ b/src/app/modules/user/dashboard/dashboard.component.ts
@@ -0,0 +1,43 @@
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
+import { AngularFirestore } from '@angular/fire/compat/firestore';
+
+@Component({
+ selector: 'app-dashboard',
+ templateUrl: './dashboard.component.html',
+ styleUrls: ['./dashboard.component.scss'],
+})
+export class DashboardComponent implements OnInit {
+ constructor(public store: AngularFirestore) {}
+
+ ngOnInit() {
+ this.GetAllData();
+ }
+ public data_db1: any[] = [];
+ public _sidebarOpen: boolean = false;
+
+ public today = new Date(); // Get the current date
+
+ onToggleSidebar() {
+ this._sidebarOpen = !this._sidebarOpen;
+ }
+ GetAllData(): void {
+ this.store
+ .collection('Vocabularies')
+ .doc('สัตว์')
+ .collection('vocab')
+ .valueChanges()
+ .subscribe((querySnapshot) => {
+ // var data_db1 = [];
+ querySnapshot.forEach((doc) => {
+ this.data_db1.push(doc);
+ console.log(doc);
+ console.log(this.data_db1);
+ });
+ });
+ }
+
+ AddAllItemToTable() {
+ console.log(this.data_db1);
+ const sdtNo: number = 0;
+ }
+}
diff --git a/src/app/modules/user/form-input/form-input.component.html b/src/app/modules/user/form-input/form-input.component.html
new file mode 100644
index 0000000..381ee0a
--- /dev/null
+++ b/src/app/modules/user/form-input/form-input.component.html
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data_form_1 | json }}
+
+
+
+
+
+
+
+
+
+ {{ data_form_2 | json }}
+
+
+
+
+
+
+
+
+
+
First name value: {{ first.value }}
+
First name valid: {{ first.valid }}
+
Form value: {{ data.value | json }}
+
Form valid: {{ data.valid }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/user/form-input/form-input.component.scss b/src/app/modules/user/form-input/form-input.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/form-input/form-input.component.ts b/src/app/modules/user/form-input/form-input.component.ts
new file mode 100644
index 0000000..cf66973
--- /dev/null
+++ b/src/app/modules/user/form-input/form-input.component.ts
@@ -0,0 +1,116 @@
+import { Component, OnInit } from '@angular/core';
+import { FormControl, FormGroup, NgForm, Validators } from '@angular/forms';
+
+@Component({
+ selector: 'app-form-input',
+ templateUrl: './form-input.component.html',
+ styleUrls: ['./form-input.component.scss'],
+})
+export class FormInputComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* Constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor() {}
+ /* -------------------------------------------------------------------------- */
+ /* Variables */
+ /* -------------------------------------------------------------------------- */
+ data_form_1: any;
+ data_form_2: any;
+ /* -------------------------------------------------------------------------- */
+ /* Life Circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {
+ this.initForm();
+
+ // @FormGroup I
+ this.form.valueChanges.subscribe((res) => {
+ console.log(res);
+ this.data_form_1 = res;
+ });
+
+ // @FormGroup II
+ this.form2.valueChanges.subscribe((res) => {
+ console.log(res);
+ this.data_form_2 = res;
+ });
+
+ // @FormControl
+ this.value7.valueChanges.subscribe((res) => {
+ console.log(`@FormControl : ${res}`);
+ });
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* @FormGroup I */
+ /* -------------------------------------------------------------------------- */
+ // Variables
+ form: FormGroup = new FormGroup({
+ value1: new FormControl('', [Validators.required]),
+ value2: new FormControl('', [Validators.required]),
+ value3: new FormControl('', [Validators.required]),
+ });
+
+ // Get Set Form
+ get value1(): FormControl {
+ return this.form.get('value1') as FormControl;
+ }
+
+ get value2(): FormControl {
+ return this.form.get('value2') as FormControl;
+ }
+
+ get value3(): FormControl {
+ return this.form.get('value3') as FormControl;
+ }
+
+ // Functions
+ onSubmitFormGroupI(): void {
+ console.log(
+ `@FormGroupSubmit I : ${this.form.get('value1')?.value} ${
+ this.form.get('value2')?.value
+ } ${this.form.get('value3')?.value}`
+ );
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* @FormGroup II */
+ /* -------------------------------------------------------------------------- */
+ form2!: FormGroup;
+ /* -------------------------------------------------------------------------- */
+ /* Function */
+ /* -------------------------------------------------------------------------- */
+ initForm(): void {
+ this.form2 = new FormGroup({
+ value4: new FormControl('', [Validators.required]),
+ value5: new FormControl('', [Validators.required]),
+ value6: new FormControl('', [Validators.required]),
+ });
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* @NgForm */
+ /* -------------------------------------------------------------------------- */
+ // Functions
+ onSubmit(data: NgForm) {
+ console.log(data.value);
+ console.log(data.valid);
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* @Form */
+ /* -------------------------------------------------------------------------- */
+ // Functions
+ onSubmitForm(name: string, surname: string): void {
+ console.log(`@Form : Name: ${name} : Surname: ${surname}`);
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* @FormControl */
+ /* -------------------------------------------------------------------------- */
+ // Variables
+ value7 = new FormControl('', [Validators.required, Validators.minLength(2)]);
+ // Functions
+ onSubmitFormControl(): void {
+ console.log(`@FormControlSubmit : ${this.value7.value}`);
+ }
+}
diff --git a/src/app/modules/user/form-project/cost-form/cost-form.component.html b/src/app/modules/user/form-project/cost-form/cost-form.component.html
new file mode 100644
index 0000000..9395908
--- /dev/null
+++ b/src/app/modules/user/form-project/cost-form/cost-form.component.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/user/form-project/cost-form/cost-form.component.scss b/src/app/modules/user/form-project/cost-form/cost-form.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/form-project/cost-form/cost-form.component.ts b/src/app/modules/user/form-project/cost-form/cost-form.component.ts
new file mode 100644
index 0000000..9dc54b8
--- /dev/null
+++ b/src/app/modules/user/form-project/cost-form/cost-form.component.ts
@@ -0,0 +1,11 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-cost-form',
+ templateUrl: './cost-form.component.html',
+ styleUrls: ['./cost-form.component.scss'],
+})
+export class CostFormComponent implements OnInit {
+ constructor() {}
+ ngOnInit() {}
+}
diff --git a/src/app/modules/user/form-project/form-project.component.html b/src/app/modules/user/form-project/form-project.component.html
new file mode 100644
index 0000000..76dc803
--- /dev/null
+++ b/src/app/modules/user/form-project/form-project.component.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/user/form-project/form-project.component.scss b/src/app/modules/user/form-project/form-project.component.scss
new file mode 100644
index 0000000..cd96ec8
--- /dev/null
+++ b/src/app/modules/user/form-project/form-project.component.scss
@@ -0,0 +1,14 @@
+.input-text {
+ border-radius: 4px;
+ background: #dfdfdf;
+ box-shadow: inset 2px 2px 4px #bebebe, inset -2px -2px 4px #ffffff;
+ outline: none;
+ height: 3rem;
+ width: 20rem;
+ padding: 0 0.5rem;
+}
+
+.input-group {
+ display: flex;
+ flex-direction: column;
+}
diff --git a/src/app/modules/user/form-project/form-project.component.ts b/src/app/modules/user/form-project/form-project.component.ts
new file mode 100644
index 0000000..d47b391
--- /dev/null
+++ b/src/app/modules/user/form-project/form-project.component.ts
@@ -0,0 +1,56 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
+
+@Component({
+ selector: 'app-form-project',
+ templateUrl: './form-project.component.html',
+ styleUrls: ['./form-project.component.scss'],
+})
+export class FormProjectComponent implements OnInit {
+ _projectForm!: FormGroup;
+ _card: string | null = '';
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor() {}
+ /* -------------------------------------------------------------------------- */
+ /* variable */
+ /* -------------------------------------------------------------------------- */
+ @Input() set formGroup(value: FormGroup) {
+ if (value) this._projectForm = value;
+ }
+
+ @Input() set card(value: string | null) {
+ if (value) this._card = value;
+ }
+ /* -------------------------------------------------------------------------- */
+ /* life circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {
+ this.initForm();
+ }
+ /* -------------------------------------------------------------------------- */
+ /* function */
+ /* -------------------------------------------------------------------------- */
+
+ initForm(): void {
+ this._projectForm = new FormGroup({
+ /// cost-form
+ project_name: new FormControl('', [Validators.required]),
+ project_date: new FormControl('', [Validators.required]),
+ customer_name: new FormControl('', [Validators.required]),
+ /// general-form
+ total_budget_no_vat: new FormControl(0, [
+ Validators.required,
+ Validators.min(0),
+ ]),
+ vat_value: new FormControl(0, [Validators.required, Validators.min(0)]),
+ total_budget: new FormControl(0, [
+ Validators.required,
+ Validators.min(0),
+ ]),
+ direct_costs: new FormArray([], [Validators.minLength(1)]),
+ ///
+ });
+ }
+}
diff --git a/src/app/modules/user/form-project/general-form/general-form.component.html b/src/app/modules/user/form-project/general-form/general-form.component.html
new file mode 100644
index 0000000..fdbc093
--- /dev/null
+++ b/src/app/modules/user/form-project/general-form/general-form.component.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/user/form-project/general-form/general-form.component.scss b/src/app/modules/user/form-project/general-form/general-form.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/form-project/general-form/general-form.component.ts b/src/app/modules/user/form-project/general-form/general-form.component.ts
new file mode 100644
index 0000000..e460ec5
--- /dev/null
+++ b/src/app/modules/user/form-project/general-form/general-form.component.ts
@@ -0,0 +1,12 @@
+import { Component, OnInit } from '@angular/core';
+import { FormGroup } from '@angular/forms';
+
+@Component({
+ selector: 'app-general-form',
+ templateUrl: './general-form.component.html',
+ styleUrls: ['./general-form.component.scss'],
+})
+export class GeneralFormComponent implements OnInit {
+ constructor() {}
+ ngOnInit() {}
+}
diff --git a/src/app/modules/user/form-project/summary-form/summary-form.component.html b/src/app/modules/user/form-project/summary-form/summary-form.component.html
new file mode 100644
index 0000000..70174df
--- /dev/null
+++ b/src/app/modules/user/form-project/summary-form/summary-form.component.html
@@ -0,0 +1 @@
+summary-form works!
diff --git a/src/app/modules/user/form-project/summary-form/summary-form.component.scss b/src/app/modules/user/form-project/summary-form/summary-form.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modules/user/form-project/summary-form/summary-form.component.ts b/src/app/modules/user/form-project/summary-form/summary-form.component.ts
new file mode 100644
index 0000000..a7facfe
--- /dev/null
+++ b/src/app/modules/user/form-project/summary-form/summary-form.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-summary-form',
+ templateUrl: './summary-form.component.html',
+ styleUrls: ['./summary-form.component.scss']
+})
+export class SummaryFormComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/src/app/modules/user/show-data/show-data.component.html b/src/app/modules/user/show-data/show-data.component.html
new file mode 100644
index 0000000..5111c8b
--- /dev/null
+++ b/src/app/modules/user/show-data/show-data.component.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
การวนเเสดงข้อมูล เเบบ * NgFor
+
+
+
+
+
+ | XXXX |
+ XXXX |
+ XXXX |
+ XXXX |
+
+
+
+
+ | {{ i + 1 }} |
+ {{ row.name }} |
+ {{ row.age }} |
+ {{ row.city }} |
+
+
+
+ Not Found . |
+
+
+
+
+
+
+ การวนเเสดงข้อมูล เเบบ Component ตัวกลาง
+
+
+
+
+
+ | XXXX |
+ XXXX |
+ XXXX |
+ XXXX |
+
+
+
+
+ | {{ i + 1 }} |
+ {{ row.age }} |
+ {{ row.city }} |
+ {{ row.city }} |
+
+
+
+
+
+
+
+
diff --git a/src/app/modules/user/show-data/show-data.component.scss b/src/app/modules/user/show-data/show-data.component.scss
new file mode 100644
index 0000000..7a0a8df
--- /dev/null
+++ b/src/app/modules/user/show-data/show-data.component.scss
@@ -0,0 +1,16 @@
+.table {
+ @apply text-slate-500;
+ width: 100%;
+}
+
+.table th,
+.table td {
+ text-align: center;
+ padding: 12px;
+ border-bottom: 1px solid #ddd;
+}
+
+.table th {
+ text-align: center;
+ font-weight: bold;
+}
diff --git a/src/app/modules/user/show-data/show-data.component.ts b/src/app/modules/user/show-data/show-data.component.ts
new file mode 100644
index 0000000..26128bd
--- /dev/null
+++ b/src/app/modules/user/show-data/show-data.component.ts
@@ -0,0 +1,23 @@
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
+import { DATA_TABLE } from './show-data.data';
+
+@Component({
+ selector: 'app-show-data',
+ templateUrl: './show-data.component.html',
+ styleUrls: ['./show-data.component.scss'],
+})
+export class ShowDataComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor() {}
+ /* -------------------------------------------------------------------------- */
+ /* life circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {}
+
+ /* -------------------------------------------------------------------------- */
+ /* Fake Data */
+ /* -------------------------------------------------------------------------- */
+ data = DATA_TABLE;
+}
diff --git a/src/app/modules/user/show-data/show-data.data.ts b/src/app/modules/user/show-data/show-data.data.ts
new file mode 100644
index 0000000..13e652b
--- /dev/null
+++ b/src/app/modules/user/show-data/show-data.data.ts
@@ -0,0 +1,5 @@
+export const DATA_TABLE: any = [
+ { name: 'XXXX', age: 'XXXX', city: 'XXXX' },
+ { name: 'XXXX', age: 'XXXX', city: 'XXXX' },
+ { name: 'XXXX', age: 'XXXX', city: 'XXXX' },
+];
diff --git a/src/app/shared/component/form/form.component.html b/src/app/shared/component/form/form.component.html
new file mode 100644
index 0000000..fb155d2
--- /dev/null
+++ b/src/app/shared/component/form/form.component.html
@@ -0,0 +1,56 @@
+
diff --git a/src/app/shared/component/form/form.component.scss b/src/app/shared/component/form/form.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/shared/component/form/form.component.ts b/src/app/shared/component/form/form.component.ts
new file mode 100644
index 0000000..33e65b8
--- /dev/null
+++ b/src/app/shared/component/form/form.component.ts
@@ -0,0 +1,45 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
+
+@Component({
+ selector: 'app-form',
+ templateUrl: './form.component.html',
+ styleUrls: ['./form.component.scss'],
+})
+export class FormComponent implements OnInit {
+ form!: FormGroup;
+
+ constructor() {}
+
+ // @Input() set formGroup(value: FormGroup) {
+ // if (value) this._projectForm = value;
+ // }
+
+ ngOnInit() {
+ this.initForm();
+ this.form.valueChanges.subscribe((res) => {
+ console.log(res);
+ });
+ }
+
+ initForm(): void {
+ this.form = new FormGroup({
+ /// cost-form
+ project_name: new FormControl('', [Validators.required]),
+ project_date: new FormControl('', [Validators.required]),
+ customer_name: new FormControl('', [Validators.required]),
+ /// general-form
+ total_budget_no_vat: new FormControl(0, [
+ Validators.required,
+ Validators.min(0),
+ ]),
+ vat_value: new FormControl(0, [Validators.required, Validators.min(0)]),
+ total_budget: new FormControl(0, [
+ Validators.required,
+ Validators.min(0),
+ ]),
+ direct_costs: new FormArray([], [Validators.minLength(1)]),
+ ///
+ });
+ }
+}
diff --git a/src/app/shared/component/form/form.module.ts b/src/app/shared/component/form/form.module.ts
new file mode 100644
index 0000000..c0767f1
--- /dev/null
+++ b/src/app/shared/component/form/form.module.ts
@@ -0,0 +1,27 @@
+import { NgModule } from '@angular/core';
+import { FormComponent } from './form.component';
+import { BrowserModule } from '@angular/platform-browser';
+import { AppRoutingModule } from 'src/app/app-routing.module';
+import { HeroIconModule, allIcons } from 'ng-heroicon';
+import { ReactiveFormsModule } from '@angular/forms';
+
+const components = [FormComponent];
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ ReactiveFormsModule,
+ HeroIconModule.withIcons(
+ {
+ ...allIcons,
+ },
+ {
+ defaultHostDisplay: 'inlineBlock',
+ }
+ ),
+ ],
+ declarations: [...components],
+ exports: [...components],
+})
+export class FormModule {}
diff --git a/src/app/shared/component/sidebar/sidebar.component.html b/src/app/shared/component/sidebar/sidebar.component.html
new file mode 100644
index 0000000..8efb3dd
--- /dev/null
+++ b/src/app/shared/component/sidebar/sidebar.component.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/shared/component/sidebar/sidebar.component.scss b/src/app/shared/component/sidebar/sidebar.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/shared/component/sidebar/sidebar.component.ts b/src/app/shared/component/sidebar/sidebar.component.ts
new file mode 100644
index 0000000..4f78891
--- /dev/null
+++ b/src/app/shared/component/sidebar/sidebar.component.ts
@@ -0,0 +1,30 @@
+import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'app-sidebar',
+ templateUrl: './sidebar.component.html',
+ styleUrls: ['./sidebar.component.scss'],
+})
+export class SidebarComponent implements OnInit {
+ /* -------------------------------------------------------------------------- */
+ /* Variable */
+ /* -------------------------------------------------------------------------- */
+ public _sidebarOpen: boolean = true;
+ public today = new Date(); // Get the current date
+
+ /* -------------------------------------------------------------------------- */
+ /* constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor(private router: Router) {}
+ /* -------------------------------------------------------------------------- */
+ /* Life Circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {}
+ /* -------------------------------------------------------------------------- */
+ /* Functions */
+ /* -------------------------------------------------------------------------- */
+ onToggleSidebar() {
+ this._sidebarOpen = !this._sidebarOpen;
+ }
+}
diff --git a/src/app/shared/component/sidebar/sidebar.module.ts b/src/app/shared/component/sidebar/sidebar.module.ts
new file mode 100644
index 0000000..0e6e6cf
--- /dev/null
+++ b/src/app/shared/component/sidebar/sidebar.module.ts
@@ -0,0 +1,24 @@
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { allIcons, HeroIconModule } from 'ng-heroicon';
+import { SidebarComponent } from './sidebar.component';
+
+const components = [SidebarComponent];
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ HeroIconModule.withIcons(
+ {
+ ...allIcons,
+ },
+ {
+ defaultHostDisplay: 'inlineBlock',
+ }
+ ),
+ ],
+ declarations: [...components],
+ exports: [...components],
+})
+export class SidebarModule {}
diff --git a/src/app/shared/component/table/table.component.html b/src/app/shared/component/table/table.component.html
new file mode 100644
index 0000000..26c4139
--- /dev/null
+++ b/src/app/shared/component/table/table.component.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+ 0">
+
+
+
+
+
+
+
+
+
+
+
+ | {{ header.key }} |
+
+
+
+
+
+ | {{ row.value }} |
+
+
+
+
+
+ | Not Found . |
+
+
diff --git a/src/app/shared/component/table/table.component.scss b/src/app/shared/component/table/table.component.scss
new file mode 100644
index 0000000..cf470a7
--- /dev/null
+++ b/src/app/shared/component/table/table.component.scss
@@ -0,0 +1,51 @@
+.table-container {
+ width: 100%;
+ border-radius: 1.5rem;
+
+ table {
+ width: 100%;
+ border-radius: 0.4rem;
+
+ thead {
+ background-color: #eff3f8;
+
+ th {
+ @apply border-b border-b-slate-200 last:border-none text-slate-500;
+ position: sticky;
+ top: 0;
+ // color: #000000;
+ padding: 1rem;
+ text-align: center;
+
+ &:first-child {
+ border-radius: 0.4rem 0 0 0;
+ }
+ &:last-child {
+ border-radius: 0 0.4rem 0 0;
+ }
+ }
+ }
+
+ tbody {
+ background-color: #ffffff;
+
+ tr {
+ // @apply border-b border-b-slate-200 last:border-none;
+
+ td {
+ @apply text-slate-500;
+ padding: 1rem;
+ // color: #000000;
+ text-align: center;
+
+ &:first-child {
+ border-radius: 0 0 0 0.4rem;
+ }
+ &:last-child {
+ border-radius: 0 0 0.4rem 0;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/shared/component/table/table.component.ts b/src/app/shared/component/table/table.component.ts
new file mode 100644
index 0000000..703232e
--- /dev/null
+++ b/src/app/shared/component/table/table.component.ts
@@ -0,0 +1,104 @@
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ContentChild,
+ EventEmitter,
+ Injector,
+ Input,
+ Output,
+ TemplateRef,
+} from '@angular/core';
+
+@Component({
+ selector: 'app-table',
+ templateUrl: './table.component.html',
+ styleUrls: ['./table.component.scss'],
+})
+export class TableComponent {
+ /* -------------------------------------------------------------------------- */
+ /* Input */
+ /* -------------------------------------------------------------------------- */
+ @Input() data: any[] = [];
+ @Input() showClearSort: boolean = false;
+
+ /* -------------------------------------------------------------------------- */
+ /* Output */
+ /* -------------------------------------------------------------------------- */
+ @Output() clickRow: EventEmitter = new EventEmitter();
+
+ /* -------------------------------------------------------------------------- */
+ /* Content Child */
+ /* -------------------------------------------------------------------------- */
+ @ContentChild('headers') headers!: TemplateRef;
+ @ContentChild('rows') rows!: TemplateRef;
+ @ContentChild('noData') noData!: TemplateRef;
+
+ /* -------------------------------------------------------------------------- */
+ /* Variables */
+ /* -------------------------------------------------------------------------- */
+ rowsDefault: any[] = [];
+ rowsClone: any[] = [];
+
+ /* -------------------------------------------------------------------------- */
+ /* Constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor(
+ private cdr: ChangeDetectorRef,
+ public readonly injector: Injector
+ ) {}
+
+ /* -------------------------------------------------------------------------- */
+ /* Logic Functions */
+ /* -------------------------------------------------------------------------- */
+ sortHeaderByKey(key: string): void {
+ let cloneRowsSort: any[] = JSON.parse(JSON.stringify(this.rowsClone));
+ const cloneRowsCompare: any[] = JSON.parse(JSON.stringify(this.rowsClone));
+
+ const typeValue = typeof cloneRowsSort[0][key];
+
+ switch (typeValue) {
+ case 'string':
+ cloneRowsSort.sort((a, b) => a[key].localeCompare(b[key]));
+ if (
+ JSON.stringify(cloneRowsSort) === JSON.stringify(cloneRowsCompare)
+ ) {
+ cloneRowsSort.sort((a, b) => b[key].localeCompare(a[key]));
+ }
+ break;
+ case 'number':
+ cloneRowsSort.sort((a, b) => a[key] - b[key]);
+ if (
+ JSON.stringify(cloneRowsSort) === JSON.stringify(cloneRowsCompare)
+ ) {
+ cloneRowsSort.sort((a, b) => b[key] - a[key]);
+ }
+ break;
+ default:
+ return;
+ }
+
+ this.rowsClone = cloneRowsSort;
+ this.cdr.markForCheck();
+ }
+
+ resetSort(): void {
+ this.rowsClone = JSON.parse(JSON.stringify(this.rowsDefault));
+ this.cdr.markForCheck();
+ }
+
+ trackByRow(index: number, item: any) {
+ return index;
+ }
+
+ trackByTd(index: number, item: any) {
+ return index;
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* Get Set Functions */
+ /* -------------------------------------------------------------------------- */
+ getKeyRow(row: any): string[] {
+ return Object.keys(row);
+ }
+}
diff --git a/src/app/shared/component/table/table.model.ts b/src/app/shared/component/table/table.model.ts
new file mode 100644
index 0000000..fade24a
--- /dev/null
+++ b/src/app/shared/component/table/table.model.ts
@@ -0,0 +1,17 @@
+export interface HeaderTable {
+ key: string;
+ title: string | number;
+ width?: string;
+ class?: string;
+ sort?: boolean;
+}
+
+export interface DataTable {
+ group_header: string;
+ data: T[];
+}
+
+export interface Table {
+ headers?: HeaderTable[];
+ data: T[] | DataTable[];
+}
diff --git a/src/app/shared/component/table/table.module.ts b/src/app/shared/component/table/table.module.ts
new file mode 100644
index 0000000..5ff6d09
--- /dev/null
+++ b/src/app/shared/component/table/table.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { SharedModule } from 'src/app/shared/shared.module';
+import { TableComponent } from './table.component';
+
+const components = [TableComponent];
+
+@NgModule({
+ imports: [SharedModule],
+ declarations: [...components],
+ exports: [...components],
+})
+export class TableModule {}
diff --git a/src/app/shared/component/toast/toast.component.html b/src/app/shared/component/toast/toast.component.html
new file mode 100644
index 0000000..7fab951
--- /dev/null
+++ b/src/app/shared/component/toast/toast.component.html
@@ -0,0 +1,204 @@
+ 0"
+ class="absolute top-0 right-0 w-96 h-auto p-5 z-50"
+>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Info
+
+ {{ toast.message }}
+
+
+
+
+
+
+
+
+
Error
+
+ {{ toast.message }}
+
+
+
+
+
+
+
+
+
Success
+
+ {{ toast.message }}
+
+
+
+
+
+
+
+
+
Warning
+
+ {{ toast.message }}
+
+
+
+
diff --git a/src/app/shared/component/toast/toast.component.scss b/src/app/shared/component/toast/toast.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/shared/component/toast/toast.component.ts b/src/app/shared/component/toast/toast.component.ts
new file mode 100644
index 0000000..390e035
--- /dev/null
+++ b/src/app/shared/component/toast/toast.component.ts
@@ -0,0 +1,120 @@
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ Input,
+ OnDestroy,
+ OnInit,
+} from '@angular/core';
+import { Toast, ToastType } from './toast.model';
+
+import { Subscription } from 'rxjs';
+import { ToastService } from '../../services/toast.service';
+
+@Component({
+ selector: 'app-toast',
+ templateUrl: './toast.component.html',
+ styleUrls: ['./toast.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ToastComponent implements OnInit, OnDestroy {
+ /* -------------------------------------------------------------------------- */
+ /* Input */
+ /* -------------------------------------------------------------------------- */
+ @Input() id = 'default-toast';
+ @Input() fade = true;
+
+ /* -------------------------------------------------------------------------- */
+ /* Variables */
+ /* -------------------------------------------------------------------------- */
+ toasts: Toast[] = [];
+
+ /* -------------------------------------------------------------------------- */
+ /* Reactive Variables */
+ /* -------------------------------------------------------------------------- */
+ toastSubscription!: Subscription;
+ routeSubscription!: Subscription;
+
+ /* -------------------------------------------------------------------------- */
+ /* Constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor(
+ private _toastService: ToastService,
+ private _cdr: ChangeDetectorRef
+ ) {}
+
+ /* -------------------------------------------------------------------------- */
+ /* Life Circle */
+ /* -------------------------------------------------------------------------- */
+ ngOnInit() {
+ // subscribe to new toast notifications
+ this.toastSubscription = this._toastService
+ .onToast(this.id)
+ .subscribe((toast) => {
+ // clear toasts when an empty toast is received
+ if (!toast.message) {
+ // filter out toasts without 'keepAfterRouteChange' flag
+ this.toasts = this.toasts.filter((x) => x.keepAfterRouteChange);
+ this._cdr.markForCheck();
+
+ // remove 'keepAfterRouteChange' flag on the rest
+ // this.toasts.forEach(x => (x?.keepAfterRouteChange ? delete x.keepAfterRouteChange : null));
+ return;
+ }
+
+ // add toast to array
+ this.toasts.push(toast);
+ this._cdr.markForCheck();
+
+ // auto close toast if required
+ if (toast.autoClose) {
+ setTimeout(() => this.removeToast(toast), 3000);
+ }
+ });
+
+ // clear toasts on location change
+ // this.routeSubscription = this.router.events.subscribe(event => {
+ // if (event instanceof NavigationStart) {
+ // this._toastService.clear(this.id);
+ // }
+ // });
+ }
+
+ ngOnDestroy() {
+ // unsubscribe to avoid memory leaks
+ this.toastSubscription?.unsubscribe();
+ this.routeSubscription?.unsubscribe();
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* Logic Functions */
+ /* -------------------------------------------------------------------------- */
+ removeToast(toast: Toast) {
+ // check if already removed to prevent error on auto close
+ if (!this.toasts.includes(toast)) return;
+
+ if (this.fade) {
+ // fade out toast
+ const toastIndex = this.toasts.findIndex((x) => x === toast);
+ this.toasts[toastIndex].fade = true;
+ this._cdr.markForCheck();
+
+ // remove toast after faded out
+ setTimeout(() => {
+ this.toasts = this.toasts.filter((x) => x !== toast);
+ this._cdr.markForCheck();
+ }, 250);
+ } else {
+ // remove toast
+ this.toasts = this.toasts.filter((x) => x !== toast);
+ this._cdr.markForCheck();
+ }
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* Get Set Functions */
+ /* -------------------------------------------------------------------------- */
+ get toastType(): typeof ToastType {
+ return ToastType;
+ }
+}
diff --git a/src/app/shared/component/toast/toast.model.ts b/src/app/shared/component/toast/toast.model.ts
new file mode 100644
index 0000000..0e89cb1
--- /dev/null
+++ b/src/app/shared/component/toast/toast.model.ts
@@ -0,0 +1,19 @@
+export class Toast {
+ id!: string;
+ type!: ToastType;
+ message!: string;
+ autoClose!: boolean;
+ keepAfterRouteChange!: boolean;
+ fade!: boolean;
+
+ constructor(init?: Partial) {
+ Object.assign(this, init);
+ }
+}
+
+export enum ToastType {
+ Success,
+ Error,
+ Info,
+ Warning,
+}
diff --git a/src/app/shared/component/toast/toast.module.ts b/src/app/shared/component/toast/toast.module.ts
new file mode 100644
index 0000000..37a9740
--- /dev/null
+++ b/src/app/shared/component/toast/toast.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ToastComponent } from './toast.component';
+
+const components = [ToastComponent];
+
+@NgModule({
+ imports: [CommonModule],
+ declarations: [...components],
+ exports: [...components],
+})
+export class ToastModule {}
diff --git a/src/app/shared/config/components.ts b/src/app/shared/config/components.ts
new file mode 100644
index 0000000..6360482
--- /dev/null
+++ b/src/app/shared/config/components.ts
@@ -0,0 +1,29 @@
+import { AppComponent } from 'src/app/app.component';
+import { SignInComponent } from 'src/app/modules/auth/sign-in/sign-in.component';
+import { AddDataComponent } from 'src/app/modules/user/add-data/add-data.component';
+import { CardDetailComponent } from 'src/app/modules/user/card/card-detail/card-detail.component';
+import { CardListComponent } from 'src/app/modules/user/card/card-list/card-list.component';
+import { CardComponent } from 'src/app/modules/user/card/card.component';
+import { DashboardComponent } from 'src/app/modules/user/dashboard/dashboard.component';
+import { FormInputComponent } from 'src/app/modules/user/form-input/form-input.component';
+import { CostFormComponent } from 'src/app/modules/user/form-project/cost-form/cost-form.component';
+import { FormProjectComponent } from 'src/app/modules/user/form-project/form-project.component';
+import { GeneralFormComponent } from 'src/app/modules/user/form-project/general-form/general-form.component';
+import { SummaryFormComponent } from 'src/app/modules/user/form-project/summary-form/summary-form.component';
+import { ShowDataComponent } from 'src/app/modules/user/show-data/show-data.component';
+
+export const components = [
+ AppComponent,
+ DashboardComponent,
+ SignInComponent,
+ CardComponent,
+ ShowDataComponent,
+ AddDataComponent,
+ CardListComponent,
+ CardDetailComponent,
+ FormProjectComponent,
+ GeneralFormComponent,
+ CostFormComponent,
+ SummaryFormComponent,
+ FormInputComponent,
+];
diff --git a/src/app/shared/config/modules.ts b/src/app/shared/config/modules.ts
new file mode 100644
index 0000000..ae876da
--- /dev/null
+++ b/src/app/shared/config/modules.ts
@@ -0,0 +1,38 @@
+import { CommonModule } from '@angular/common';
+import { AngularFireModule } from '@angular/fire/compat';
+import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { BrowserModule } from '@angular/platform-browser';
+import { HeroIconModule, allIcons } from 'ng-heroicon';
+import { AppRoutingModule } from 'src/app/app-routing.module';
+import { environment } from 'src/environments/environment';
+import { SidebarModule } from '../component/sidebar/sidebar.module';
+import { TableModule } from '../component/table/table.module';
+import { FormModule } from '../component/form/form.module';
+import { RouterModule } from '@angular/router';
+
+const componentModule = [SidebarModule, TableModule, FormModule];
+
+const firebaseModule = [
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFirestoreModule, // For Firestore
+];
+
+const commonModule = [
+ RouterModule,
+ ReactiveFormsModule,
+ FormsModule,
+ // BrowserModule,
+ AppRoutingModule,
+ CommonModule,
+ HeroIconModule.withIcons(
+ {
+ ...allIcons,
+ },
+ {
+ defaultHostDisplay: 'inlineBlock',
+ }
+ ),
+];
+
+export const imports = [...firebaseModule, ...componentModule, ...commonModule];
diff --git a/src/app/shared/config/store.ts b/src/app/shared/config/store.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/shared/services/toast.service.ts b/src/app/shared/services/toast.service.ts
new file mode 100644
index 0000000..f8a49c8
--- /dev/null
+++ b/src/app/shared/services/toast.service.ts
@@ -0,0 +1,73 @@
+import { Observable, Subject, filter } from 'rxjs';
+
+import { Injectable } from '@angular/core';
+import { Toast, ToastType } from '../component/toast/toast.model';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class ToastService {
+ /* -------------------------------------------------------------------------- */
+ /* Variable */
+ /* -------------------------------------------------------------------------- */
+ private subject = new Subject();
+ private defaultId = 'default-toast';
+
+ /* -------------------------------------------------------------------------- */
+ /* Constructor */
+ /* -------------------------------------------------------------------------- */
+ constructor() {}
+
+ /* -------------------------------------------------------------------------- */
+ /* enable subscribing to alerts observable */
+ /* -------------------------------------------------------------------------- */
+ onToast(id = this.defaultId): Observable {
+ return this.subject.asObservable().pipe(filter((x) => x && x.id === id));
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* convenience methods */
+ /* -------------------------------------------------------------------------- */
+ success(message: string, options?: Partial) {
+ options = {
+ autoClose: options?.autoClose === false ? false : true,
+ };
+ this.toast(new Toast({ ...options, type: ToastType.Success, message }));
+ }
+
+ error(message: string, options?: Partial) {
+ options = {
+ autoClose: options?.autoClose === false ? false : true,
+ };
+ this.toast(new Toast({ ...options, type: ToastType.Error, message }));
+ }
+
+ info(message: string, options?: Partial) {
+ options = {
+ autoClose: options?.autoClose === false ? false : true,
+ };
+ this.toast(new Toast({ ...options, type: ToastType.Info, message }));
+ }
+
+ warn(message: string, options?: Partial) {
+ options = {
+ autoClose: options?.autoClose === false ? false : true,
+ };
+ this.toast(new Toast({ ...options, type: ToastType.Warning, message }));
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* main toast method */
+ /* -------------------------------------------------------------------------- */
+ toast(toast: Toast) {
+ toast.id = toast.id || this.defaultId;
+ this.subject.next(toast);
+ }
+
+ /* -------------------------------------------------------------------------- */
+ /* clear alerts */
+ /* -------------------------------------------------------------------------- */
+ clear(id = this.defaultId) {
+ this.subject.next(new Toast({ id }));
+ }
+}
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
new file mode 100644
index 0000000..7e8c4be
--- /dev/null
+++ b/src/app/shared/shared.module.ts
@@ -0,0 +1,12 @@
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+
+const modules = [CommonModule, FormsModule, ReactiveFormsModule];
+
+@NgModule({
+ declarations: [],
+ imports: [...modules],
+ exports: [...modules],
+})
+export class SharedModule {}
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index 502e95c..05532eb 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -1,12 +1,12 @@
export const environment = {
production: false,
firebase: {
- apiKey: 'xxx',
- authDomain: 'bezkoder-firebase.firebaseapp.com',
- databaseURL: 'https://bezkoder-firebase.firebaseio.com',
- projectId: 'bezkoder-firebase',
- storageBucket: 'bezkoder-firebase.appspot.com',
- messagingSenderId: 'xxx',
- appId: 'xxx'
- }
-};
\ No newline at end of file
+ apiKey: 'AIzaSyD3EVluxQNUBL41XQYiOLE2sSCzn8YTb4o',
+ authDomain: 'authentication-app-946bc.firebaseapp.com',
+ databaseURL: 'https://authentication-app-946bc-default-rtdb.firebaseio.com',
+ projectId: 'authentication-app-946bc',
+ storageBucket: 'authentication-app-946bc.appspot.com',
+ messagingSenderId: '188956505376',
+ appId: '1:188956505376:web:34047a53be8372e58c9345',
+ },
+};
diff --git a/src/styles.scss b/src/styles.scss
deleted file mode 100644
index 90d4ee0..0000000
--- a/src/styles.scss
+++ /dev/null
@@ -1 +0,0 @@
-/* You can add global styles to this file, and also import other style files */
diff --git a/src/styles/customs/buttons.scss b/src/styles/customs/buttons.scss
new file mode 100644
index 0000000..a62c031
--- /dev/null
+++ b/src/styles/customs/buttons.scss
@@ -0,0 +1,46 @@
+.button {
+ &-default {
+ @apply text-white bg-blue-700 hover:bg-blue-800 focus:outline-none active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-blue-300
+ font-medium rounded-md text-sm px-5 py-2.5 text-center mr-2 mb-2;
+ }
+
+ &-alternative {
+ @apply py-2.5 px-5 mr-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-md
+ border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-gray-700;
+ }
+
+ &-dark {
+ @apply text-white bg-gray-800 hover:bg-gray-900 focus:outline-none active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-gray-700 font-medium rounded-md text-sm px-5 py-2.5 mr-2 mb-2;
+ }
+
+ &-light {
+ @apply text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-gray-700 font-medium rounded-md text-sm px-5 py-2.5 mr-2 mb-2;
+ }
+
+ &-green {
+ @apply text-white bg-green-700 hover:bg-green-800 focus:outline-none active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-green-700 font-medium rounded-md text-sm px-5 py-2.5 text-center mr-2 mb-2;
+ }
+
+ &-red {
+ @apply text-white bg-red-700 hover:bg-red-800 focus:outline-none active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-red-700 font-medium rounded-md text-sm px-5 py-2.5 text-center mr-2 mb-2;
+ }
+
+ &-yellow {
+ @apply text-white bg-yellow-500 hover:bg-yellow-600 focus:outline-none active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-yellow-700 font-medium rounded-md text-sm px-5 py-2.5 text-center mr-2 mb-2;
+ }
+
+ &-purple {
+ @apply text-white bg-purple-700 hover:bg-purple-800 focus:outline-none active:ring-4 focus:ring-4 focus:ring-opacity-40 focus:ring-purple-700 font-medium rounded-md text-sm px-5 py-2.5 text-center mb-2;
+ }
+
+ &-default-rounded,
+ &-alternative-rounded,
+ &-dark-rounded,
+ &-light-rounded,
+ &-green-rounded,
+ &-red-rounded,
+ &-yellow-rounded,
+ &-purple-rounded {
+ @apply rounded-full;
+ }
+}
diff --git a/src/styles/customs/form.scss b/src/styles/customs/form.scss
new file mode 100644
index 0000000..a6f91c4
--- /dev/null
+++ b/src/styles/customs/form.scss
@@ -0,0 +1,42 @@
+.input {
+ @apply relative;
+
+ &-text,
+ &-password {
+ @apply text-black block m-0 p-2.5 w-full font-[2.1rem] rounded-[0.4rem] border border-slate-300
+ shadow-sm outline-none;
+ line-height: 1.8;
+ transition: box-shadow 300ms;
+
+ &::placeholder {
+ color: #b0bec5;
+ }
+
+ &:focus {
+ @apply shadow-md;
+ }
+ }
+
+ &-label {
+ display: block;
+ position: absolute;
+ bottom: 50%;
+ left: 1rem;
+ color: #fff;
+ font-family: inherit;
+ font-size: 2.1rem;
+ font-weight: inherit;
+ line-height: 1.8;
+ opacity: 0;
+ transform: translate3d(0, 50%, 0) scale(1);
+ transform-origin: 0 0;
+ transition: opacity 300s cubic-bezier(0.645, 0.045, 0.355, 1),
+ transform 300s cubic-bezier(0.645, 0.045, 0.355, 1),
+ visibility 0ms 300s cubic-bezier(0.645, 0.045, 0.355, 1),
+ z-index 0ms 300s cubic-bezier(0.645, 0.045, 0.355, 1);
+ }
+
+ &-invalid {
+ @apply border border-red-400;
+ }
+}
diff --git a/src/styles/customs/scrollbar.scss b/src/styles/customs/scrollbar.scss
new file mode 100644
index 0000000..561b52b
--- /dev/null
+++ b/src/styles/customs/scrollbar.scss
@@ -0,0 +1,21 @@
+/* ===== Scrollbar CSS ===== */
+/* Firefox */
+* {
+ scrollbar-width: thin;
+ scrollbar-color: #7c6e65 #ffffff;
+}
+
+/* Chrome, Edge, and Safari */
+*::-webkit-scrollbar {
+ width: 11px;
+}
+
+*::-webkit-scrollbar-track {
+ background: #ffffff;
+}
+
+*::-webkit-scrollbar-thumb {
+ background-color: #7c6e65;
+ border-radius: 16px;
+ border: 3px solid #ffffff;
+}
diff --git a/src/styles/customs/table.scss b/src/styles/customs/table.scss
new file mode 100644
index 0000000..031cdc6
--- /dev/null
+++ b/src/styles/customs/table.scss
@@ -0,0 +1,54 @@
+.table-class {
+ background-color: #ffffff;
+ border-radius: 1.5rem;
+ width: 100%;
+ height: 100%;
+ border-radius: 0.4rem;
+
+ thead {
+ background-color: #eff3f8;
+
+ th {
+ @apply border-b border-b-slate-200 text-slate-500;
+ padding: 1rem;
+
+ &:first-child {
+ border-radius: 0.4rem 0 0 0;
+ }
+ &:last-child {
+ border-radius: 0 0.4rem 0 0;
+ }
+ }
+ }
+
+ thead,
+ tbody tr {
+ display: table;
+ width: 100%;
+ table-layout: fixed; /* even columns width , fix width of table too*/
+ }
+
+ tbody {
+ background-color: #ffffff;
+ display: block;
+ height: 100%;
+ overflow: auto;
+
+ tr {
+ @apply border-b border-b-slate-200;
+
+ td {
+ @apply text-slate-500;
+ padding: 1rem;
+ // color: #000000;
+
+ &:first-child {
+ border-radius: 0 0 0 0.4rem;
+ }
+ &:last-child {
+ border-radius: 0 0 0.4rem 0;
+ }
+ }
+ }
+ }
+}
diff --git a/src/styles/styles.scss b/src/styles/styles.scss
index 33c347e..1bdebf4 100644
--- a/src/styles/styles.scss
+++ b/src/styles/styles.scss
@@ -1,4 +1,35 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
+/* You can add global styles to this file, and also import other style files */
@import "./customs/fonts.scss";
+@import "./customs/scrollbar.scss";
+// @import "./customs/form.scss";
+@import "./customs/table.scss";
+// @import './customs/buttons.scss';
+@import "./tailwind.scss";
+
+/* Background - Colors */
+.bg-login {
+ height: 100vh;
+ width: 100%;
+ // background-image: linear-gradient(130deg, #1abc9c, #2ecc71);
+}
+
+/* Sidebar - Effects */
+.sidebar {
+ background: linear-gradient(173.81deg, #ffffff 7.11%, #ffffff 207.37%);
+ transform: translateX(-400px);
+ min-width: 18rem;
+ max-width: 18rem;
+ width: 0%;
+ &.active {
+ width: 100%;
+ transform: translateX(0);
+ }
+}
+
+input {
+ padding-left: 10px;
+}
+
+label {
+ @apply text-slate-500;
+}
diff --git a/src/styles/tailwind.scss b/src/styles/tailwind.scss
new file mode 100644
index 0000000..07bf451
--- /dev/null
+++ b/src/styles/tailwind.scss
@@ -0,0 +1,4 @@
+/* - Tailwind - */
+@tailwind base;
+@tailwind components;
+@tailwind utilities;