diff --git a/angular.json b/angular.json index 3667153ac..713797410 100644 --- a/angular.json +++ b/angular.json @@ -37,7 +37,10 @@ "src/assets" ], "styles": [ - "src/scss/styles.scss" + "src/scss/styles.scss", + "node_modules/primeng/resources/themes/lara-light-blue/theme.css", + "node_modules/primeng/resources/primeng.min.css", + "node_modules/primeicons/primeicons.css" ], "scripts": [], "allowedCommonJsDependencies": [], diff --git a/package-lock.json b/package-lock.json index 786849d93..bac02451c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@angular/platform-browser": "^21.0.6", "@angular/platform-browser-dynamic": "^21.0.6", "@angular/router": "^21.0.6", + "@auth0/angular-jwt": "^5.2.0", "@coreui/angular": "~5.6.4", "@coreui/angular-chartjs": "~5.6.4", "@coreui/chartjs": "~4.1.0", @@ -27,9 +28,16 @@ "@coreui/icons": "^3.0.1", "@coreui/icons-angular": "~5.6.4", "@coreui/utils": "^2.0.2", + "@types/mime-types": "^3.0.1", "chart.js": "^4.5.1", + "jwt-decode": "^4.0.0", "lodash-es": "^4.17.22", + "mime": "^4.1.0", + "mime-types": "^3.0.2", "ngx-scrollbar": "^13.0.3", + "primeicons": "^7.0.0", + "primeng": "^21.0.2", + "react-spinners": "^0.17.0", "rxjs": "~7.8.2", "tslib": "^2.8.1", "zone.js": "~0.16.0" @@ -701,6 +709,17 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@auth0/angular-jwt": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@auth0/angular-jwt/-/angular-jwt-5.2.0.tgz", + "integrity": "sha512-9FS2L0QwGNlxA/zgeehCcsR9CZscouyXkoIj1fODM36A8BLfdzg9k9DWAXUQ2Drjk0AypGAFzeNZR4vsLMhdeQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=14.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -989,7 +1008,6 @@ "version": "5.6.4", "resolved": "https://registry.npmjs.org/@coreui/angular/-/angular-5.6.4.tgz", "integrity": "sha512-iOelu602tuEoQLj0esOx/HqJMxquQKiMvm6heeYTtk/GB4BegSPlqUVdbNkfK+MlTlxrYdORfE5Ymz0zvRDo3g==", - "license": "MIT", "dependencies": { "@popperjs/core": "~2.11.8", "tslib": "^2.3.0" @@ -3257,6 +3275,44 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@primeuix/motion": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@primeuix/motion/-/motion-0.0.10.tgz", + "integrity": "sha512-PsZwOPq79Scp7/ionshRcQ5xKVf9+zuLcyY5mf6onK8chHT5C9JGphmcIZ4CzcqxuGEpsm8AIbTGy+zS3RtzLA==", + "dependencies": { + "@primeuix/utils": "^0.6.3" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primeuix/styled": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.7.4.tgz", + "integrity": "sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==", + "dependencies": { + "@primeuix/utils": "^0.6.1" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primeuix/styles": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@primeuix/styles/-/styles-2.0.2.tgz", + "integrity": "sha512-LNtkJsTonNHF5ag+9s3+zQzm00+LRmffw68QRIHy6S/dam1JpdrrAnUzNYlWbaY7aE2EkZvQmx7Np7+PyHn+ow==", + "dependencies": { + "@primeuix/styled": "^0.7.4" + } + }, + "node_modules/@primeuix/utils": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.6.3.tgz", + "integrity": "sha512-/SLNQSKQ73WbBIsflKVqbpVjCfFYvQO3Sf1LMheXyxh8JqxO4M63dzP56wwm9OPGuCQ6MYOd2AHgZXz+g7PZcg==", + "engines": { + "node": ">=12.11.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-beta.47", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.47.tgz", @@ -4063,6 +4119,11 @@ "@types/lodash": "*" } }, + "node_modules/@types/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==" + }, "node_modules/@types/node": { "version": "24.10.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", @@ -6465,6 +6526,14 @@ ], "license": "MIT" }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, "node_modules/karma": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", @@ -6745,6 +6814,18 @@ "node": ">= 0.6" } }, + "node_modules/karma/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/karma/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -7252,23 +7333,23 @@ } }, "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", + "funding": [ + "https://github.com/sponsors/broofa" + ], "bin": { - "mime": "cli.js" + "mime": "bin/cli.js" }, "engines": { - "node": ">=4.0.0" + "node": ">=16" } }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -7278,8 +7359,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "dev": true, - "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, @@ -8215,6 +8294,32 @@ "dev": true, "license": "MIT" }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==" + }, + "node_modules/primeng": { + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-21.0.2.tgz", + "integrity": "sha512-suf+xK3V3z2aG9OmQMAriqhMt79JM3jBSoRDu4XQKCiTiWDRnOvoVdCZCjByT32sE5tYqhrLBsCH0lT7RLcosg==", + "dependencies": { + "@primeuix/motion": "^0.0.10", + "@primeuix/styled": "^0.7.4", + "@primeuix/styles": "^2.0.2", + "@primeuix/utils": "^0.6.3", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": "^21.0.0", + "@angular/common": "^21.0.0", + "@angular/core": "^21.0.0", + "@angular/forms": "^21.0.0", + "@angular/platform-browser": "^21.0.0", + "@angular/router": "^21.0.0", + "rxjs": "^6.0.0 || ^7.8.1" + } + }, "node_modules/proc-log": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", @@ -8312,6 +8417,36 @@ "node": ">= 0.10" } }, + "node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "node_modules/react-spinners": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.17.0.tgz", + "integrity": "sha512-L/8HTylaBmIWwQzIjMq+0vyaRXuoAevzWoD35wKpNTxxtYXWZp+xtgkfD7Y4WItuX0YvdxMPU79+7VhhmbmuTQ==", + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -8577,6 +8712,12 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "peer": true + }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", diff --git a/package.json b/package.json index a4518dc72..a8ffefda3 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@angular/platform-browser": "^21.0.6", "@angular/platform-browser-dynamic": "^21.0.6", "@angular/router": "^21.0.6", + "@auth0/angular-jwt": "^5.2.0", "@coreui/angular": "~5.6.4", "@coreui/angular-chartjs": "~5.6.4", "@coreui/chartjs": "~4.1.0", @@ -37,9 +38,16 @@ "@coreui/icons": "^3.0.1", "@coreui/icons-angular": "~5.6.4", "@coreui/utils": "^2.0.2", + "@types/mime-types": "^3.0.1", "chart.js": "^4.5.1", + "jwt-decode": "^4.0.0", "lodash-es": "^4.17.22", + "mime": "^4.1.0", + "mime-types": "^3.0.2", "ngx-scrollbar": "^13.0.3", + "primeicons": "^7.0.0", + "primeng": "^21.0.2", + "react-spinners": "^0.17.0", "rxjs": "~7.8.2", "tslib": "^2.8.1", "zone.js": "~0.16.0" diff --git a/src/app/Interceptor/auth.interceptor.ts b/src/app/Interceptor/auth.interceptor.ts new file mode 100644 index 000000000..4be101bc9 --- /dev/null +++ b/src/app/Interceptor/auth.interceptor.ts @@ -0,0 +1,18 @@ +import { HttpInterceptorFn, HttpRequest, HttpEvent, HttpHandlerFn } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +export const authInterceptor: HttpInterceptorFn = (req: HttpRequest, next: HttpHandlerFn): Observable> => { + // Get token logic... + const token = localStorage.getItem('token'); // Replace with actual token retrieval + + if (token) { + const authReq = req.clone({ + setHeaders: { + Authorization: `Bearer ${token}` + } + }); + return next(authReq); + } + + return next(req); +}; \ No newline at end of file diff --git a/src/app/RouteGuard/AuthGuard.ts b/src/app/RouteGuard/AuthGuard.ts new file mode 100644 index 000000000..7ea7bb0fd --- /dev/null +++ b/src/app/RouteGuard/AuthGuard.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { Observable } from 'rxjs'; +import { AuthService } from '../services/auth.service'; // Assume you have an authentication service + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + decodeToken:any; + decodedTxt:any; + constructor(private authService: AuthService, private router: Router) {} + + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean { + this.decodeToken = this.authService.getDecodeToken(); + this.decodedTxt = JSON.parse(this.decodeToken); + if (this.decodedTxt?.isAdmin) { + console.log("Admin") + this.router.navigate(['/forms/form-control']); + return true + } else { + console.log("User") + return false; // Block navigation + } + } +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3514d8698..2efa9ff46 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -14,7 +14,7 @@ import { iconSubset } from './icons/icon-subset'; imports: [RouterOutlet] }) export class AppComponent implements OnInit { - title = 'CoreUI Angular Admin Template'; + title = 'Ecomm'; readonly #destroyRef: DestroyRef = inject(DestroyRef); readonly #activatedRoute: ActivatedRoute = inject(ActivatedRoute); diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 8196731ab..5f133429d 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -10,9 +10,13 @@ import { } from '@angular/router'; import { IconSetService } from '@coreui/icons-angular'; import { routes } from './app.routes'; +import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import {authInterceptor} from './Interceptor/auth.interceptor'; +import { provideAnimations } from '@angular/platform-browser/animations'; export const appConfig: ApplicationConfig = { providers: [ + provideHttpClient(withInterceptors([authInterceptor])), provideRouter(routes, withRouterConfig({ onSameUrlNavigation: 'reload' @@ -26,7 +30,7 @@ export const appConfig: ApplicationConfig = { withHashLocation() ), IconSetService, - provideAnimationsAsync() + provideAnimations() ] }; diff --git a/src/app/layout/default-layout/_nav.ts b/src/app/layout/default-layout/_nav.ts index db48a851b..b5322f70f 100644 --- a/src/app/layout/default-layout/_nav.ts +++ b/src/app/layout/default-layout/_nav.ts @@ -1,4 +1,6 @@ import { INavData } from '@coreui/angular'; +import { CanActivate } from '@angular/router'; +import { AuthGuard } from '../../RouteGuard/AuthGuard'; export const navItems: INavData[] = [ { @@ -15,12 +17,12 @@ export const navItems: INavData[] = [ name: 'Theme' }, { - name: 'Colors', + name: 'Product Details', url: '/theme/colors', iconComponent: { name: 'cil-drop' } }, { - name: 'Typography', + name: 'AddToCart', url: '/theme/typography', linkProps: { fragment: 'headings' }, iconComponent: { name: 'cil-pencil' } @@ -189,9 +191,9 @@ export const navItems: INavData[] = [ attributes: { target: '_blank' } }, { - name: 'Form Control', + name: 'Add Product', url: '/forms/form-control', - icon: 'nav-icon-bullet' + icon: 'nav-icon-bullet', }, { name: 'Checks & Radios', diff --git a/src/app/layout/default-layout/default-header/default-header.component.html b/src/app/layout/default-layout/default-header/default-header.component.html index 34fcecfc2..173488013 100644 --- a/src/app/layout/default-layout/default-header/default-header.component.html +++ b/src/app/layout/default-layout/default-header/default-header.component.html @@ -16,11 +16,21 @@ Dashboard + + + + + @@ -46,12 +56,15 @@ + - + + + @@ -59,15 +72,30 @@ +
+

{{decodedEmail}}

+

{{decodedTxt?.isAdmin ? 'Admin' : 'User'}}

+
  • @@ -97,7 +125,7 @@
    42
  • -
  • +
  • @@ -150,7 +178,7 @@
    + Logout diff --git a/src/app/layout/default-layout/default-header/default-header.component.ts b/src/app/layout/default-layout/default-header/default-header.component.ts index 6f4d6954e..e5d412bc9 100644 --- a/src/app/layout/default-layout/default-header/default-header.component.ts +++ b/src/app/layout/default-layout/default-header/default-header.component.ts @@ -1,6 +1,6 @@ -import { NgTemplateOutlet } from '@angular/common'; -import { Component, computed, inject, input } from '@angular/core'; -import { RouterLink, RouterLinkActive } from '@angular/router'; +import { CommonModule, NgTemplateOutlet } from '@angular/common'; +import { Component, computed, inject, input, OnInit } from '@angular/core'; +import { Router, RouterLink, RouterLinkActive } from '@angular/router'; import { AvatarComponent, @@ -23,17 +23,24 @@ import { } from '@coreui/angular'; import { IconDirective } from '@coreui/icons-angular'; +import { AuthService } from '../../../services/auth.service'; +import { FormsModule } from '@angular/forms'; +import { ProductService } from '../../../services/product.service'; @Component({ selector: 'app-default-header', templateUrl: './default-header.component.html', - imports: [ContainerComponent, HeaderTogglerDirective, SidebarToggleDirective, IconDirective, HeaderNavComponent, NavItemComponent, NavLinkDirective, RouterLink, RouterLinkActive, NgTemplateOutlet, BreadcrumbRouterComponent, DropdownComponent, DropdownToggleDirective, AvatarComponent, DropdownMenuDirective, DropdownHeaderDirective, DropdownItemDirective, BadgeComponent, DropdownDividerDirective] + imports: [ContainerComponent,FormsModule, HeaderTogglerDirective, SidebarToggleDirective, IconDirective, HeaderNavComponent, NavItemComponent, NavLinkDirective, RouterLink, RouterLinkActive, NgTemplateOutlet, BreadcrumbRouterComponent, DropdownComponent, DropdownToggleDirective, AvatarComponent, DropdownMenuDirective, DropdownHeaderDirective, DropdownItemDirective, BadgeComponent, DropdownDividerDirective] }) -export class DefaultHeaderComponent extends HeaderComponent { +export class DefaultHeaderComponent extends HeaderComponent{ readonly #colorModeService = inject(ColorModeService); readonly colorMode = this.#colorModeService.colorMode; - + decodedImage:any; + decodedEmail:any; + decodeToken:any; + decodedTxt:any; + searchTxt = ''; readonly colorModes = [ { name: 'light', text: 'Light', icon: 'cilSun' }, { name: 'dark', text: 'Dark', icon: 'cilMoon' }, @@ -45,10 +52,29 @@ export class DefaultHeaderComponent extends HeaderComponent { return this.colorModes.find(mode => mode.name === currentMode)?.icon ?? 'cilSun'; }); - constructor() { + constructor(private authService:AuthService, + private router:Router,private productService:ProductService) { super(); } + ngOnInit() { + let email = ''; + this.decodeToken = this.authService.getDecodeToken(); + this.decodedTxt = JSON.parse(this.decodeToken); + this.decodedImage = this.decodedTxt?.image; + this.decodedEmail = this.decodedTxt?.email; + } + + handleLogout(){ + this.authService.removeToken(); + this.router.navigateByUrl('/login') + } + + //search + getSearchProduct(searchTxt:string){ + return this.productService.getSearchProduct(searchTxt) + } + sidebarId = input('sidebar1'); public newMessages = [ diff --git a/src/app/layout/default-layout/default-layout.component.html b/src/app/layout/default-layout/default-layout.component.html index f7ae8e188..3d2f2465b 100644 --- a/src/app/layout/default-layout/default-layout.component.html +++ b/src/app/layout/default-layout/default-layout.component.html @@ -7,15 +7,20 @@ visible > - + Logo + + + + + - - - @if (!sidebar1.narrow) { diff --git a/src/app/layout/default-layout/default-layout.component.ts b/src/app/layout/default-layout/default-layout.component.ts index 2e01ea849..fac218603 100644 --- a/src/app/layout/default-layout/default-layout.component.ts +++ b/src/app/layout/default-layout/default-layout.component.ts @@ -1,12 +1,10 @@ import { Component } from '@angular/core'; -import { RouterLink, RouterOutlet } from '@angular/router'; +import { RouterOutlet } from '@angular/router'; import { NgScrollbar } from 'ngx-scrollbar'; -import { IconDirective } from '@coreui/icons-angular'; import { ContainerComponent, ShadowOnScrollDirective, - SidebarBrandComponent, SidebarComponent, SidebarFooterComponent, SidebarHeaderComponent, @@ -17,6 +15,7 @@ import { import { DefaultFooterComponent, DefaultHeaderComponent } from './'; import { navItems } from './_nav'; +import { AuthService } from '../../services/auth.service'; function isOverflown(element: HTMLElement) { return ( @@ -32,7 +31,6 @@ function isOverflown(element: HTMLElement) { imports: [ SidebarComponent, SidebarHeaderComponent, - SidebarBrandComponent, SidebarNavComponent, SidebarFooterComponent, SidebarToggleDirective, @@ -40,13 +38,33 @@ function isOverflown(element: HTMLElement) { ContainerComponent, DefaultFooterComponent, DefaultHeaderComponent, - IconDirective, NgScrollbar, RouterOutlet, - RouterLink, ShadowOnScrollDirective ] }) export class DefaultLayoutComponent { public navItems = [...navItems]; + decodeToken:any;decodedTxt:any; + isItemFound = true; + matchedItem:any; + constructor(private authService:AuthService){} + + ngOnInit() { + this.navItems.filter(item => { + // console.log(item.children) + + this.decodeToken = this.authService.getDecodeToken(); + this.decodedTxt = JSON.parse(this.decodeToken); + if (!this.decodedTxt?.isAdmin) { + + console.log("No Name") + this.matchedItem = item.children?.filter(item => item.name === 'Add Product'); + console.log(this.matchedItem); + // this.isItemFound = !!match; + //return this.isItemFound; + } + }) + } + } diff --git a/src/app/model/auth.model.ts b/src/app/model/auth.model.ts new file mode 100644 index 000000000..2209c83dc --- /dev/null +++ b/src/app/model/auth.model.ts @@ -0,0 +1,10 @@ +export class UserRegister{ + username!:string; + password!:string; + email!:string; + image!:string; +} +export class UserLogin{ + email!:string; + password!:string; +} diff --git a/src/app/model/product.model.ts b/src/app/model/product.model.ts new file mode 100644 index 000000000..ad509f8b8 --- /dev/null +++ b/src/app/model/product.model.ts @@ -0,0 +1,8 @@ +export class Product{ + title!:string; + description!:string; + rating!:number; + price!:number; + image!:File; + category!:string; +} diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts new file mode 100644 index 000000000..1d22e117d --- /dev/null +++ b/src/app/services/auth.service.ts @@ -0,0 +1,84 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { UserLogin, UserRegister } from '../model/auth.model'; +import { Subject } from 'rxjs'; +import { JwtHelperService } from '@auth0/angular-jwt'; + +@Injectable({ + providedIn:'root' +}) +export class AuthService implements OnInit{ + decodedToken:any; + private authStatusListener = new Subject(); + constructor(public http:HttpClient,private router:Router){} + token:any; + ngOnInit(): void { + + } + + registerUser(username:string,password:string,email:string,image:File){ + const form = new FormData(); + form.append('username',username); + form.append('password',password); + form.append('email',email); + form.append('image',image); + + this.http.post<{message:string,userReg:UserRegister}> + ('http://localhost:8000/api/auth/register',form) + .subscribe(responseData=>{ + alert("Registration Successfully"); + this.router.navigateByUrl('/login') + },error=>{ + if(error.status === 400){ + this.authStatusListener.next(false); + console.log(error) + alert(error.error.message) + } + + }) + } + + login(email:string,password:string){ + const form = new FormData(); + form.append('email',email); + form.append('password',password); + + this.http.post<{ + token: any;message:string,userLogin:UserLogin + }> + ('http://localhost:8000/api/auth/login',form) + .subscribe(responseData=>{ + this.token = responseData.token; + localStorage.setItem('token', responseData.token); + const helper = new JwtHelperService(); + this.decodedToken= helper.decodeToken(this.token); + localStorage.setItem('userInfo', JSON.stringify(this.decodedToken)); + console.log(this.decodedToken); + alert("Login Successfully"); + this.router.navigateByUrl('/'); + },error=>{ + if(error.status === 400){ + this.authStatusListener.next(false); + console.log(error) + alert(error.error.message) + } + }) + } + + adminAuthentication(){ + this.http.get(``) + } + + getAuthenticationToken(){ + return localStorage.getItem('token'); + } + getDecodeToken(){ + return localStorage.getItem('userInfo'); + } + // Remove token + public removeToken(): void { + localStorage.removeItem('token'); + localStorage.removeItem('userInfo'); + } +} diff --git a/src/app/services/product.service.ts b/src/app/services/product.service.ts new file mode 100644 index 000000000..816ed620d --- /dev/null +++ b/src/app/services/product.service.ts @@ -0,0 +1,124 @@ +import { Injectable, signal } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Router } from '@angular/router'; +import { Product } from '../model/product.model'; +import { BehaviorSubject, map, retry } from 'rxjs'; +@Injectable({ + providedIn:'root' +}) + +export class ProductService { + TOKEN = localStorage.getItem('token'); + productInfo = signal([]); + form:any; + private searchTxt = new BehaviorSubject(''); + seacrhProduct = this.searchTxt.asObservable(); + constructor(private http:HttpClient,private route:Router){} + + // createProduct(title:string,description:string,rating:string,price:string,image:File,category:string){ + // const headers = new HttpHeaders().set('Authorization',`Bearer ${localStorage.getItem('token')}`); + // this.form = new FormData(); + // this.form.append('title',title); + // this.form.append('description',description); + // this.form.append('rating',rating); + // this.form.append('price',price), + // this.form.append('image',image) + // this.form.append('category',category); + + // this.http.post<{message:string,product:Product}> + // ('http://localhost:8000/api/products/create-product', + // {headers},this.form).subscribe((resp:any)=>{ + // console.log(this.form) + // alert(resp) + // },error=>{ + // alert(error.message) + // }) + + // } + + createProduct(title:string,description:string,rating:string,price:string,image:File,category:string){ + // const headers = new HttpHeaders().set('Authorization', `Bearer ${localStorage.getItem('token')}`); + this.form = new FormData(); + this.form.append('title',title); + this.form.append('description',description); + this.form.append('rating',rating); + this.form.append('price',price), + this.form.append('image',image) + this.form.append('category',category); + console.log(title) + console.log(description) + console.log(rating) + console.log(price) + console.log(image) + console.log(category) + this.http.post<{message:string,product:Product}> + (`http://localhost:8000/api/products/create-product`, + this.form).subscribe((responseData:any)=>{ + console.log(responseData) + alert("Add Product Successfully") + this.getAllProducts(); + this.route.navigateByUrl('/'); + },error=>{ + alert(error.message) + }) + } + + getAllProducts(){ + return this.http.get('http://localhost:8000/api/products/get-products') + } + + deleteProductById(id:any){ + // const headers = new HttpHeaders().set('Authorization', `Bearer ${localStorage.getItem('token')}`); + return this.http.delete(`http://localhost:8000/api/products/delete-product/${id}`,) + + } + + updateProductById(id:any,title:string,description:string,rating:string,price:string,image:File,category:string){ + // const headers = new HttpHeaders().set('Authorization', `Bearer ${localStorage.getItem('token')}`); + this.form = new FormData(); + this.form.append('title',title); + this.form.append('description',description); + this.form.append('rating',rating); + this.form.append('price',price), + this.form.append('image',image) + this.form.append('category',category); + console.log(this.form) + this.http.put(`http://localhost:8000/api/products/update-product/${id}`,this.form) + .subscribe((response:any)=>{ + //console.log + alert("Product Updated Successfully"); + //this.getAllProducts(); + this.route.navigateByUrl('/'); + },error=>{ + alert("Product Updated Failure"); + }) + + } + + getProductById(id:string){ + return this.http.get(`http://localhost:8000/api/products/get-product/${id}`) + } + + getSearchProduct(searchNewTxt:any){ + this.searchTxt.next(searchNewTxt) + } + + getAddToCartProduct(product:any){ + this.form = new FormData(); + this.form.append('title',product?.title); + this.form.append('price',product?.price), + this.form.append('image',product?.image) + console.log(this.form) + return this.http.post(`http://localhost:8000/api/products/create-addToCart`,this.form) + } + + getRemoveAddToCart(id:any){ + return this.http.delete(`http://localhost:8000/api/products/deleteToCart/${id}`) + + } + + getAllAddToCartProduct(){ + return this.http.get(`http://localhost:8000/api/products/getToProductCart`) + } + +} diff --git a/src/app/views/base/breadcrumbs/breadcrumbs.component.html b/src/app/views/base/breadcrumbs/breadcrumbs.component.html index eeb12b547..e14aa3a9c 100644 --- a/src/app/views/base/breadcrumbs/breadcrumbs.component.html +++ b/src/app/views/base/breadcrumbs/breadcrumbs.component.html @@ -1,6 +1,6 @@ - + Angular Breadcrumbs diff --git a/src/app/views/base/cards/cards.component.html b/src/app/views/base/cards/cards.component.html index 1221b1686..35b61fc54 100644 --- a/src/app/views/base/cards/cards.component.html +++ b/src/app/views/base/cards/cards.component.html @@ -1,6 +1,6 @@ - + Angular Card diff --git a/src/app/views/base/carousels/carousels.component.html b/src/app/views/base/carousels/carousels.component.html index f90214ce1..1ef6be0ed 100644 --- a/src/app/views/base/carousels/carousels.component.html +++ b/src/app/views/base/carousels/carousels.component.html @@ -1,6 +1,6 @@ - + Angular Carousel Slide only diff --git a/src/app/views/base/collapses/collapses.component.html b/src/app/views/base/collapses/collapses.component.html index 8c42b1016..62257eabc 100644 --- a/src/app/views/base/collapses/collapses.component.html +++ b/src/app/views/base/collapses/collapses.component.html @@ -1,6 +1,6 @@ - + Angular Collapse diff --git a/src/app/views/base/list-groups/list-groups.component.html b/src/app/views/base/list-groups/list-groups.component.html index 5e86cac28..e740552f3 100644 --- a/src/app/views/base/list-groups/list-groups.component.html +++ b/src/app/views/base/list-groups/list-groups.component.html @@ -1,6 +1,6 @@ - + Angular List Group Basic example diff --git a/src/app/views/base/navs/navs.component.html b/src/app/views/base/navs/navs.component.html index a95e631cf..e76b54336 100644 --- a/src/app/views/base/navs/navs.component.html +++ b/src/app/views/base/navs/navs.component.html @@ -1,6 +1,6 @@ - + Angular Navs Base navs diff --git a/src/app/views/base/navs/navs.component.ts b/src/app/views/base/navs/navs.component.ts index b26b40279..1dd2f70d2 100644 --- a/src/app/views/base/navs/navs.component.ts +++ b/src/app/views/base/navs/navs.component.ts @@ -14,11 +14,11 @@ import { NavLinkDirective, RowComponent } from '@coreui/angular'; -import { DocsComponentsComponent, DocsExampleComponent } from '@docs-components/public-api'; +import { DocsExampleComponent } from '@docs-components/public-api'; @Component({ selector: 'app-navs', templateUrl: './navs.component.html', - imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, NavComponent, NavItemComponent, NavLinkDirective, RouterLink, DropdownComponent, DropdownToggleDirective, DropdownMenuDirective, DropdownItemDirective, DocsComponentsComponent] + imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, NavComponent, NavItemComponent, NavLinkDirective, RouterLink, DropdownComponent, DropdownToggleDirective, DropdownMenuDirective, DropdownItemDirective] }) export class NavsComponent {} diff --git a/src/app/views/base/paginations/paginations.component.html b/src/app/views/base/paginations/paginations.component.html index d734de2e6..c75249b47 100644 --- a/src/app/views/base/paginations/paginations.component.html +++ b/src/app/views/base/paginations/paginations.component.html @@ -1,6 +1,6 @@ - + Angular Pagination diff --git a/src/app/views/base/paginations/paginations.component.ts b/src/app/views/base/paginations/paginations.component.ts index 4036cb6da..2edc2b946 100644 --- a/src/app/views/base/paginations/paginations.component.ts +++ b/src/app/views/base/paginations/paginations.component.ts @@ -10,11 +10,11 @@ import { PaginationComponent, RowComponent } from '@coreui/angular'; -import { DocsComponentsComponent, DocsExampleComponent } from '@docs-components/public-api'; +import { DocsExampleComponent } from '@docs-components/public-api'; @Component({ selector: 'app-paginations', templateUrl: './paginations.component.html', - imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, PaginationComponent, PageItemComponent, PageLinkDirective, RouterLink, DocsComponentsComponent] + imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, PaginationComponent, PageItemComponent, PageLinkDirective, RouterLink] }) export class PaginationsComponent {} diff --git a/src/app/views/base/placeholders/placeholders.component.html b/src/app/views/base/placeholders/placeholders.component.html index 5ed4c4ef2..8356141a7 100644 --- a/src/app/views/base/placeholders/placeholders.component.html +++ b/src/app/views/base/placeholders/placeholders.component.html @@ -1,6 +1,6 @@ - + Angular Placeholder diff --git a/src/app/views/base/placeholders/placeholders.component.ts b/src/app/views/base/placeholders/placeholders.component.ts index 0349c9caf..adc712dc4 100644 --- a/src/app/views/base/placeholders/placeholders.component.ts +++ b/src/app/views/base/placeholders/placeholders.component.ts @@ -15,11 +15,11 @@ import { PlaceholderDirective, RowComponent } from '@coreui/angular'; -import { DocsComponentsComponent, DocsExampleComponent } from '@docs-components/public-api'; +import { DocsExampleComponent } from '@docs-components/public-api'; @Component({ selector: 'app-placeholders', templateUrl: './placeholders.component.html', - imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, CardImgDirective, CardTitleDirective, CardTextDirective, ButtonDirective, ColDirective, RouterLink, PlaceholderAnimationDirective, PlaceholderDirective, BgColorDirective, DocsComponentsComponent] + imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, CardImgDirective, CardTitleDirective, CardTextDirective, ButtonDirective, ColDirective, RouterLink, PlaceholderAnimationDirective, PlaceholderDirective, BgColorDirective] }) export class PlaceholdersComponent {} diff --git a/src/app/views/dashboard/dashboard.component.ts b/src/app/views/dashboard/dashboard.component.ts index 7c087fc1d..2fdd9b266 100644 --- a/src/app/views/dashboard/dashboard.component.ts +++ b/src/app/views/dashboard/dashboard.component.ts @@ -1,3 +1,4 @@ + import { Component, DestroyRef, DOCUMENT, effect, inject, OnInit, Renderer2, signal, WritableSignal } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { ChartOptions } from 'chart.js'; @@ -22,6 +23,9 @@ import { IconDirective } from '@coreui/icons-angular'; import { WidgetsBrandComponent } from '../widgets/widgets-brand/widgets-brand.component'; import { WidgetsDropdownComponent } from '../widgets/widgets-dropdown/widgets-dropdown.component'; import { DashboardChartsData, IChartProps } from './dashboard-charts-data'; +import { AuthService } from '../../services/auth.service'; +import { JwtHelperService } from '@auth0/angular-jwt'; + interface IUser { name: string; @@ -132,6 +136,9 @@ export class DashboardComponent implements OnInit { public mainChart: IChartProps = { type: 'line' }; public mainChartRef: WritableSignal = signal(undefined); + token:any; + constructor(public authService:AuthService){} + #mainChartRefEffect = effect(() => { if (this.mainChartRef()) { this.setChartStyles(); @@ -145,6 +152,11 @@ export class DashboardComponent implements OnInit { ngOnInit(): void { this.initCharts(); this.updateChartOnColorModeChange(); + // const token = this.authService.getAuthenticationToken(); + // console.log(token); + // const helper = new JwtHelperService(); + // const decoded= helper.decodeToken(token); + // console.log(decoded); } initCharts(): void { diff --git a/src/app/views/forms/form-controls/form-controls.component.html b/src/app/views/forms/form-controls/form-controls.component.html index 55cdfe980..fa3a9fb8c 100644 --- a/src/app/views/forms/form-controls/form-controls.component.html +++ b/src/app/views/forms/form-controls/form-controls.component.html @@ -1,239 +1,91 @@ - + - Angular Form Control +

    {{id ? "Edit Product" : "Add Product"}}

    - -
    + +
    - + + +
    + +
    +
    +
    - - -
    -
    -
    -
    -
    -
    - - - - Angular Form Control Sizing - - -

    - Set heights using sizing property like sizing="lg" and - sizing="sm". -

    - - -
    - -
    - -
    -
    -
    -
    - - - - Angular Form Control Disabled - - -

    - Add the disabled boolean attribute on an input to give it a grayed out - appearance and remove pointer events. -

    - - -
    - -
    -
    -
    -
    -
    - - - - Angular Form Control Readonly - - -

    - Add the readOnly boolean attribute on an input to prevent modification of - the input's value. Read-only inputs appear lighter (just like disabled inputs), - but retain the standard cursor. -

    - - - -
    -
    -
    - - - - Angular Form Control Readonly plain text - - -

    - If you want to have <input readonly> elements in your form styled - as plain text, use the plainText boolean property to remove the default - form field styling and preserve the correct margin and padding. -

    - - - - - - - - - - - - - - - -
    - - - Price + - - - - - - - - + + +
    + + +
    + +
    + + +
    + +
    + +
    +
    + + +
    + + @if (imagePreview !== '' && imagePreview && form.get('image').valid) { +
    + +
    + }@else if(updatedImagePreview){ +
    + +
    + } + +
    + +
    + +
    + +
    -
    -
    -
    -
    - - - - Angular Form Control File input - - - -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    - - - - Angular Form Control Color - - - - - - - - - - - -
    -
    - - {{ favoriteColor() }} - -
    -
    +
    +
    diff --git a/src/app/views/forms/form-controls/form-controls.component.ts b/src/app/views/forms/form-controls/form-controls.component.ts index f6b3256a6..8d7bb48e3 100644 --- a/src/app/views/forms/form-controls/form-controls.component.ts +++ b/src/app/views/forms/form-controls/form-controls.component.ts @@ -1,29 +1,96 @@ -import { Component, signal } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { Component, OnInit, signal } from '@angular/core'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { ButtonDirective, CardBodyComponent, CardComponent, CardHeaderComponent, ColComponent, - ColDirective, FormControlDirective, FormDirective, FormLabelDirective, - GutterDirective, RowComponent, RowDirective } from '@coreui/angular'; -import { DocsComponentsComponent, DocsExampleComponent } from '@docs-components/public-api'; +import { DocsExampleComponent } from '@docs-components/public-api'; +import { ProductService } from '../../../services/product.service'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-form-controls', templateUrl: './form-controls.component.html', styleUrls: ['./form-controls.component.scss'], - imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, ReactiveFormsModule, FormsModule, FormDirective, FormLabelDirective, FormControlDirective, ButtonDirective, RowDirective, GutterDirective, ColDirective, DocsComponentsComponent] + imports: [RowComponent, ColComponent, CardComponent, CardHeaderComponent, CardBodyComponent, DocsExampleComponent, ReactiveFormsModule, FormsModule, FormDirective, FormLabelDirective, FormControlDirective, ButtonDirective, RowDirective] }) -export class FormControlsComponent { - +export class FormControlsComponent implements OnInit{ + form:FormGroup | any; + imagePreview!: string | ArrayBuffer | any; + updatedImagePreview!: string | ArrayBuffer | any; + paramsObject:any; public favoriteColor = signal('#26ab3c'); + id:any; + title:any;description:any;price:any;rating:any;image:any;category:any; + constructor(private productService:ProductService,private route: ActivatedRoute){} + + ngOnInit(): void { + this.form = new FormGroup({ + title:new FormControl(null,{validators:[Validators.required]}), + description: new FormControl(null,{validators:[Validators.required]}), + rating:new FormControl(null,{validators:[Validators.required]}), + price:new FormControl(null,{validators:[Validators.required]}), + category:new FormControl(null,{validators:[Validators.required]}), + image:new FormControl(null,{validators:[Validators.required]}) + }); + this.route.queryParamMap.subscribe(params => { + this.paramsObject = params; + this.id = params.get('id') + if(params.get('id')){ + this.form.get('title')?.setValue(params.get('title')); + this.form.get('description')?.setValue(params.get('description')); + this.form.get('price')?.setValue(params.get('price')); + this.form.get('rating')?.setValue(params.get('rating')); + this.form.get('category')?.setValue(params.get('category')); + this.form.get('image')?.setValue(params.get('image')); + this.updatedImagePreview = params.get('image'); + console.log(this.updatedImagePreview) + }else{ + this.form.get('title')?.setValue(''); + this.form.get('description')?.setValue(''); + this.form.get('price')?.setValue(''); + this.form.get('rating')?.setValue(''); + this.form.get('category')?.setValue(''); + this.form.get('image')?.setValue(''); + this.updatedImagePreview = ''; + } + }) + } + + onImagePicked(event:any){ + const file = event.target.files[0]; + this.form.patchValue({image:file}); + this.form.get('image').updateValueAndValidity(); + const reader = new FileReader(); + reader.onload = () => { + this.imagePreview = reader.result; + } + reader.readAsDataURL(file) + } + + handleEditAddProduct(){ + // alert("***********" + this.id) + if(this.form.invalid){ + return + } + console.log(this.form.value.category); + if(!this.id){ + this.productService.createProduct(this.form.value.title, + this.form.value.description,this.form.value.rating, + this.form.value.price,this.form.value.image,this.form.value.category) + }else{ + this.productService.updateProductById(this.id,this.form.value.title, + this.form.value.description,this.form.value.rating, + this.form.value.price,this.form.value.image,this.form.value.category) + } + } } diff --git a/src/app/views/forms/routes.ts b/src/app/views/forms/routes.ts index e413478a1..3207ac196 100644 --- a/src/app/views/forms/routes.ts +++ b/src/app/views/forms/routes.ts @@ -1,4 +1,5 @@ import { Routes } from '@angular/router'; +import { AuthGuard } from '../../RouteGuard/AuthGuard'; export const routes: Routes = [ { @@ -6,6 +7,7 @@ export const routes: Routes = [ data: { title: 'Forms' }, + children: [ { path: '', @@ -16,7 +18,7 @@ export const routes: Routes = [ path: 'form-control', loadComponent: () => import('./form-controls/form-controls.component').then(m => m.FormControlsComponent), data: { - title: 'Form Control' + title: 'Add Product' } }, { diff --git a/src/app/views/pages/login/login.component.html b/src/app/views/pages/login/login.component.html index a1cf9b4d6..f8e7ece04 100644 --- a/src/app/views/pages/login/login.component.html +++ b/src/app/views/pages/login/login.component.html @@ -5,14 +5,17 @@ -
    +

    Login

    Sign In to your account

    - + @@ -22,17 +25,18 @@

    Login

    autoComplete="current-password" cFormControl placeholder="Password" + formControlName="password" type="password" />
    - - @@ -48,7 +52,7 @@

    Sign up

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    - diff --git a/src/app/views/pages/login/login.component.ts b/src/app/views/pages/login/login.component.ts index e9ab25b01..b992f9744 100644 --- a/src/app/views/pages/login/login.component.ts +++ b/src/app/views/pages/login/login.component.ts @@ -1,5 +1,6 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { IconDirective } from '@coreui/icons-angular'; +import { AuthService } from '../../../services/auth.service'; import { ButtonDirective, CardBodyComponent, @@ -13,10 +14,33 @@ import { InputGroupTextDirective, RowComponent } from '@coreui/angular'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; + @Component({ selector: 'app-login', templateUrl: './login.component.html', - imports: [ContainerComponent, RowComponent, ColComponent, CardGroupComponent, CardComponent, CardBodyComponent, FormDirective, InputGroupComponent, InputGroupTextDirective, IconDirective, FormControlDirective, ButtonDirective] + imports: [ContainerComponent,ReactiveFormsModule, RowComponent, ColComponent, CardGroupComponent, CardComponent, CardBodyComponent, FormDirective, InputGroupComponent, InputGroupTextDirective, IconDirective, FormControlDirective, ButtonDirective] }) -export class LoginComponent {} +export class LoginComponent implements OnInit{ + form:FormGroup | any; + constructor(private authService:AuthService,private router:Router){ + + } + ngOnInit(): void { + this.form = new FormGroup({ + email: new FormControl(null,{validators:[Validators.required]}), + password: new FormControl(null,{validators:[Validators.required]}), + }) + } + handleLogin(){ + if(this.form.invalid){ + return + } + this.authService.login(this.form.value.email,this.form.value.password); + } + navigateToRegister(){ + this.router.navigate(['/register']) + } +} diff --git a/src/app/views/pages/register/register.component.html b/src/app/views/pages/register/register.component.html index 68bffd177..725e59595 100644 --- a/src/app/views/pages/register/register.component.html +++ b/src/app/views/pages/register/register.component.html @@ -4,33 +4,78 @@ - +

    Register

    Create your account

    + + +
    +
    + + +
    + @if (!imagePreview) { + + } + @if (imagePreview !== '' && imagePreview && form.get('image').valid) { +
    + +
    + } + +
    + + - + @ - + + - + + - +
    - +
    diff --git a/src/app/views/pages/register/register.component.ts b/src/app/views/pages/register/register.component.ts index c19b54418..074ad78fc 100644 --- a/src/app/views/pages/register/register.component.ts +++ b/src/app/views/pages/register/register.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { IconDirective } from '@coreui/icons-angular'; import { ButtonDirective, @@ -12,10 +12,52 @@ import { InputGroupTextDirective, RowComponent } from '@coreui/angular'; +import { CommonModule } from '@angular/common'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { AuthService } from '../../../services/auth.service'; + + @Component({ selector: 'app-register', templateUrl: './register.component.html', - imports: [ContainerComponent, RowComponent, ColComponent, CardComponent, CardBodyComponent, FormDirective, InputGroupComponent, InputGroupTextDirective, IconDirective, FormControlDirective, ButtonDirective] + imports: [CommonModule,ContainerComponent,RowComponent, + ColComponent, CardComponent, CardBodyComponent, + FormDirective, InputGroupComponent, InputGroupTextDirective, + IconDirective, FormControlDirective, ButtonDirective,ReactiveFormsModule] }) -export class RegisterComponent {} +export class RegisterComponent implements OnInit{ + form:FormGroup | any; + imagePreview!: string | ArrayBuffer | any; + + constructor(private authService:AuthService){} + ngOnInit(): void { + this.form = new FormGroup({ + username: new FormControl(null,{validators:[Validators.required]}), + email: new FormControl(null,{validators:[Validators.required]}), + password: new FormControl(null,{validators:[Validators.required]}), + image: new FormControl(null,{validators:[Validators.required]}) + }); + } + + + onImagePicked(event:any){ + const file = event.target.files[0]; + this.form.patchValue({image:file}); + this.form.get('image').updateValueAndValidity(); + const reader = new FileReader(); + reader.onload = () => { + this.imagePreview = reader.result; + } + reader.readAsDataURL(file) + } + + handleRegistration(){ + if(this.form.invalid){ + return + } + this.authService.registerUser(this.form.value.username, + this.form.value.password,this.form.value.email,this.form.value.image + ) + } +} diff --git a/src/app/views/theme/colors.component.html b/src/app/views/theme/colors.component.html index 159b16bc2..171fe78f6 100644 --- a/src/app/views/theme/colors.component.html +++ b/src/app/views/theme/colors.component.html @@ -1,33 +1,21 @@ - Theme colors + Product Detail - -
    Brand Primary Color
    -
    - -
    Brand Secondary Color
    -
    - -
    Brand Success Color
    -
    - -
    Brand Danger Color
    -
    - -
    Brand Warning Color
    -
    - -
    Brand Info Color
    -
    - -
    Brand Light Color
    -
    - -
    Brand Dark Color
    -
    -
    +
    + @if(title === null || description === null || price === null || rating === null || category === null){ +

    No Product Detail Found

    + }@else { + +
    Product Title: {{title}}
    +
    Product Description: {{description}}
    +
    Product Price: {{price}}
    +
    Product Rating: {{rating}}
    +
    Product Category: {{category}}
    + } +
    +
    diff --git a/src/app/views/theme/colors.component.ts b/src/app/views/theme/colors.component.ts index c3a389235..3ed52d793 100644 --- a/src/app/views/theme/colors.component.ts +++ b/src/app/views/theme/colors.component.ts @@ -2,6 +2,7 @@ import { AfterViewInit, Component, computed, DOCUMENT, forwardRef, inject, input import { getStyle, rgbToHex } from '@coreui/utils'; import { CardBodyComponent, CardComponent, CardHeaderComponent, ColComponent, RowComponent } from '@coreui/angular'; +import { ActivatedRoute } from '@angular/router'; @Component({ templateUrl: 'colors.component.html', @@ -10,7 +11,8 @@ import { CardBodyComponent, CardComponent, CardHeaderComponent, ColComponent, Ro export class ColorsComponent implements OnInit, AfterViewInit { private document = inject(DOCUMENT); private renderer = inject(Renderer2); - + title:any;description:any;price:any;rating:any;image:any;category:any; + constructor(private route: ActivatedRoute) {} public themeColors(): void { Array.from(this.document.querySelectorAll('.theme-color')).forEach( (element: Element) => { @@ -36,7 +38,17 @@ export class ColorsComponent implements OnInit, AfterViewInit { ); } - ngOnInit(): void {} + ngOnInit(): void { + this.route.queryParamMap.subscribe(params => { + const id = params.get('id'); + this.image = params.get('image'); + this.title = params.get('title'); + this.description = params.get('description') + this.price = params.get('price'); + this.rating = params.get('rating'); + this.category = params.get('category'); + }); + } ngAfterViewInit(): void { this.themeColors(); diff --git a/src/app/views/theme/routes.ts b/src/app/views/theme/routes.ts index 44f2aa98b..c92173a6c 100644 --- a/src/app/views/theme/routes.ts +++ b/src/app/views/theme/routes.ts @@ -23,7 +23,7 @@ export const routes: Routes = [ path: 'typography', loadComponent: () => import('./typography.component').then(m => m.TypographyComponent), data: { - title: 'Typography' + title: 'AddToCart' } } ] diff --git a/src/app/views/theme/typography.component.html b/src/app/views/theme/typography.component.html index 2bea088f5..eac54b286 100644 --- a/src/app/views/theme/typography.component.html +++ b/src/app/views/theme/typography.component.html @@ -1,166 +1,36 @@ +@if(isLoading){ +
    +
    +
    +} - Headings + Shopping Cart -

    Documentation and examples for Bootstrap typography, including global settings, headings, body text, lists, and - more.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    HeadingExample
    -

    <h1></h1>

    -

    h1. Bootstrap heading

    -

    <h2></h2>

    -

    h2. Bootstrap heading

    -

    <h3></h3>

    -

    h3. Bootstrap heading

    -

    <h4></h4>

    -

    h4. Bootstrap heading

    -

    <h5></h5>

    -
    h5. Bootstrap heading
    -

    <h6></h6>

    -
    h6. Bootstrap heading
    -
    -
    - - - Headings - - -

    .h1 through .h6 classes are - also available, for when you want to match the font styling of a heading but cannot use the associated HTML - element.

    -
    -

    h1. Bootstrap heading

    -

    h2. Bootstrap heading

    -

    h3. Bootstrap heading

    -

    h4. Bootstrap heading

    -

    h5. Bootstrap heading

    -

    h6. Bootstrap heading

    -
    -
    -
    - - - Display headings - - -

    Traditional heading elements are designed to work best in the meat of your page content. When you need a heading - to stand out, consider using a display heading—a larger, slightly more opinionated heading style. -

    -
    - - - - - - - - - - - - - - - -
    Display 1
    Display 2
    Display 3
    Display 4
    -
    -
    -
    - - - Inline text elements - - -

    Traditional heading elements are designed to work best in the meat of your page content. When you need a heading - to stand out, consider using a display heading—a larger, slightly more opinionated heading style. -

    -
    -

    You can use the mark tag to - highlight - text. -

    -

    - This line of text is meant to be treated as deleted text. -

    -

    This line of text is meant to be treated as no longer accurate.

    -

    - This line of text is meant to be treated as an addition to the document. -

    -

    This line of text will render as underlined

    -

    This line of text is meant to be treated as fine print.

    -

    This line rendered as bold text.

    -

    This line rendered as italicized text.

    -
    -
    -
    - - - Description list alignment - - -

    Align terms and descriptions horizontally by using our grid system’s predefined classes (or semantic mixins). For - longer terms, you can optionally add a .text-truncate class to truncate - the text with an ellipsis.

    -
    -
    -
    Description lists
    -
    A description list is perfect for defining terms.
    - -
    Euismod
    -
    -

    Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.

    -

    Donec id elit non mi porta gravida at eget metus.

    -
    - -
    Malesuada porta
    -
    Etiam porta sem malesuada magna mollis euismod.
    - -
    Truncated term is truncated with d-block
    -
    Fusce dapibus, tellus ac cursus commodo, tortor mauris - condimentum nibh, ut fermentum massa justo sit amet risus. -
    - -
    Nesting
    -
    -
    -
    Nested definition list
    -
    Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc.
    -
    -
    -
    -
    + + @for(product of products; track product._id){ + @if(product){ +
    + +
    +

    Title: {{product.title}}

    +

    Price : {{product.price}}

    +
    + +
    + }@else{ +

    No Product Found!!

    + } + }
    + @if(products && totalPrice){ +
    +
    Subtotal ({{products?.length}} items) + {{"\u20B9"}} {{totalPrice}}
    + +
    + }@else{ + + }
    diff --git a/src/app/views/theme/typography.component.ts b/src/app/views/theme/typography.component.ts index 5644b2c55..91eef2b19 100644 --- a/src/app/views/theme/typography.component.ts +++ b/src/app/views/theme/typography.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { CardBodyComponent, CardComponent, CardHeaderComponent } from '@coreui/angular'; +import { ProductService } from '../../services/product.service'; @Component({ templateUrl: 'typography.component.html', @@ -9,4 +10,42 @@ import { CardBodyComponent, CardComponent, CardHeaderComponent } from '@coreui/a CardBodyComponent ] }) -export class TypographyComponent {} +export class TypographyComponent{ + products:any; + totalPrice: number = 0; + prod:any; + isLoading = false; + constructor(private productService:ProductService){ + + } + ngOnInit() { + this.isLoading = true; + this.productService.getAllAddToCartProduct() + .subscribe((response:any)=>{ + this.products = response.products; + this.calculateTotal(this.products) + this.isLoading = false; + },error=>{ + console.log(error) + this.isLoading = false; + }); + + } + handleRemoveProduct(id:any){ + this.isLoading = true; + this.productService.getRemoveAddToCart(id).subscribe((response:any)=>{ + alert(response.message) + this.isLoading = false; + this.productService.getAllAddToCartProduct(); + },error=>{ + alert(error) + this.isLoading = false; + }) + } + calculateTotal(products:any): void { + console.log(products) + this.totalPrice = products.reduce((acc:any, item:any) => { + return acc + (item.price); + }, 0); + } +} diff --git a/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.html b/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.html index 2f1062523..e8f408fc4 100644 --- a/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.html +++ b/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.html @@ -1,123 +1,39 @@ +@if(isLoading){ +
    +
    +
    +} - - - - 26K - - (-12.4% ) - - - - - - - - - - - - - - - - - $6.200 - - (40.9%) - - - - - - - - - - - - - - - - - 2.49 - - (84.7% ) - - - - - - - - - - - - - - - - - 44K - - (-23.6% ) - - - - - - - - - - - - + @if(searchProductTxt){ +

    You want to search : {{searchProductTxt}}

    + } +@for (product of resObject; track product._id) { + + + @if(searchProductTxt === '' || + product.category.toLowerCase().includes(searchProductTxt) || + product.title.toLowerCase().includes(searchProductTxt)){ +
    + +

    Title : {{product.title}}

    +

    Price : {{product.price}}

    +

    Rating: {{product.rating}}

    + + @if(decodedTxt?.isAdmin){ +
    + + + +
    + }@else{ + + } +
    + } + +
    + } @empty { +

    No Product found.

    + }
    diff --git a/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.ts b/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.ts index dee287481..eb1f81764 100644 --- a/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.ts +++ b/src/app/views/widgets/widgets-dropdown/widgets-dropdown.component.ts @@ -1,7 +1,7 @@ import { AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, inject, OnInit, viewChild } from '@angular/core'; import { getStyle } from '@coreui/utils'; import { ChartjsComponent } from '@coreui/angular-chartjs'; -import { RouterLink } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { IconDirective } from '@coreui/icons-angular'; import { ButtonDirective, @@ -15,6 +15,8 @@ import { TemplateIdDirective, WidgetStatAComponent } from '@coreui/angular'; +import { ProductService } from '../../../services/product.service'; +import { AuthService } from '../../../services/auth.service'; @Component({ selector: 'app-widgets-dropdown', @@ -22,10 +24,15 @@ import { imports: [RowComponent, ColComponent, WidgetStatAComponent, TemplateIdDirective, IconDirective, DropdownComponent, ButtonDirective, DropdownToggleDirective, DropdownMenuDirective, DropdownItemDirective, RouterLink, DropdownDividerDirective, ChartjsComponent] }) export class WidgetsDropdownComponent implements OnInit, AfterContentInit { - private changeDetectorRef = inject(ChangeDetectorRef); + private changeDetectorRef = inject(ChangeDetectorRef); + resObject:any; data: any[] = []; options: any[] = []; + searchProductTxt = ''; + decodeToken:any; + decodedTxt:any; + isLoading = false; labels = [ 'January', 'February', @@ -120,10 +127,106 @@ export class WidgetsDropdownComponent implements OnInit, AfterContentInit { } }; + constructor(public authService:AuthService, + public productService:ProductService,private router:Router){} ngOnInit(): void { this.setData(); + this.getAllProductsData(); + this.decodeToken = this.authService.getDecodeToken(); + this.decodedTxt = JSON.parse(this.decodeToken); + //console.log(this.decodedTxt.isAdmin) + this.productService.seacrhProduct.subscribe(searchProductTxt => this.searchProductTxt = searchProductTxt) + } + + getAllProductsData(){ + this.isLoading = true; + this.productService.getAllProducts() + .subscribe((products:any)=>{ + if (products.hasOwnProperty('products')) { + this.resObject = products['products']; + console.log(this.resObject) + this.isLoading = false; + } + },error=>{ + alert(error) + this.isLoading = false; + }) + } + //delete product + handleDelete(id:any){ + this.isLoading = true; + this.productService.deleteProductById(id) + .subscribe((response:any)=>{ + try{ + console.log(response) + alert("Product Deleted Successfully"); + this.getAllProductsData(); + this.isLoading = false; + }catch(error){ + alert(error) + this.isLoading = false + } + }) } + handleEdit(id:any){ + this.isLoading = true; + this.productService.getProductById(id) + .subscribe((responseData:any)=>{ + console.log(responseData) + this.isLoading = false; + this.router.navigate(['/forms/form-control'],{ + queryParams: { id:responseData._id, + title: responseData.title, + description:responseData.description, + price:responseData.price, + rating:responseData.rating, + image:responseData.image, + category:responseData.category + } + }) + },error=>{ + alert(error) + this.isLoading = false; + }) + + } + + //getproductbyID + handleShow(id:any){ + this.isLoading = true; + this.productService.getProductById(id) + .subscribe((responseData:any)=>{ + console.log(responseData); + this.isLoading = false; + this.router.navigate(['/theme/colors'],{ + queryParams: { title: responseData.title, + description:responseData.description, + price:responseData.price, + rating:responseData.rating, + image:responseData.image, + category:responseData.category + } + }) + + },error=>{ + alert(error) + this.isLoading = false; + }) + } + + handleAddToCart(product:any) { + this.isLoading = true; + this.productService.getAddToCartProduct(product) + .subscribe(response=>{ + alert("Product Added Successfully") + this.router.navigateByUrl('/theme/typography'); + this.isLoading = false; + },error=>{ + alert(error) + this.isLoading = false; + }) + } ngAfterContentInit(): void { this.changeDetectorRef.detectChanges(); diff --git a/src/assets/brand/brandlogo.jpg b/src/assets/brand/brandlogo.jpg new file mode 100644 index 000000000..539bd008f Binary files /dev/null and b/src/assets/brand/brandlogo.jpg differ diff --git a/src/assets/images/avatars/noProduct.png b/src/assets/images/avatars/noProduct.png new file mode 100644 index 000000000..8c6de308d Binary files /dev/null and b/src/assets/images/avatars/noProduct.png differ diff --git a/src/assets/images/avatars/teacher.png b/src/assets/images/avatars/teacher.png new file mode 100644 index 000000000..187088282 Binary files /dev/null and b/src/assets/images/avatars/teacher.png differ diff --git a/src/assets/images/avatars/user.png b/src/assets/images/avatars/user.png new file mode 100644 index 000000000..a3fb639a7 Binary files /dev/null and b/src/assets/images/avatars/user.png differ diff --git a/src/index.html b/src/index.html index b80d2944e..63c610d46 100644 --- a/src/index.html +++ b/src/index.html @@ -18,15 +18,15 @@ name="keyword" /> - CoreUI Free Angular Admin Template + E-commerce -
    + diff --git a/src/scss/styles.scss b/src/scss/styles.scss index 04f4cc5ac..e04079e12 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -21,3 +21,33 @@ // We use those styles to show code examples, you should remove them in your application. @use "examples"; +/* The overlay container that covers the whole screen */ +.spinner-overlay { + position: fixed; /* Positions the overlay relative to the viewport */ + top: 0; + left: 0; + width: 100%; + height: 100vh; /* Viewport height ensures it covers the entire screen */ + display: flex; /* Use Flexbox for easy centering */ + justify-content: center; /* Centers horizontally */ + align-items: center; /* Centers vertically */ + background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black background */ + z-index: 9999; /* Ensures it's above all other content */ +} + +/* The spinner itself */ +.loader { + border: 5px solid transparent; /* Transparent border for most of the circle */ + border-top: 5px solid #ffffff; /* Colored border for the spinning part */ + border-radius: 50%; /* Makes it a circle */ + width: 50px; + height: 50px; + animation: spin 1s linear infinite; /* Animation to make it spin */ + /* The loader itself has no background by default, so it's transparent */ +} + +/* Keyframes for the spin animation */ +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index c47455c0c..5b2031c9d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "esModuleInterop": true, - "strict": true, + "strict": false, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, @@ -21,7 +21,8 @@ "importHelpers": true, "target": "ES2022", "module": "preserve", - "useDefineForClassFields": false + "useDefineForClassFields": false, + "strictPropertyInitialization": false, }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false,