From 92953b884ece2638e116dd9f04d880381e2bd925 Mon Sep 17 00:00:00 2001 From: shabeelashraf5 <05shebz@gmail.com> Date: Sun, 19 Jan 2025 22:38:46 +0530 Subject: [PATCH] Modified Quotation and Purchase section --- client/package-lock.json | 28 ++--- client/package.json | 2 +- client/src/app/app-routing.module.ts | 105 +++++++++++------- client/src/app/app.module.ts | 12 +- client/src/app/model/ad-product.model.ts | 2 +- .../ad-product/ad-product.component.ts | 22 ++-- .../product/ad-product/ad-product.service.ts | 12 +- .../ad-product/store/ad-product.action.ts | 10 +- .../ad-product/store/ad-product.effects.ts | 10 +- .../ad-product/store/ad-product.state.ts | 4 +- .../em-dashboard/em-dashboard.component.ts | 13 ++- .../add-quotation.component.html | 2 +- .../add-quotation/add-quotation.component.ts | 7 +- .../add-quotation/add-quotation.service.ts | 7 +- .../shipment-history.component.ts | 2 +- .../inventory-list.component.ts | 10 +- .../inventory-list/inventory-list.service.ts | 6 +- .../customer/customer.resolver.spec.ts | 17 +++ .../app/service/customer/customer.resolver.ts | 5 + .../dashboard-resolver.resolver.spec.ts | 17 +++ .../dashboard/dashboard-resolver.resolver.ts | 11 ++ .../service/profile/profile.resolver.spec.ts | 17 +++ .../app/service/profile/profile.resolver.ts | 10 ++ .../app/store/action/inventory-list.action.ts | 4 +- .../store/effects/inventory-list.effects.ts | 4 +- .../app/store/state/inventory-list.state.ts | 4 +- client/src/custom-theme.scss | 5 +- server/controller/salesController.js | 17 ++- server/routes/sales.js | 2 + 29 files changed, 262 insertions(+), 105 deletions(-) create mode 100644 client/src/app/service/customer/customer.resolver.spec.ts create mode 100644 client/src/app/service/customer/customer.resolver.ts create mode 100644 client/src/app/service/dashboard/dashboard-resolver.resolver.spec.ts create mode 100644 client/src/app/service/dashboard/dashboard-resolver.resolver.ts create mode 100644 client/src/app/service/profile/profile.resolver.spec.ts create mode 100644 client/src/app/service/profile/profile.resolver.ts diff --git a/client/package-lock.json b/client/package-lock.json index 506f8f9..3b64fdf 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@amcharts/amcharts4": "^4.10.38", - "@angular/animations": "^17.1.0", + "@angular/animations": "^17.3.8", "@angular/cdk": "^17.1.2", "@angular/common": "^17.1.0", "@angular/compiler": "^17.1.0", @@ -415,9 +415,9 @@ } }, "node_modules/@angular/cdk": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.3.8.tgz", - "integrity": "sha512-9UQovtq1R3iGppBP6c1xgnokhG3LaUObpm6htMyuQ2v034WinemoeMdHbqs/OvyUbqOUttQI/9vz37TVB0DjXA==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.1.2.tgz", + "integrity": "sha512-eu9D60RQv213qi7oh6ae9Z+d6+AG/aqi0y70Ag9BjwqTiatDiYvSySxswxYYKdzPp0hx0ZUTGi16LqtT6pyj6Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -604,9 +604,9 @@ } }, "node_modules/@angular/material": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.3.8.tgz", - "integrity": "sha512-P15p3ixO119DvqtFPCUc+9uKlFgwrwoZtKstcdx/knFlw9c+wS5s9SZzTbB2yqjZoBZ4gC92kqbUQI2o7AUbUQ==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.1.2.tgz", + "integrity": "sha512-50n7JDWtWGCxfrMKVKZ2wqkdozukA3IWeypQgXxzZc+4jqgT6Vj8/U4xNvcO9OgPLMOaTvktfT+wzUmCKJ0sng==", "dependencies": { "@material/animation": "15.0.0-canary.7f224ddd4.0", "@material/auto-init": "15.0.0-canary.7f224ddd4.0", @@ -659,7 +659,7 @@ }, "peerDependencies": { "@angular/animations": "^17.0.0 || ^18.0.0", - "@angular/cdk": "17.3.8", + "@angular/cdk": "17.1.2", "@angular/common": "^17.0.0 || ^18.0.0", "@angular/core": "^17.0.0 || ^18.0.0", "@angular/forms": "^17.0.0 || ^18.0.0", @@ -15437,9 +15437,9 @@ } }, "@angular/cdk": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.3.8.tgz", - "integrity": "sha512-9UQovtq1R3iGppBP6c1xgnokhG3LaUObpm6htMyuQ2v034WinemoeMdHbqs/OvyUbqOUttQI/9vz37TVB0DjXA==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.1.2.tgz", + "integrity": "sha512-eu9D60RQv213qi7oh6ae9Z+d6+AG/aqi0y70Ag9BjwqTiatDiYvSySxswxYYKdzPp0hx0ZUTGi16LqtT6pyj6Q==", "requires": { "parse5": "^7.1.2", "tslib": "^2.3.0" @@ -15559,9 +15559,9 @@ } }, "@angular/material": { - "version": "17.3.8", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.3.8.tgz", - "integrity": "sha512-P15p3ixO119DvqtFPCUc+9uKlFgwrwoZtKstcdx/knFlw9c+wS5s9SZzTbB2yqjZoBZ4gC92kqbUQI2o7AUbUQ==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-17.1.2.tgz", + "integrity": "sha512-50n7JDWtWGCxfrMKVKZ2wqkdozukA3IWeypQgXxzZc+4jqgT6Vj8/U4xNvcO9OgPLMOaTvktfT+wzUmCKJ0sng==", "requires": { "@material/animation": "15.0.0-canary.7f224ddd4.0", "@material/auto-init": "15.0.0-canary.7f224ddd4.0", diff --git a/client/package.json b/client/package.json index 38bfe62..1efefdf 100644 --- a/client/package.json +++ b/client/package.json @@ -11,7 +11,7 @@ "private": true, "dependencies": { "@amcharts/amcharts4": "^4.10.38", - "@angular/animations": "^17.1.0", + "@angular/animations": "^17.3.8", "@angular/cdk": "^17.1.2", "@angular/common": "^17.1.0", "@angular/compiler": "^17.1.0", diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 204dd15..4069a05 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -41,10 +41,8 @@ import { SuccessPaymentComponent } from './portal/employee/accounting/success-pa import { NotFoundComponent } from './layouts/not-found/not-found.component'; import { ExcelComponent } from './layouts/excel/excel.component'; import { AdminRouterComponent } from './portal/admin/admin-router.component'; - - - - +import { dashboardResolverResolver } from './service/dashboard/dashboard-resolver.resolver'; +import { profileResolver } from './service/profile/profile.resolver'; const routes: Routes = [ @@ -61,7 +59,7 @@ const routes: Routes = [ children: [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, {path: 'portal' , loadChildren: () => import('./portal/admin/admin-portal/admin-portal.module').then( m => m.AdminPortalModule) }, - {path: 'dashboard' , loadChildren: () => import('./portal/admin/dashboard/ad-dashboard/ad-dashboard.module').then( m => m.AdDashboardModule)}, + {path: 'dashboard' , loadChildren: () => import('./portal/admin/dashboard/ad-dashboard/ad-dashboard.module').then( m => m.AdDashboardModule) }, {path: 'employee' , loadChildren: () => import('./portal/admin/employee/ad-employee/ad-employee.module').then( m => m.AdEmployeeModule) }, {path: 'product' , loadChildren: () => import('./portal/admin/product/ad-product/ad-product.module').then( m => m.AdProductModule) }, {path: 'category' , loadChildren: () => import('./portal/admin/category/ad-category/ad-category.module').then( m => m.AdCategoryModule) }, @@ -71,62 +69,93 @@ const routes: Routes = [ ] }, + { + path: 'portal', + children: [ + + { path: 'dashboard' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule), resolve: {profileData: profileResolver}}, - { path: 'portal/dashboard' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, + { + path: 'sales', + children: [ - { path: 'portal/sales' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, - { path: 'portal/sales/customer' , loadChildren: () => import('./portal/employee/sales/customer/customer.module').then( m => m.CustomerModule) }, - { path: 'portal/sales/quotations' , loadChildren: () => import('./portal/employee/sales/quotation/quotation.module').then( m => m.QuotationModule) }, - { path: 'portal/sales/quotations/:id' , loadChildren: () => import('./portal/employee/sales/add-quotation/add-quotation.module').then( m => m.AddQuotationModule) }, - { path: 'portal/sales/sales-order' , loadChildren: () => import('./portal/employee/sales/sales-order/sales-order.module').then( m => m.SalesOrderModule) }, - { path: 'portal/sales/sales-order/:id' , loadChildren: () => import('./portal/employee/sales/client-po/client-po.module').then( m => m.ClientPoModule) }, - { path: 'portal/sales/sales-analysis' , loadChildren: () => import('./portal/employee/sales/sales-analysis/sales-analysis.module').then( m => m.SalesAnalysisModule) }, + // { path: 'sales' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, + { path: 'customer' , loadChildren: () => import('./portal/employee/sales/customer/customer.module').then( m => m.CustomerModule) }, + { path: 'quotations' , loadChildren: () => import('./portal/employee/sales/quotation/quotation.module').then( m => m.QuotationModule) }, + { path: 'quotations/:id' , loadChildren: () => import('./portal/employee/sales/add-quotation/add-quotation.module').then( m => m.AddQuotationModule) }, + { path: 'sales-order' , loadChildren: () => import('./portal/employee/sales/sales-order/sales-order.module').then( m => m.SalesOrderModule) }, + { path: 'sales-order/:id' , loadChildren: () => import('./portal/employee/sales/client-po/client-po.module').then( m => m.ClientPoModule) }, + { path: 'sales-analysis' , loadChildren: () => import('./portal/employee/sales/sales-analysis/sales-analysis.module').then( m => m.SalesAnalysisModule) }, + ] + }, + + // { path: 'purchase' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, + + { + path: 'purchase', + children: [ - { path: 'portal/purchase' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, + { path: 'supplier' , loadChildren: () => import('./portal/employee/purchase/supplier/supplier.module').then( m => m.SupplierModule) }, + { path: 'purchase-order' , loadChildren: () => import('./portal/employee/purchase/purchase-order/purchase-order.module').then( m => m.PurchaseOrderModule) }, - { path: 'portal/purchase/supplier' , loadChildren: () => import('./portal/employee/purchase/supplier/supplier.module').then( m => m.SupplierModule) }, - { path: 'portal/purchase/purchase-order' , loadChildren: () => import('./portal/employee/purchase/purchase-order/purchase-order.module').then( m => m.PurchaseOrderModule) }, - //{ path: 'portal/purchase/vendor-evaluation' , component: EvaluationComponent, canActivate: [CanEmployeeLogged] }, - { path: 'portal/purchase/purchase-order/:id' , loadChildren: () => import('./portal/employee/purchase/add-po/add-po.module').then( m => m.AddPoModule) }, - { path: 'portal/purchase/purchase-history' , loadChildren: () => import('./portal/employee/purchase/purchase-history/purchase-history.module').then( m => m.PurchaseHistoryModule) }, + { path: 'purchase-order/:id' , loadChildren: () => import('./portal/employee/purchase/add-po/add-po.module').then( m => m.AddPoModule) }, + { path: 'purchase-history' , loadChildren: () => import('./portal/employee/purchase/purchase-history/purchase-history.module').then( m => m.PurchaseHistoryModule) }, + ] + }, + + { + path: 'warehouse', + children: [ + // { path: 'warehouse' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, + { path: 'inventory-list' , loadChildren: () => import('./portal/employee/warehouse/inventory-list/inventory-list.module').then( m => m.InventoryListModule) }, - { path: 'portal/warehouse' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, - { path: 'portal/warehouse/inventory-list' , loadChildren: () => import('./portal/employee/warehouse/inventory-list/inventory-list.module').then( m => m.InventoryListModule) }, + ] + }, + { + path: 'shipment', + children: [ - { path: 'portal/shipment' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, - { path: 'portal/shipment/shipment-history' , loadChildren: () => import('./portal/employee/shipment/shipment-history/shipment-history.module').then( m => m.ShipmentHistoryModule) }, + // { path: 'shipment' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, + { path: 'shipment-history' , loadChildren: () => import('./portal/employee/shipment/shipment-history/shipment-history.module').then( m => m.ShipmentHistoryModule) }, + ] + }, - { path: 'portal/accounting' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, - { path: 'portal/accounting/invoicing' , loadChildren: () => import('./portal/employee/accounting/invoicing/invoicing.module').then( m => m.InvoicingModule)}, - { path: 'portal/accounting/invoicing/:id' , loadChildren: () => import('./portal/employee/accounting/invoice-details/invoice-details.module').then( m => m.InvoiceDetailsModule) }, - { path: 'portal/accounting/financial-transaction' , loadChildren: () => import('./portal/employee/accounting/financial-transaction/financial-transaction.module').then( m => m.FinancialTransactionModule) }, - { path: 'portal/accounting/financial-reports' , component: FinancialReportComponent, canActivate: [CanEmployeeLogged] }, - { path: 'portal/accounting/financial-transaction/:id' , loadChildren: () => import('./portal/employee/accounting/payment/payment.module').then( m => m.PaymentModule) }, + { + path: 'accounting', + children: [ + + // { path: 'accounting' , loadChildren: () => import('./portal/employee/dashboard/em-dashboard/em-dashboard.module').then(m => m.EmDashboardModule) }, + { path: 'invoicing' , loadChildren: () => import('./portal/employee/accounting/invoicing/invoicing.module').then( m => m.InvoicingModule)}, + { path: 'invoicing/:id' , loadChildren: () => import('./portal/employee/accounting/invoice-details/invoice-details.module').then( m => m.InvoiceDetailsModule) }, + { path: 'financial-transaction' , loadChildren: () => import('./portal/employee/accounting/financial-transaction/financial-transaction.module').then( m => m.FinancialTransactionModule) }, + { path: 'financial-reports' , component: FinancialReportComponent, canActivate: [CanEmployeeLogged] }, + { path: 'financial-transaction/:id' , loadChildren: () => import('./portal/employee/accounting/payment/payment.module').then( m => m.PaymentModule) }, + ] + + }, //{ path: 'success' , component: SuccessComponent, canActivate: [CanEmployeeLogged] }, - { path: 'portal/profile' , loadChildren: () => import('./portal/employee/profile/profile/em-profile.module').then( m => m.EmProfileModule) }, + { path: 'profile' , loadChildren: () => import('./portal/employee/profile/profile/em-profile.module').then( m => m.EmProfileModule) }, - { path: 'reset-password' , loadChildren: () => import('./portal/employee/reset-password/reset-password.module').then( m => m.ResetPasswordModule) }, - { path: 'portal/video-conference' , loadChildren: () => import('./layouts/video-chat/video-chat.module').then( m => m.VideoChatModule) }, + { path: 'video-conference' , loadChildren: () => import('./layouts/video-chat/video-chat.module').then( m => m.VideoChatModule) }, - { path: 'portal/messenger', loadChildren: () => import('./layouts/message/message.module').then( m => m.MessageModule) }, + { path: 'messenger', loadChildren: () => import('./layouts/message/message.module').then( m => m.MessageModule) }, - { path: 'portal/success' , component: SuccessPaymentComponent, canActivate: [CanEmployeeLogged] }, + { path: 'success' , component: SuccessPaymentComponent, canActivate: [CanEmployeeLogged] }, + ] - + }, + { path: 'reset-password' , loadChildren: () => import('./portal/employee/reset-password/reset-password.module').then( m => m.ResetPasswordModule) }, { path: '**', component: NotFoundComponent, canActivate: [CanEmployeeLogged] }, - - - ]; @NgModule({ diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index c339b2f..7623510 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -81,6 +81,10 @@ import { NgxPaginationModule } from 'ngx-pagination'; import { AdminRouterComponent } from './portal/admin/admin-router.component'; import { ArticleComponent } from './portal/admin/article/article.component'; import { ArticleModule } from './portal/admin/article/article.module'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MatOptionModule } from '@angular/material/core'; @@ -133,8 +137,7 @@ import { ArticleModule } from './portal/admin/article/article.module'; NotFoundComponent, ExcelComponent, AdminRouterComponent, - - + ], imports: [ @@ -143,6 +146,11 @@ import { ArticleModule } from './portal/admin/article/article.module'; HttpClientModule, FormsModule, CommonModule, + MatFormFieldModule, + MatInputModule, + MatAutocompleteModule, + MatOptionModule, + ReactiveFormsModule, HttpClientModule, PlotlyViaCDNModule, diff --git a/client/src/app/model/ad-product.model.ts b/client/src/app/model/ad-product.model.ts index f9a9907..d0d5b7d 100644 --- a/client/src/app/model/ad-product.model.ts +++ b/client/src/app/model/ad-product.model.ts @@ -1,7 +1,7 @@ import mongoose from 'mongoose'; import { Category } from './ad-category.model'; -export interface Product { +export interface Products { _id: string; category_id: Category | string ; diff --git a/client/src/app/portal/admin/product/ad-product/ad-product.component.ts b/client/src/app/portal/admin/product/ad-product/ad-product.component.ts index 2b5bb50..1cdcd0a 100644 --- a/client/src/app/portal/admin/product/ad-product/ad-product.component.ts +++ b/client/src/app/portal/admin/product/ad-product/ad-product.component.ts @@ -1,7 +1,7 @@ import { Component, ViewChild, ElementRef, OnInit, OnDestroy,} from '@angular/core'; import { Store, select } from '@ngrx/store'; import * as AdProductActions from '../ad-product/store/ad-product.action'; -import { Product } from '../../../../model/ad-product.model'; +import { Products } from '../../../../model/ad-product.model'; import { Observable, map, Subject, takeUntil } from 'rxjs'; import { AppState } from '../../../../state/app.state'; import { AdCategoryService } from '../../category/ad-category/ad-category.service'; @@ -30,15 +30,15 @@ export class AdProductComponent implements OnInit, OnDestroy { itemsPerPage: number = 5; totalPages: number = 1; - selectedProduct: Product | null = null; + selectedProduct: Products | null = null; categories$!: Observable; - product$: Observable; + product$: Observable; categories: Category[] = []; - productToEdit: Partial = {}; + productToEdit: Partial = {}; private destroy$ = new Subject(); @@ -102,7 +102,7 @@ export class AdProductComponent implements OnInit, OnDestroy { - editProducts(product: Partial) { + editProducts(product: Partial) { this.productToEdit = { ...product, @@ -115,12 +115,12 @@ export class AdProductComponent implements OnInit, OnDestroy { this.modal2.nativeElement.showModal(); } - editProduct(product: Partial) { + editProduct(product: Partial) { this.store.dispatch(AdProductActions.updateProduct({ product })); this.modal2.nativeElement.close(); } - deleteProduct(product: Product): void { + deleteProduct(product: Products): void { if (product._id) { this.store.dispatch(AdProductActions.deleteProduct({ productId: product._id })); console.log(`Deleted product with ID: ${product._id}`); @@ -129,7 +129,7 @@ export class AdProductComponent implements OnInit, OnDestroy { } } - confirmDelete(product: Product): void { + confirmDelete(product: Products): void { if (confirm('Are you sure you want to delete this product?')) { this.deleteProduct(product); } @@ -139,7 +139,7 @@ export class AdProductComponent implements OnInit, OnDestroy { this.modal.nativeElement.showModal(); } - get filteredRecords(): Observable { + get filteredRecords(): Observable { const searchTermLower = this.searchTerm.toLowerCase(); return this.product$.pipe( map(records => records.filter(record => @@ -163,7 +163,7 @@ export class AdProductComponent implements OnInit, OnDestroy { return category._id } - trackByProduct(index: number, product: Product): string { + trackByProduct(index: number, product: Products): string { return product._id; } @@ -173,7 +173,7 @@ export class AdProductComponent implements OnInit, OnDestroy { }); } - getCurrentPageRecords(): Observable { + getCurrentPageRecords(): Observable { return this.filteredRecords.pipe( map(records => { const startIndex = (this.currentPage - 1) * this.itemsPerPage; diff --git a/client/src/app/portal/admin/product/ad-product/ad-product.service.ts b/client/src/app/portal/admin/product/ad-product/ad-product.service.ts index 3650228..9c59068 100644 --- a/client/src/app/portal/admin/product/ad-product/ad-product.service.ts +++ b/client/src/app/portal/admin/product/ad-product/ad-product.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { Product } from '../../../../model/ad-product.model'; +import { Products } from '../../../../model/ad-product.model'; import { environment } from '../../../../../environment/environment'; @Injectable({ @@ -13,16 +13,16 @@ export class AdProductService { constructor(private http: HttpClient) { } - getProducts(): Observable { + getProducts(): Observable { const headers = { 'Cache-Control': 'no-cache' }; - return this.http.get(`${this.apiUrl}/product`, { headers }); + return this.http.get(`${this.apiUrl}/product`, { headers }); } - addProduct(product: Product): Observable { + addProduct(product: Products): Observable { return this.http.post(`${this.apiUrl}/product/add`, product); } - updateProduct(product: Partial): Observable { + updateProduct(product: Partial): Observable { return this.http.put(`${this.apiUrl}/product/update/${product._id}`, product); } @@ -30,4 +30,6 @@ export class AdProductService { return this.http.delete(`${this.apiUrl}/product/delete/${id}`); } + + } diff --git a/client/src/app/portal/admin/product/ad-product/store/ad-product.action.ts b/client/src/app/portal/admin/product/ad-product/store/ad-product.action.ts index 4b8d1e1..aa19f52 100644 --- a/client/src/app/portal/admin/product/ad-product/store/ad-product.action.ts +++ b/client/src/app/portal/admin/product/ad-product/store/ad-product.action.ts @@ -1,19 +1,19 @@ import { createAction, props } from '@ngrx/store'; -import { Product } from '../../../../../model/ad-product.model'; +import { Products } from '../../../../../model/ad-product.model'; export const loadProduct = createAction('[Product] Load Product'); -export const loadProductSuccess = createAction('[Product] Load Product Success', props<{ products: Product[] }>()); +export const loadProductSuccess = createAction('[Product] Load Product Success', props<{ products: Products[] }>()); export const loadProductFailure = createAction('[Product] Load Product Failure', props<{ error: string }>()); export const addProduct = createAction('[Ad Product ] Create Product', props<{ category_id: string, product: string, description: string, uom: string, price: number, availability: string }>()); -export const addProductSuccess = createAction('[Ad Product] Create Product Success', props<{ product: Product }>()); +export const addProductSuccess = createAction('[Ad Product] Create Product Success', props<{ product: Products }>()); export const addProductFailure = createAction('[Ad Product] Create Product Failure', props<{ error: string}>()); -export const updateProduct = createAction('[Ad Product] Update Product', props<{ product: Partial }>()); -export const updateProductSuccess = createAction('[Ad Product] Update Product Success', props<{ product: Product }>()); +export const updateProduct = createAction('[Ad Product] Update Product', props<{ product: Partial }>()); +export const updateProductSuccess = createAction('[Ad Product] Update Product Success', props<{ product: Products }>()); export const updateProductFailure = createAction('[Ad Product] Update Product Failure', props<{ error: string }>() ); diff --git a/client/src/app/portal/admin/product/ad-product/store/ad-product.effects.ts b/client/src/app/portal/admin/product/ad-product/store/ad-product.effects.ts index 33a0ad0..60216d8 100644 --- a/client/src/app/portal/admin/product/ad-product/store/ad-product.effects.ts +++ b/client/src/app/portal/admin/product/ad-product/store/ad-product.effects.ts @@ -5,7 +5,7 @@ import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; import { AdProductService } from '../ad-product.service'; import * as AdProductActions from '../store/ad-product.action' -import { Product } from '../../../../../model/ad-product.model'; +import { Products } from '../../../../../model/ad-product.model'; @@ -16,7 +16,7 @@ export class AdProductEffects { ofType(AdProductActions.loadProduct), mergeMap(() => this.adProductService.getProducts().pipe( - map((products: Product[]) => AdProductActions.loadProductSuccess({ products })), + map((products: Products[]) => AdProductActions.loadProductSuccess({ products })), catchError(error => of(AdProductActions.loadProductFailure({ error: error.message }))) ) @@ -29,8 +29,8 @@ export class AdProductEffects { ofType(AdProductActions.addProduct), switchMap(({ category_id, product, description, uom, price, availability }) => { console.log('Creating user...'); - const products: Partial = { category_id, product, description, uom, price, availability }; // Use Partial here - return this.adProductService.addProduct(products as Product).pipe( // Cast it back to User + const products: Partial = { category_id, product, description, uom, price, availability }; // Use Partial here + return this.adProductService.addProduct(products as Products).pipe( // Cast it back to User map(() => { console.log('User created successfully'); return AdProductActions.loadProduct(); // Trigger a load after create @@ -48,7 +48,7 @@ this.actions$.pipe( switchMap(({ product }) => { console.log('Updating category...'); return this.adProductService.updateProduct(product).pipe( - map((updatedProduct: Product) => { + map((updatedProduct: Products) => { console.log('Category updated successfully'); return AdProductActions.updateProductSuccess({ product: updatedProduct }); }), diff --git a/client/src/app/portal/admin/product/ad-product/store/ad-product.state.ts b/client/src/app/portal/admin/product/ad-product/store/ad-product.state.ts index cc5d4d2..cbe9b9e 100644 --- a/client/src/app/portal/admin/product/ad-product/store/ad-product.state.ts +++ b/client/src/app/portal/admin/product/ad-product/store/ad-product.state.ts @@ -1,8 +1,8 @@ -import { Product } from "../../../../../model/ad-product.model"; +import { Products } from "../../../../../model/ad-product.model"; export interface AdProductState { - products: Product[]; + products: Products[]; } diff --git a/client/src/app/portal/employee/dashboard/em-dashboard/em-dashboard.component.ts b/client/src/app/portal/employee/dashboard/em-dashboard/em-dashboard.component.ts index d0c2d1d..36f794e 100644 --- a/client/src/app/portal/employee/dashboard/em-dashboard/em-dashboard.component.ts +++ b/client/src/app/portal/employee/dashboard/em-dashboard/em-dashboard.component.ts @@ -12,6 +12,7 @@ import { ProfileService } from '../../profile/profile/profile.service'; import { SalesAnalysisComponent } from '../../sales/sales-analysis/sales-analysis.component'; import { ArticleService } from '../../../admin/article/article.service'; import { Article } from '../../../../model/ad-article.model'; +import { ActivatedRoute } from '@angular/router'; @@ -42,15 +43,25 @@ export class EmDashboardComponent implements OnInit { messages$: Observable; + profileData: any; + profileData$!: Observable - constructor(private store: Store , private authService: EmployeeLoginService, private employeeService: ProfileService, private articleService: ArticleService) { + + + constructor(private store: Store , private authService: EmployeeLoginService, private employeeService: ProfileService, private articleService: ArticleService, private route: ActivatedRoute) { this.messages$ = this.store.pipe(select(state => state.message.messages)); } ngOnInit(): void { + + this.route.data.subscribe((data) => console.log('Data',data)) + + this.profileData$ = this.route.data.pipe(map(data => data['profileData'] )) + + this.store.dispatch(EmMessagingActions.loadMessage()); this.loadProfile() this.loadArticle() diff --git a/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.html b/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.html index f517652..8d508f0 100644 --- a/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.html +++ b/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.html @@ -97,7 +97,7 @@ - + diff --git a/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.ts b/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.ts index f3ab28a..5b2918d 100644 --- a/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.ts +++ b/client/src/app/portal/employee/sales/add-quotation/add-quotation.component.ts @@ -4,7 +4,7 @@ import { QuotationService } from '../quotation/quotation.service'; import { AddQuotationService } from './add-quotation.service'; import { AddQuotation, Product } from '../../../../model/sales-addquo'; import { MatSnackBar } from '@angular/material/snack-bar'; -import { Subscription, firstValueFrom } from 'rxjs'; +import { Subject, Subscription, firstValueFrom } from 'rxjs'; import { Quotation } from '../../../../model/sales-quotation.model'; import { OrderStatus } from '../../../../enums/order-status.enum'; //import { jsPDF } from "jspdf" @@ -12,6 +12,8 @@ import pdfMake from 'pdfmake/build/pdfmake'; import pdfFonts from 'pdfmake/build/vfs_fonts'; import { TDocumentDefinitions } from 'pdfmake/interfaces'; import { environment } from '../../../../../environment/environment'; +import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; +import { Products } from '../../../../model/ad-product.model'; @@ -50,6 +52,9 @@ export class AddQuotationComponent implements OnInit, OnDestroy { items: Product[] = [{ product: '', qty: 1 , uom: '', unit: 0 , uplift: 0 , upliftedprice: 0 , total: 0 }]; + filteredProducts: Products[] = []; + + constructor(private route: ActivatedRoute, private addQuotationService: AddQuotationService, private quotationService: QuotationService, private snackBar: MatSnackBar ) { } diff --git a/client/src/app/portal/employee/sales/add-quotation/add-quotation.service.ts b/client/src/app/portal/employee/sales/add-quotation/add-quotation.service.ts index 439c57a..0f57e42 100644 --- a/client/src/app/portal/employee/sales/add-quotation/add-quotation.service.ts +++ b/client/src/app/portal/employee/sales/add-quotation/add-quotation.service.ts @@ -3,6 +3,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; import { AddQuotation } from '../../../../model/sales-addquo'; import { environment } from '../../../../../environment/environment'; +import { Products } from '../../../../model/ad-product.model'; @Injectable({ providedIn: 'root' @@ -32,7 +33,11 @@ export class AddQuotationService { getQuotationBySalesRFQId(salesRFQId: string): Observable { return this.http.get(`${this.apiUrl}/quotations/form-add/${salesRFQId}`); } - + searchProducts(query: string): Observable { + const headers = { 'Cache-Control': 'no-cache' }; + return this.http.get(`${this.apiUrl}/quatations/products?q=${query}`, { headers }); + } + } diff --git a/client/src/app/portal/employee/shipment/shipment-history/shipment-history.component.ts b/client/src/app/portal/employee/shipment/shipment-history/shipment-history.component.ts index 92550b0..58e7adc 100644 --- a/client/src/app/portal/employee/shipment/shipment-history/shipment-history.component.ts +++ b/client/src/app/portal/employee/shipment/shipment-history/shipment-history.component.ts @@ -1,9 +1,9 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ShipmentHistoryService } from './shipment-history.service'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { Subscription, firstValueFrom } from 'rxjs'; import { Shipment } from '../../../../model/shipment.model'; import { response } from 'express'; +import { MatSnackBar } from '@angular/material/snack-bar'; @Component({ selector: 'app-shipment-history', diff --git a/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.component.ts b/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.component.ts index 77ef1f0..331743e 100644 --- a/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.component.ts +++ b/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.component.ts @@ -1,7 +1,7 @@ import { Component, ViewChild, ElementRef, OnInit, OnDestroy } from '@angular/core'; import { Store, select } from '@ngrx/store'; import * as AdProductActions from '../../../../store/action/inventory-list.action' -import { Product } from '../../../../model/ad-product.model'; +import { Products } from '../../../../model/ad-product.model'; import { Observable, map, Subject, of } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { AppState } from '../../../../state/app.state'; @@ -18,7 +18,7 @@ import { Category } from '../../../../model/ad-category.model'; export class InventoryListComponent implements OnInit, OnDestroy { - product$: Observable; + product$: Observable; searchTerm: string = ''; currentPage: number = 1; @@ -57,12 +57,12 @@ export class InventoryListComponent implements OnInit, OnDestroy { return 'Unknown Category'; } - trackByProductId(index: number, product: Product): string { + trackByProductId(index: number, product: Products): string { return product._id; } - getCurrentPageRecords(): Observable { + getCurrentPageRecords(): Observable { return this.filteredRecords.pipe( map(records => { const startIndex = (this.currentPage - 1) * this.itemsPerPage; @@ -72,7 +72,7 @@ export class InventoryListComponent implements OnInit, OnDestroy { } - get filteredRecords(): Observable { + get filteredRecords(): Observable { const searchTermLower = this.searchTerm.toLowerCase(); return this.product$.pipe( map(records => records.filter(record => diff --git a/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.service.ts b/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.service.ts index 8f4f6f5..50523c5 100644 --- a/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.service.ts +++ b/client/src/app/portal/employee/warehouse/inventory-list/inventory-list.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { Product } from '../../../../model/ad-product.model'; +import { Products } from '../../../../model/ad-product.model'; import { environment } from '../../../../../environment/environment'; @Injectable({ @@ -13,9 +13,9 @@ export class InventoryListService { constructor(private http: HttpClient) { } - getInventory(): Observable { + getInventory(): Observable { const headers = { 'Cache-Control': 'no-cache' }; - return this.http.get(`${this.apiUrl}/inventory-list`, { headers }); + return this.http.get(`${this.apiUrl}/inventory-list`, { headers }); } } diff --git a/client/src/app/service/customer/customer.resolver.spec.ts b/client/src/app/service/customer/customer.resolver.spec.ts new file mode 100644 index 0000000..f31e136 --- /dev/null +++ b/client/src/app/service/customer/customer.resolver.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { ResolveFn } from '@angular/router'; + +import { customerResolver } from './customer.resolver'; + +describe('customerResolver', () => { + const executeResolver: ResolveFn = (...resolverParameters) => + TestBed.runInInjectionContext(() => customerResolver(...resolverParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeResolver).toBeTruthy(); + }); +}); diff --git a/client/src/app/service/customer/customer.resolver.ts b/client/src/app/service/customer/customer.resolver.ts new file mode 100644 index 0000000..670cbab --- /dev/null +++ b/client/src/app/service/customer/customer.resolver.ts @@ -0,0 +1,5 @@ +import { ResolveFn } from '@angular/router'; + +export const customerResolver: ResolveFn = (route, state) => { + return true; +}; diff --git a/client/src/app/service/dashboard/dashboard-resolver.resolver.spec.ts b/client/src/app/service/dashboard/dashboard-resolver.resolver.spec.ts new file mode 100644 index 0000000..e214323 --- /dev/null +++ b/client/src/app/service/dashboard/dashboard-resolver.resolver.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { ResolveFn } from '@angular/router'; + +import { dashboardResolverResolver } from './dashboard-resolver.resolver'; + +describe('dashboardResolverResolver', () => { + const executeResolver: ResolveFn = (...resolverParameters) => + TestBed.runInInjectionContext(() => dashboardResolverResolver(...resolverParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeResolver).toBeTruthy(); + }); +}); diff --git a/client/src/app/service/dashboard/dashboard-resolver.resolver.ts b/client/src/app/service/dashboard/dashboard-resolver.resolver.ts new file mode 100644 index 0000000..a24ef39 --- /dev/null +++ b/client/src/app/service/dashboard/dashboard-resolver.resolver.ts @@ -0,0 +1,11 @@ +import { inject } from '@angular/core'; +import { ResolveFn } from '@angular/router'; +import { EmDashboardService } from '../../portal/employee/dashboard/em-dashboard/em-dashboard.service'; +import { Messaging } from '../../model/em-messaging.model'; + +export const dashboardResolverResolver: ResolveFn = (route, state) => { + + const dashService = inject(EmDashboardService) + + return dashService.getMessage(); +}; diff --git a/client/src/app/service/profile/profile.resolver.spec.ts b/client/src/app/service/profile/profile.resolver.spec.ts new file mode 100644 index 0000000..aa1c3c4 --- /dev/null +++ b/client/src/app/service/profile/profile.resolver.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { ResolveFn } from '@angular/router'; + +import { profileResolver } from './profile.resolver'; + +describe('profileResolver', () => { + const executeResolver: ResolveFn = (...resolverParameters) => + TestBed.runInInjectionContext(() => profileResolver(...resolverParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeResolver).toBeTruthy(); + }); +}); diff --git a/client/src/app/service/profile/profile.resolver.ts b/client/src/app/service/profile/profile.resolver.ts new file mode 100644 index 0000000..6adecdf --- /dev/null +++ b/client/src/app/service/profile/profile.resolver.ts @@ -0,0 +1,10 @@ +import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router'; +import { Profile } from '../../model/emp-profile.model'; +import { inject } from '@angular/core'; +import { ProfileService } from '../../portal/employee/profile/profile/profile.service'; +import { Employee } from '../../model/ad-employee.model'; + +export const profileResolver: ResolveFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + console.log('Resolver triggered'); + return inject(ProfileService).getProfile(); +}; diff --git a/client/src/app/store/action/inventory-list.action.ts b/client/src/app/store/action/inventory-list.action.ts index 66deaed..7fc61df 100644 --- a/client/src/app/store/action/inventory-list.action.ts +++ b/client/src/app/store/action/inventory-list.action.ts @@ -1,7 +1,7 @@ import { createAction, props } from '@ngrx/store'; -import { Product } from '../../model/ad-product.model'; +import { Products } from '../../model/ad-product.model'; export const loadInventory = createAction('[Product] Load Product'); -export const loadInventorySuccess = createAction('[Product] Load Product Success', props<{ products: Product[] }>()); +export const loadInventorySuccess = createAction('[Product] Load Product Success', props<{ products: Products[] }>()); export const loadInventoryFailure = createAction('[Product] Load Product Failure', props<{ error: string }>()); \ No newline at end of file diff --git a/client/src/app/store/effects/inventory-list.effects.ts b/client/src/app/store/effects/inventory-list.effects.ts index f9d26de..c1dd11c 100644 --- a/client/src/app/store/effects/inventory-list.effects.ts +++ b/client/src/app/store/effects/inventory-list.effects.ts @@ -5,7 +5,7 @@ import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; import { InventoryListService } from '../../portal/employee/warehouse/inventory-list/inventory-list.service'; import * as AdProductActions from '../action/inventory-list.action' -import { Product } from '../../model/ad-product.model'; +import { Products } from '../../model/ad-product.model'; @Injectable() @@ -15,7 +15,7 @@ export class InventoryEffects { ofType(AdProductActions.loadInventory), mergeMap(() => this.adProductService.getInventory().pipe( - map((products: Product[]) => AdProductActions.loadInventorySuccess({ products })), + map((products: Products[]) => AdProductActions.loadInventorySuccess({ products })), catchError(error => of(AdProductActions.loadInventoryFailure({ error: error.message }))) ) diff --git a/client/src/app/store/state/inventory-list.state.ts b/client/src/app/store/state/inventory-list.state.ts index 04c80b3..220adcd 100644 --- a/client/src/app/store/state/inventory-list.state.ts +++ b/client/src/app/store/state/inventory-list.state.ts @@ -1,8 +1,8 @@ -import { Product } from "../../model/ad-product.model"; +import { Products } from "../../model/ad-product.model"; export interface InventoryState { - products: Product[]; + products: Products[]; } diff --git a/client/src/custom-theme.scss b/client/src/custom-theme.scss index ce92607..8de2056 100644 --- a/client/src/custom-theme.scss +++ b/client/src/custom-theme.scss @@ -25,7 +25,9 @@ $client-theme: mat.define-light-theme(( primary: $client-primary, accent: $client-accent, warn: $client-warn, - ) + ), + typography: mat.define-typography-config(), + density: 0 )); // Include theme styles for core and each component used in your app. @@ -33,3 +35,4 @@ $client-theme: mat.define-light-theme(( // that you are using. @include mat.all-component-themes($client-theme); + diff --git a/server/controller/salesController.js b/server/controller/salesController.js index 9412710..a6790c1 100644 --- a/server/controller/salesController.js +++ b/server/controller/salesController.js @@ -6,6 +6,7 @@ const salesQuotation = require('../model/quotatiionDB') const collectionemployee = require('../model/employeeDB') const purchasePO = require('../model/poDS') const clientPO = require('../model/clientPoDS') +const collectionproduct = require('../model/productDB') const generateCustomUUID = require('../uuid/uuid') const genereateCustomSPO = require('../uuid/idspo') @@ -428,6 +429,19 @@ const loadSalesAnalysis = async (req , res) => { } +const searchProduct = async (req, res) => { + try { + const searchQuery = req.query.q; + + const products = await collectionproduct.find({ name: { $regex: searchQuery, $options: 'i' } }).select('description'); + res.json(products); + } catch (error) { + console.error('Error fetching Products:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } + }; + + @@ -449,6 +463,7 @@ module.exports = { ClientPoSingle, addClientPo, updateClientPo, - fetchQuotationId + fetchQuotationId, + searchProduct } \ No newline at end of file diff --git a/server/routes/sales.js b/server/routes/sales.js index 01f3a33..b48193e 100644 --- a/server/routes/sales.js +++ b/server/routes/sales.js @@ -16,6 +16,8 @@ router.get('/quotations/:id', verifyToken, salesController.quotationSingle) router.post('/quotations/form-add', verifyToken, salesController.addQuotation) router.put('/quotations/form-update/:id', verifyToken, salesController.updateQuotation) +router.get('/quotations/products', salesController.searchProduct) + router.get('/quotations/form-add/:salesRFQ_id', verifyToken, salesController.fetchSrfq) router.get('/sales-order', verifyToken , salesController.loadSalesOrder)