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 @@ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ ADD

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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 @@ +
+
+ +
+ + + +
+
+
+
+

@FormGroup I

+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ {{ data_form_1 | json }} +
+
+
+ + +
+
+
+
+

@FormGroup II

+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ {{ data_form_2 | json }} +
+
+ + +
+
+
+
+

@NgForm

+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+

First name value: {{ first.value }}

+

First name valid: {{ first.valid }}

+

Form value: {{ data.value | json }}

+

Form valid: {{ data.valid }}

+
+
+
+ + +
+
+
+
+

@Form

+
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+ + +
+
+
+
+

@FormControl

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+ + +
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

+
+ + + + + + + + + + + + + + + + + + + + + +
XXXXXXXXXXXXXXXX
{{ 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 @@ + +
+ + + + + + +
+
+ +
{{ today }}
+
+
+ + + +
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 @@ +
+
+ +
+ + + + + + + + + + + + +
+
+ + + + {{ 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 @@ +
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + 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;