diff --git a/apps/angular/angular.json b/apps/angular/angular.json index 51220794..cb38e730 100644 --- a/apps/angular/angular.json +++ b/apps/angular/angular.json @@ -161,7 +161,7 @@ "projectType": "library", "root": "projects/catalog", "sourceRoot": "projects/catalog/src", - "prefix": "lib", + "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:ng-packagr", @@ -192,7 +192,7 @@ "projectType": "library", "root": "projects/ordering", "sourceRoot": "projects/ordering/src", - "prefix": "lib", + "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:ng-packagr", diff --git a/apps/angular/projects/catalog/src/pages/product/product.component.ts b/apps/angular/projects/catalog/src/pages/product/product.component.ts index 675e824f..e56170fc 100644 --- a/apps/angular/projects/catalog/src/pages/product/product.component.ts +++ b/apps/angular/projects/catalog/src/pages/product/product.component.ts @@ -8,7 +8,7 @@ import { ProductDto } from '../../lib/proxy/products/models'; import { ProductService } from '../../lib/proxy/products/product.service'; @Component({ - selector: 'lib-product', + selector: 'app-product', templateUrl: './product.component.html', styleUrls: ['./product.component.css'], providers: [ListService], @@ -86,7 +86,9 @@ export class ProductComponent implements OnInit { } save() { - if (!this.form.valid || this.modalBusy) return; + if (!this.form.valid || this.modalBusy) { + return; + } this.modalBusy = true; const { id } = this.selected; diff --git a/apps/angular/projects/ordering/config/src/enums/route-names.ts b/apps/angular/projects/ordering/config/src/enums/route-names.ts index 6dc09b1f..01d05b2e 100644 --- a/apps/angular/projects/ordering/config/src/enums/route-names.ts +++ b/apps/angular/projects/ordering/config/src/enums/route-names.ts @@ -1,3 +1,5 @@ export const enum eOrderingRouteNames { - ordering = 'OrderingService::Menu:OrderManagement' + ordering = 'OrderingService::Menu:OrderManagement', + dashboard = 'OrderingService::Menu:Dashboard', + orders = 'OrderingService::Menu:Orders', } diff --git a/apps/angular/projects/ordering/config/src/providers/route.provider.ts b/apps/angular/projects/ordering/config/src/providers/route.provider.ts index be624bc5..770d6536 100644 --- a/apps/angular/projects/ordering/config/src/providers/route.provider.ts +++ b/apps/angular/projects/ordering/config/src/providers/route.provider.ts @@ -18,6 +18,20 @@ export function configureRoutes(routesService: RoutesService) { parentName: null, iconClass: 'bi bi-collection-fill', }, + { + path: '/ordering', + name: eOrderingRouteNames.dashboard, + parentName: eOrderingRouteNames.ordering, + order: 1, + requiredPolicy: eOrderingPolicyNames.ordering, + }, + { + path: '/ordering/orders', + name: eOrderingRouteNames.orders, + parentName: eOrderingRouteNames.ordering, + order: 2, + requiredPolicy: eOrderingPolicyNames.ordering, + }, ]); }; } diff --git a/apps/angular/projects/ordering/src/lib/proxy/generate-proxy.json b/apps/angular/projects/ordering/src/lib/proxy/generate-proxy.json index 626d4922..24e3a0b7 100644 --- a/apps/angular/projects/ordering/src/lib/proxy/generate-proxy.json +++ b/apps/angular/projects/ordering/src/lib/proxy/generate-proxy.json @@ -3,6 +3,84 @@ "ordering" ], "modules": { + "abp": { + "rootPath": "abp", + "remoteServiceName": "abp", + "controllers": { + "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController": { + "controllerName": "AbpApplicationConfiguration", + "controllerGroupName": "AbpApplicationConfiguration", + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController", + "interfaces": [ + { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService" + } + ], + "actions": { + "GetAsync": { + "uniqueName": "GetAsync", + "name": "GetAsync", + "httpMethod": "GET", + "url": "api/abp/application-configuration", + "supportedVersions": [], + "parametersOnMethod": [], + "parameters": [], + "returnValue": { + "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto", + "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService" + } + } + }, + "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController": { + "controllerName": "AbpApiDefinition", + "controllerGroupName": "AbpApiDefinition", + "type": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController", + "interfaces": [], + "actions": { + "GetByModel": { + "uniqueName": "GetByModel", + "name": "Get", + "httpMethod": "GET", + "url": "api/abp/api-definition", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "model", + "typeAsString": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto, Volo.Abp.Http", + "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", + "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "model", + "name": "IncludeTypes", + "jsonName": null, + "type": "System.Boolean", + "typeSimple": "boolean", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "model" + } + ], + "returnValue": { + "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel", + "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel" + }, + "allowAnonymous": null, + "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController" + } + } + } + } + }, "ordering": { "rootPath": "ordering", "remoteServiceName": "Ordering", @@ -60,7 +138,7 @@ "type": "EShopOnAbp.OrderingService.Orders.OrderDto", "typeSimple": "EShopOnAbp.OrderingService.Orders.OrderDto" }, - "allowAnonymous": null, + "allowAnonymous": false, "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" }, "GetMyOrdersAsyncByInput": { @@ -97,7 +175,7 @@ "type": "System.Collections.Generic.List", "typeSimple": "[EShopOnAbp.OrderingService.Orders.OrderDto]" }, - "allowAnonymous": null, + "allowAnonymous": true, "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" }, "GetOrdersAsyncByInput": { @@ -198,6 +276,43 @@ "allowAnonymous": false, "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" }, + "GetDashboardAsyncByInput": { + "uniqueName": "GetDashboardAsyncByInput", + "name": "GetDashboardAsync", + "httpMethod": "GET", + "url": "api/ordering/order/dashboard", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "EShopOnAbp.OrderingService.Orders.DashboardInput, EShopOnAbp.OrderingService.Application.Contracts", + "type": "EShopOnAbp.OrderingService.Orders.DashboardInput", + "typeSimple": "EShopOnAbp.OrderingService.Orders.DashboardInput", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "Filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + } + ], + "returnValue": { + "type": "EShopOnAbp.OrderingService.Orders.DashboardDto", + "typeSimple": "EShopOnAbp.OrderingService.Orders.DashboardDto" + }, + "allowAnonymous": false, + "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" + }, "GetByOrderNoAsyncByOrderNo": { "uniqueName": "GetByOrderNoAsyncByOrderNo", "name": "GetByOrderNoAsync", @@ -232,7 +347,7 @@ "type": "EShopOnAbp.OrderingService.Orders.OrderDto", "typeSimple": "EShopOnAbp.OrderingService.Orders.OrderDto" }, - "allowAnonymous": null, + "allowAnonymous": false, "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" }, "SetAsCancelledAsyncById": { @@ -343,90 +458,12 @@ "type": "EShopOnAbp.OrderingService.Orders.OrderDto", "typeSimple": "EShopOnAbp.OrderingService.Orders.OrderDto" }, - "allowAnonymous": null, + "allowAnonymous": true, "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" } } } } - }, - "abp": { - "rootPath": "abp", - "remoteServiceName": "abp", - "controllers": { - "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController": { - "controllerName": "AbpApplicationConfiguration", - "controllerGroupName": "AbpApplicationConfiguration", - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationController", - "interfaces": [ - { - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService" - } - ], - "actions": { - "GetAsync": { - "uniqueName": "GetAsync", - "name": "GetAsync", - "httpMethod": "GET", - "url": "api/abp/application-configuration", - "supportedVersions": [], - "parametersOnMethod": [], - "parameters": [], - "returnValue": { - "type": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto", - "typeSimple": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ApplicationConfigurationDto" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.IAbpApplicationConfigurationAppService" - } - } - }, - "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController": { - "controllerName": "AbpApiDefinition", - "controllerGroupName": "AbpApiDefinition", - "type": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController", - "interfaces": [], - "actions": { - "GetByModel": { - "uniqueName": "GetByModel", - "name": "Get", - "httpMethod": "GET", - "url": "api/abp/api-definition", - "supportedVersions": [], - "parametersOnMethod": [ - { - "name": "model", - "typeAsString": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto, Volo.Abp.Http", - "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", - "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModelRequestDto", - "isOptional": false, - "defaultValue": null - } - ], - "parameters": [ - { - "nameOnMethod": "model", - "name": "IncludeTypes", - "jsonName": null, - "type": "System.Boolean", - "typeSimple": "boolean", - "isOptional": false, - "defaultValue": null, - "constraintTypes": null, - "bindingSourceId": "ModelBinding", - "descriptorName": "model" - } - ], - "returnValue": { - "type": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel", - "typeSimple": "Volo.Abp.Http.Modeling.ApplicationApiDescriptionModel" - }, - "allowAnonymous": null, - "implementFrom": "Volo.Abp.AspNetCore.Mvc.ApiExploring.AbpApiDefinitionController" - } - } - } - } } }, "types": { @@ -2285,6 +2322,135 @@ } ] }, + "EShopOnAbp.OrderingService.Orders.DashboardInput": { + "baseType": null, + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "Filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false + } + ] + }, + "EShopOnAbp.OrderingService.Orders.DashboardDto": { + "baseType": "Volo.Abp.Application.Dtos.EntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "TopSellings", + "jsonName": null, + "type": "[EShopOnAbp.OrderingService.OrderItems.TopSellingDto]", + "typeSimple": "[EShopOnAbp.OrderingService.OrderItems.TopSellingDto]", + "isRequired": false + }, + { + "name": "Payments", + "jsonName": null, + "type": "[EShopOnAbp.OrderingService.Orders.PaymentDto]", + "typeSimple": "[EShopOnAbp.OrderingService.Orders.PaymentDto]", + "isRequired": false + }, + { + "name": "OrderStatusDto", + "jsonName": null, + "type": "[EShopOnAbp.OrderingService.Orders.OrderStatusDto]", + "typeSimple": "[EShopOnAbp.OrderingService.Orders.OrderStatusDto]", + "isRequired": false + } + ] + }, + "EShopOnAbp.OrderingService.OrderItems.TopSellingDto": { + "baseType": "Volo.Abp.Application.Dtos.EntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "ProductName", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false + }, + { + "name": "PictureUrl", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false + }, + { + "name": "Units", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false + } + ] + }, + "EShopOnAbp.OrderingService.Orders.PaymentDto": { + "baseType": "Volo.Abp.Application.Dtos.EntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "RateOfPaymentMethod", + "jsonName": null, + "type": "System.Decimal", + "typeSimple": "number", + "isRequired": false + }, + { + "name": "PaymentMethod", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false + } + ] + }, + "EShopOnAbp.OrderingService.Orders.OrderStatusDto": { + "baseType": "Volo.Abp.Application.Dtos.EntityDto", + "isEnum": false, + "enumNames": null, + "enumValues": null, + "genericArguments": null, + "properties": [ + { + "name": "CountOfStatusOrder", + "jsonName": null, + "type": "System.Int32", + "typeSimple": "number", + "isRequired": false + }, + { + "name": "OrderStatus", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isRequired": false + }, + { + "name": "TotalIncome", + "jsonName": null, + "type": "System.Decimal", + "typeSimple": "number", + "isRequired": false + } + ] + }, "EShopOnAbp.OrderingService.Orders.OrderCreateDto": { "baseType": null, "isEnum": false, diff --git a/apps/angular/projects/ordering/src/lib/proxy/index.ts b/apps/angular/projects/ordering/src/lib/proxy/index.ts index 4939e1de..cb1485e9 100644 --- a/apps/angular/projects/ordering/src/lib/proxy/index.ts +++ b/apps/angular/projects/ordering/src/lib/proxy/index.ts @@ -1,2 +1,3 @@ +import * as OrderItems from './order-items'; import * as Orders from './orders'; -export { Orders }; +export { OrderItems, Orders }; diff --git a/apps/angular/projects/ordering/src/lib/proxy/order-items/index.ts b/apps/angular/projects/ordering/src/lib/proxy/order-items/index.ts new file mode 100644 index 00000000..e9644dae --- /dev/null +++ b/apps/angular/projects/ordering/src/lib/proxy/order-items/index.ts @@ -0,0 +1 @@ +export * from './models'; diff --git a/apps/angular/projects/ordering/src/lib/proxy/order-items/models.ts b/apps/angular/projects/ordering/src/lib/proxy/order-items/models.ts new file mode 100644 index 00000000..5700a85f --- /dev/null +++ b/apps/angular/projects/ordering/src/lib/proxy/order-items/models.ts @@ -0,0 +1,7 @@ +import type { EntityDto } from '@abp/ng.core'; + +export interface TopSellingDto extends EntityDto { + productName?: string; + pictureUrl?: string; + units: number; +} diff --git a/apps/angular/projects/ordering/src/lib/proxy/orders/models.ts b/apps/angular/projects/ordering/src/lib/proxy/orders/models.ts index 8abaecce..67f3929d 100644 --- a/apps/angular/projects/ordering/src/lib/proxy/orders/models.ts +++ b/apps/angular/projects/ordering/src/lib/proxy/orders/models.ts @@ -1,4 +1,5 @@ import type { EntityDto } from '@abp/ng.core'; +import type { TopSellingDto } from '../order-items/models'; import type { OrderStatus } from './order-status.enum'; export interface BuyerDto extends EntityDto { @@ -6,6 +7,16 @@ export interface BuyerDto extends EntityDto { email?: string; } +export interface DashboardDto extends EntityDto { + topSellings: TopSellingDto[]; + payments: PaymentDto[]; + orderStatusDto: OrderStatusDto[]; +} + +export interface DashboardInput { + filter?: string; +} + export interface GetMyOrdersInput { filter?: string; } @@ -56,3 +67,14 @@ export interface OrderItemDto extends EntityDto { discount: number; units: number; } + +export interface OrderStatusDto extends EntityDto { + countOfStatusOrder: number; + orderStatus?: string; + totalIncome: number; +} + +export interface PaymentDto extends EntityDto { + rateOfPaymentMethod: number; + paymentMethod?: string; +} diff --git a/apps/angular/projects/ordering/src/lib/proxy/orders/order-status.enum.ts b/apps/angular/projects/ordering/src/lib/proxy/orders/order-status.enum.ts index d63bfc72..75df2295 100644 --- a/apps/angular/projects/ordering/src/lib/proxy/orders/order-status.enum.ts +++ b/apps/angular/projects/ordering/src/lib/proxy/orders/order-status.enum.ts @@ -1,10 +1,10 @@ -import { mapEnumToOptions } from '@abp/ng.core'; - -export enum OrderStatus { - Placed = 0, - Paid = 1, - Shipped = 2, - Cancelled = 3, -} - -export const orderStatusOptions = mapEnumToOptions(OrderStatus); +import { mapEnumToOptions } from '@abp/ng.core'; + +export enum OrderStatus { + Placed = 0, + Paid = 1, + Shipped = 2, + Cancelled = 3, +} + +export const orderStatusOptions = mapEnumToOptions(OrderStatus); diff --git a/apps/angular/projects/ordering/src/lib/proxy/orders/order.service.ts b/apps/angular/projects/ordering/src/lib/proxy/orders/order.service.ts index efe0ba2a..9bd2e3cb 100644 --- a/apps/angular/projects/ordering/src/lib/proxy/orders/order.service.ts +++ b/apps/angular/projects/ordering/src/lib/proxy/orders/order.service.ts @@ -1,4 +1,4 @@ -import type { GetMyOrdersInput, GetOrdersInput, OrderCreateDto, OrderDto } from './models'; +import type { DashboardDto, DashboardInput, GetMyOrdersInput, GetOrdersInput, OrderCreateDto, OrderDto } from './models'; import { RestService } from '@abp/ng.core'; import type { PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core'; import { Injectable } from '@angular/core'; @@ -32,6 +32,14 @@ export class OrderService { }, { apiName: this.apiName }); + getDashboard = (input: DashboardInput) => + this.restService.request({ + method: 'GET', + url: '/api/ordering/order/dashboard', + params: { filter: input.filter }, + }, + { apiName: this.apiName }); + getListPaged = (input: PagedAndSortedResultRequestDto) => this.restService.request>({ method: 'GET', diff --git a/apps/angular/projects/ordering/src/ordering-routing.module.ts b/apps/angular/projects/ordering/src/ordering-routing.module.ts index 3ed73a9a..b244fed3 100644 --- a/apps/angular/projects/ordering/src/ordering-routing.module.ts +++ b/apps/angular/projects/ordering/src/ordering-routing.module.ts @@ -2,8 +2,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = [ - { path: '', redirectTo: 'orders', pathMatch: 'full' }, - { path: 'orders', loadChildren: () => import('./pages/orders/orders.module').then(m => m.OrdersModule) }, + { + path: '', + loadChildren: () => import('./pages/dashboard/dashboard.module').then(m => m.DashboardModule), + pathMatch: 'full', + }, + { + path: 'orders', + loadChildren: () => import('./pages/orders/orders.module').then(m => m.OrdersModule), + }, ]; @NgModule({ diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.html b/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.html new file mode 100644 index 00000000..67767856 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.html @@ -0,0 +1,19 @@ + +
+
+
+
+
{{ 'OrderingService::Dashboard:OrderStatusChartTitle' | abpLocalization }}
+
+
+
+
+ +
+
+ diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.spec.ts b/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.spec.ts new file mode 100644 index 00000000..24501c16 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { OrderStatusChartComponent } from './order-status-chart.component'; + +describe('OrderStatusChartComponent', () => { + let component: OrderStatusChartComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ OrderStatusChartComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(OrderStatusChartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.ts b/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.ts new file mode 100644 index 00000000..9465457e --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/order-status-chart/order-status-chart.component.ts @@ -0,0 +1,38 @@ +import { Component, Input } from '@angular/core'; +import { OrderStatusDto } from '../../../../lib/proxy/orders'; + +@Component({ + selector: 'app-order-status-chart', + templateUrl: './order-status-chart.component.html', +}) +export class OrderStatusChartComponent { + @Input() + set data(value: OrderStatusDto[]) { + const filtered = value.filter(x => x.countOfStatusOrder > 0); + this.chartData.datasets = [ + { + data: [...filtered.map(x => x.countOfStatusOrder)], + backgroundColor: ['#fdcb6e', '#0984e3', '#ff7675'], + }, + ]; + this.chartData.labels = [...filtered.map(x => x.orderStatus)]; + } + + chartData = { + labels: [], + datasets: [], + }; + + options = { + plugins: { + title: { + display: false, + text: '', + fontSize: 16, + }, + legend: { + position: 'bottom', + }, + }, + }; +} diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.html b/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.html new file mode 100644 index 00000000..d46e694a --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.html @@ -0,0 +1,19 @@ + +
+
+
+
+
{{ 'OrderingService::Dashboard:PaymentChartTitle' | abpLocalization }}
+
+
+
+
+ +
+
+ diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.spec.ts b/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.spec.ts new file mode 100644 index 00000000..cbdd8ee3 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PaymentsChartComponent } from './payments-chart.component'; + +describe('PaymentsChartComponent', () => { + let component: PaymentsChartComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PaymentsChartComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PaymentsChartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.ts b/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.ts new file mode 100644 index 00000000..fc287008 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/payments-chart/payments-chart.component.ts @@ -0,0 +1,35 @@ +import { Component, Input } from '@angular/core'; +import { PaymentDto } from '../../../../lib/proxy/orders'; + +@Component({ + selector: 'app-payments-chart', + templateUrl: './payments-chart.component.html', +}) +export class PaymentsChartComponent { + @Input() + set data(value: PaymentDto[]) { + this.chartData.datasets = [ + { + data: [...value.map(x => x.rateOfPaymentMethod)], + backgroundColor: ['#fdcb6e', '#0984e3', '#ff7675'], + }, + ]; + this.chartData.labels = [...value.map(x => x.paymentMethod)]; + } + + chartData = { + labels: [], + datasets: [], + }; + + options = { + plugins: { + title: { + display: false, + }, + legend: { + position: 'bottom', + }, + }, + }; +} diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.html b/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.html new file mode 100644 index 00000000..a44780d8 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.html @@ -0,0 +1,24 @@ +
+
+
+
+
{{ 'OrderingService::Dashboard:TopSellingCardTitle' | abpLocalization }}
+
+
+
+
+ + + + + + +
+
+ diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.spec.ts b/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.spec.ts new file mode 100644 index 00000000..22723afe --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TopSellingComponent } from './top-selling.component'; + +describe('TopSellingComponent', () => { + let component: TopSellingComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ TopSellingComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TopSellingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.ts b/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.ts new file mode 100644 index 00000000..07f13f0f --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/components/top-selling/top-selling.component.ts @@ -0,0 +1,12 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { TopSellingDto } from '../../../../lib/proxy/order-items'; + +@Component({ + selector: 'app-top-selling', + templateUrl: './top-selling.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class TopSellingComponent { + @Input() + topSellingProducts: Array | undefined; +} diff --git a/apps/angular/projects/ordering/src/pages/dashboard/dashboard-routing.module.ts b/apps/angular/projects/ordering/src/pages/dashboard/dashboard-routing.module.ts new file mode 100644 index 00000000..ebb49f42 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/dashboard-routing.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DashboardComponent } from './dashboard.component'; + +const routes: Routes = [ + { + path: '', + component: DashboardComponent, + // canActivate: [AuthGuard, PermissionGuard], + // data: { + // requiredPolicy: eCatalogPolicyNames.ProductManagement, + // }, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class DashboardRoutingModule {} diff --git a/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.css b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.css new file mode 100644 index 00000000..e69de29b diff --git a/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.html b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.html new file mode 100644 index 00000000..a62a5932 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.html @@ -0,0 +1,19 @@ +

{{"OrderingService::Dashboard:Title" | abpLocalization}}

+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
diff --git a/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.spec.ts b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.spec.ts new file mode 100644 index 00000000..5ec4ff8f --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DashboardComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.ts b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.ts new file mode 100644 index 00000000..11fb5481 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; +import { OrderService } from '../../lib/proxy/orders'; + +@Component({ + selector: 'app-dashboard', + templateUrl: './dashboard.component.html', + styleUrls: ['./dashboard.component.css'], +}) +export class DashboardComponent { + constructor(private service: OrderService) {} + + dashboard$ = this.service.getDashboard({}); +} diff --git a/apps/angular/projects/ordering/src/pages/dashboard/dashboard.module.ts b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.module.ts new file mode 100644 index 00000000..0a66aec3 --- /dev/null +++ b/apps/angular/projects/ordering/src/pages/dashboard/dashboard.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ProductRoutingModule } from '@eshoponabp/catalog'; +import { ThemeSharedModule } from '@abp/ng.theme.shared'; +import { CoreModule } from '@abp/ng.core'; +import { DashboardRoutingModule } from './dashboard-routing.module'; +import { DashboardComponent } from './dashboard.component'; +import { TopSellingComponent } from './components/top-selling/top-selling.component'; +import { OrderStatusChartComponent } from './components/order-status-chart/order-status-chart.component'; +import { ChartModule } from '@abp/ng.components/chart.js'; +import { PaymentsChartComponent } from './components/payments-chart/payments-chart.component'; + +@NgModule({ + declarations: [DashboardComponent, TopSellingComponent, OrderStatusChartComponent, PaymentsChartComponent], + imports: [ + DashboardRoutingModule, + CommonModule, + ProductRoutingModule, + CommonModule, + ThemeSharedModule, + CoreModule, + ChartModule, + ], +}) +export class DashboardModule {} diff --git a/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail-item/order-detail-item.component.ts b/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail-item/order-detail-item.component.ts index e6044526..ef5349b0 100644 --- a/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail-item/order-detail-item.component.ts +++ b/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail-item/order-detail-item.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; @Component({ - selector: 'lib-order-detail-item', + selector: 'app-order-detail-item', template: `
diff --git a/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.html b/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.html index 1e9e3b01..e855d244 100644 --- a/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.html +++ b/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.html @@ -7,33 +7,33 @@
- #{{ order.orderNo }}#{{ order.orderNo }} - {{ + {{ order.orderStatus - }} - {{ + }} + {{ order.buyer.name - }} - {{ + }} + {{ order.buyer.email - }} + }} - + {{ address.description }}
{{ address.street }}
{{ address.zipCode }}
{{ address.city }} / {{ address.country }}
-
- {{ + + {{ order.paymentMethod - }} - {{ + }} + {{ order.orderTotal | currency - }} + }}



diff --git a/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.ts b/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.ts index a38ef123..ae1de7cd 100644 --- a/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.ts +++ b/apps/angular/projects/ordering/src/pages/orders/order-detail/order-detail.component.ts @@ -3,7 +3,7 @@ import { OrderViewModel } from '../../../lib/order-view-model'; import { environment } from '../../../../../../src/environments/environment'; @Component({ - selector: 'lib-order-detail', + selector: 'app-order-detail', templateUrl: './order-detail.component.html', }) export class OrderDetailComponent { diff --git a/apps/angular/projects/ordering/src/pages/orders/orders.component.html b/apps/angular/projects/ordering/src/pages/orders/orders.component.html index d0c6963b..0d0adbfa 100644 --- a/apps/angular/projects/ordering/src/pages/orders/orders.component.html +++ b/apps/angular/projects/ordering/src/pages/orders/orders.component.html @@ -79,9 +79,9 @@
- - + diff --git a/apps/angular/projects/ordering/src/pages/orders/orders.component.ts b/apps/angular/projects/ordering/src/pages/orders/orders.component.ts index 680e10a4..1a37f9d1 100644 --- a/apps/angular/projects/ordering/src/pages/orders/orders.component.ts +++ b/apps/angular/projects/ordering/src/pages/orders/orders.component.ts @@ -6,7 +6,7 @@ import { ListService } from '@abp/ng.core'; import { eOrderingPolicyNames } from '@eshoponabp/ordering/config'; @Component({ - selector: 'lib-orders', + selector: 'app-orders', templateUrl: './orders.component.html', providers: [ListService], }) diff --git a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json index 2f586248..ffbfd570 100644 --- a/gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json +++ b/gateways/web-public/src/EShopOnAbp.WebPublicGateway/yarp.json @@ -56,7 +56,7 @@ "administrationCluster": { "Destinations": { "destination1": { - "Address": "http://localhost:44353" + "Address": "https://localhost:44353" } } }, diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/OrderItems/TopSellingDto.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/OrderItems/TopSellingDto.cs new file mode 100644 index 00000000..2e9599d4 --- /dev/null +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/OrderItems/TopSellingDto.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Application.Dtos; + +namespace EShopOnAbp.OrderingService.OrderItems +{ + public class TopSellingDto : EntityDto + { + public string ProductName { get; set; } + public string PictureUrl { get; set; } + public int Units { get; set; } + } +} diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/OrderingServiceRemoteServiceConsts.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/OrderingServiceRemoteServiceConsts.cs index ca7531ee..6259d93c 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/OrderingServiceRemoteServiceConsts.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/OrderingServiceRemoteServiceConsts.cs @@ -1,6 +1,6 @@ namespace EShopOnAbp.OrderingService { - public class OrderingServiceRemoteServiceConsts + public static class OrderingServiceRemoteServiceConsts { public const string RemoteServiceName = "Ordering"; } diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/DashboardDto.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/DashboardDto.cs new file mode 100644 index 00000000..2d44e0ee --- /dev/null +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/DashboardDto.cs @@ -0,0 +1,13 @@ +using EShopOnAbp.OrderingService.OrderItems; +using System.Collections.Generic; +using Volo.Abp.Application.Dtos; + +namespace EShopOnAbp.OrderingService.Orders +{ + public class DashboardDto: EntityDto + { + public List TopSellings { get; set; } + public List Payments { get; set; } + public List OrderStatusDto { get; set; } + } +} diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/DashboardInput.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/DashboardInput.cs new file mode 100644 index 00000000..63f83b08 --- /dev/null +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/DashboardInput.cs @@ -0,0 +1,7 @@ +namespace EShopOnAbp.OrderingService.Orders +{ + public class DashboardInput + { + public string Filter { get; set; } + } +} \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/IOrderAppService.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/IOrderAppService.cs index 169ab68a..03a1b7e5 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/IOrderAppService.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/IOrderAppService.cs @@ -16,5 +16,6 @@ public interface IOrderAppService : IApplicationService Task SetAsCancelledAsync(Guid id); Task SetAsShippedAsync(Guid id); Task> GetListPagedAsync(PagedAndSortedResultRequestDto input); + Task GetDashboardAsync(DashboardInput input); } \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/OrderStatusDto.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/OrderStatusDto.cs new file mode 100644 index 00000000..a8ee7f97 --- /dev/null +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/OrderStatusDto.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Application.Dtos; + +namespace EShopOnAbp.OrderingService.Orders; + +public class OrderStatusDto : EntityDto +{ + public int CountOfStatusOrder { get; set; } + public string OrderStatus { get; set; } + public decimal TotalIncome { get; set; } +} \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/PaymentDto.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/PaymentDto.cs new file mode 100644 index 00000000..8b59a15c --- /dev/null +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/PaymentDto.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Dtos; + +namespace EShopOnAbp.OrderingService.Orders; + +public class PaymentDto : EntityDto +{ + public decimal RateOfPaymentMethod { get; set; } + public string PaymentMethod { get; set; } +} \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissionDefinitionProvider.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissionDefinitionProvider.cs index f06acec2..ff8aca27 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissionDefinitionProvider.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissionDefinitionProvider.cs @@ -9,10 +9,12 @@ namespace EShopOnAbp.OrderingService.Permissions public override void Define(IPermissionDefinitionContext context) { var orderManagmentGroup = context.AddGroup(OrderingServicePermissions.GroupName, L("Permission:OrderingService")); + var oders = orderManagmentGroup.AddPermission(OrderingServicePermissions.Orders.Default, L("Permission:Orders")); oders.AddChild(OrderingServicePermissions.Orders.SetAsCancelled, L("Permission:Orders.SetAsCancelled")); oders.AddChild(OrderingServicePermissions.Orders.SetAsShipped, L("Permission:Orders.SetAsShipped")); + orderManagmentGroup.AddPermission(OrderingServicePermissions.Orders.Dashboard, L("Permission:Dashboard")); } private static LocalizableString L(string name) diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissions.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissions.cs index 30005b60..52b55b9e 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissions.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Permissions/OrderingServicePermissions.cs @@ -11,6 +11,7 @@ namespace EShopOnAbp.OrderingService.Permissions public const string Default = GroupName + ".Orders"; public const string SetAsCancelled = GroupName + ".SetAsCancelled"; public const string SetAsShipped = GroupName + ".SetAsShipped"; + public const string Dashboard = GroupName + ".Dashboard"; } public static string[] GetAll() diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application/OrderingServiceApplicationAutoMapperProfile.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application/OrderingServiceApplicationAutoMapperProfile.cs index 2153beb2..3209fded 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Application/OrderingServiceApplicationAutoMapperProfile.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application/OrderingServiceApplicationAutoMapperProfile.cs @@ -1,4 +1,5 @@ using AutoMapper; +using EShopOnAbp.OrderingService.OrderItems; using EShopOnAbp.OrderingService.Orders; using Volo.Abp.AutoMapper; @@ -10,9 +11,10 @@ namespace EShopOnAbp.OrderingService { CreateMap(); CreateMap(); - + CreateMap(); - + CreateMap(); + CreateMap() .Ignore(q => q.Address) .Ignore(q => q.Items) diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs b/services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs index 3e134693..dc87bef8 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs @@ -1,10 +1,12 @@ using EShopOnAbp.OrderingService.Localization; +using EShopOnAbp.OrderingService.OrderItems; using EShopOnAbp.OrderingService.Orders.Specifications; using EShopOnAbp.OrderingService.Permissions; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; @@ -61,7 +63,37 @@ public class OrderAppService : ApplicationService, IOrderAppService ); } - [AllowAnonymous] + [Authorize(OrderingServicePermissions.Orders.Dashboard)] + public async Task GetDashboardAsync(DashboardInput input) + { + return new DashboardDto() + { + TopSellings = await GetTopSellingAsync(input.Filter), + OrderStatusDto = await GetCountOfTotalOrderStatusAsync(input.Filter), + Payments = await GetPercentOfTotalPaymentAsync(input.Filter) + }; + } + private async Task> GetTopSellingAsync(string filter) + { + ISpecification specification = SpecificationFactory.Create(filter); + var orderItems = await _orderRepository.GetTopSelling(specification, true); + return ObjectMapper.Map, List>(orderItems); + } + + private async Task> GetPercentOfTotalPaymentAsync(string filter) + { + ISpecification specification = SpecificationFactory.Create(filter); + var orders = await _orderRepository.GetPercentOfTotalPayment(specification); + return CreatePaymentDtoMapping(orders); + } + + private async Task> GetCountOfTotalOrderStatusAsync(string filter) + { + ISpecification specification = SpecificationFactory.Create(filter); + var orders = await _orderRepository.GetCountOfTotalOrderStatus(specification, true); + return CreateOrderStatusDtoMapping(orders); + } + public async Task GetByOrderNoAsync(int orderNo) { var order = await _orderRepository.GetByOrderNoAsync(orderNo); @@ -149,4 +181,36 @@ public class OrderAppService : ApplicationService, IOrderAppService PaymentMethod = order.PaymentMethod }; } + + private List CreatePaymentDtoMapping(List orders) + { + var payments = orders + .GroupBy(p => p.PaymentMethod) + .Select(p => new PaymentDto { RateOfPaymentMethod = p.Count(), PaymentMethod = p.Key }) + .OrderBy(p => p.RateOfPaymentMethod) + .ToList(); + + var denominator = payments.Sum(p => p.RateOfPaymentMethod); + if (denominator != 0) + { + decimal rate = 100 / payments.Sum(p => p.RateOfPaymentMethod); + payments.ForEach(p => p.RateOfPaymentMethod *= rate); + } + return payments; + } + + private List CreateOrderStatusDtoMapping(List orders) + { + var orderStatus = orders + .GroupBy(p => p.OrderStatus) + .Select(p => new OrderStatusDto { CountOfStatusOrder = p.Count(), OrderStatus = p.Key.ToString(), }) + .OrderBy(p => p.CountOfStatusOrder) + .ToList(); + + decimal totalIncome = orders.Sum(p => p.OrderItems.Sum(p => p.Units * p.UnitPrice)); + + orderStatus.Add(new OrderStatusDto() { OrderStatus = "Total Income", TotalIncome = totalIncome }); + + return orderStatus; + } } \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/en.json b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/en.json index f3484dd7..dd4ed65e 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/en.json +++ b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/en.json @@ -12,9 +12,8 @@ "Permission:OrderingService": "Ordering Service", "Menu:Orders": "Orders", "Menu:OrderManagement": "Order Management", - + "Menu:Dashboard": "Dashboard", "ModalTitle":"Order Detail", - "DisplayName:OrderNo": "Order No", "DisplayName:OrderDate": "Order Date", "DisplayName:OrderTotal": "Order Total", @@ -30,14 +29,17 @@ "DisplayName:BuyerEmail": "Buyer E-mail", "DisplayName:Address": "Address", "DisplayName:PaymentMethod": "Payment Method", - "DisplayName:Total": "Total", - + "DisplayName:Total": "Total", "Permission:Orders.SetAsCancelled": "Set As Cancelled", "Permission:Orders.SetAsShipped": "Set As Shipped", - "Enum:OrderStatus:0":"Placed", "Enum:OrderStatus:1":"Paid", "Enum:OrderStatus:2":"Shipped", - "Enum:OrderStatus:3":"Cancelled" + "Enum:OrderStatus:3":"Cancelled", + "Permission:Dashboard": "Dashboard", + "Dashboard:Title": "Dashboard", + "Dashboard:OrderStatusChartTitle": "Order Status Chart", + "Dashboard:PaymentChartTitle": "Payments Chart", + "Dashboard:TopSellingCardTitle": "Top Selling" } } \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/tr.json b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/tr.json index 59f1b966..c9bad04f 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/tr.json +++ b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/tr.json @@ -38,6 +38,8 @@ "Enum:OrderStatus:0": "Oluşturuldu", "Enum:OrderStatus:1": "Ödendi", "Enum:OrderStatus:2": "Sevk Edildi", - "Enum:OrderStatus:3": "İptal Edildi" + "Enum:OrderStatus:3": "İptal Edildi", + + "Permission:Dashboard": "Gösterge Paneli" } } \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/zh-Hans.json b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/zh-Hans.json index 2aaafa54..53d85c07 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/zh-Hans.json +++ b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Localization/OrderingService/zh-Hans.json @@ -14,6 +14,7 @@ "Menu:Orders": "命令", "Menu:OrderManagement": "订单管理", "Permission:Orders.SetAsCancelled": "设置为取消", - "Permission:Orders.SetAsShipped": "设置为发货" + "Permission:Orders.SetAsShipped": "设置为发货", + "Permission:Dashboard": "仪表板" } } \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderConstants.cs b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderConstants.cs index 8bb2cb50..8c52f1bb 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderConstants.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderConstants.cs @@ -5,5 +5,5 @@ public static class OrderConstants public const int OrderPaymentMethodNameMaxLength = 128; public const int OrderStatusNameMaxLength = 256; public const int PaymentStatusMaxLength = 256; - + public const int Top10 = 10; } \ No newline at end of file diff --git a/services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/IOrderRepository.cs b/services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/IOrderRepository.cs index 71ea87dc..0e953790 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/IOrderRepository.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/IOrderRepository.cs @@ -20,6 +20,20 @@ public interface IOrderRepository : IRepository bool includeDetails = true, CancellationToken cancellationToken = default); + Task> GetTopSelling( + ISpecification spec, + bool includeDetails = true, + CancellationToken cancellationToken = default); + + Task> GetPercentOfTotalPayment( + ISpecification spec, + CancellationToken cancellationToken = default); + + Task> GetCountOfTotalOrderStatus( + ISpecification spec, + bool includeDetails = true, + CancellationToken cancellationToken = default); + Task GetByOrderNoAsync(int orderNo, bool includeDetails = true, CancellationToken cancellationToken = default); diff --git a/services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs b/services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs index 98349251..b717a372 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs @@ -46,6 +46,44 @@ public class EfCoreOrderRepository : EfCoreRepository> GetTopSelling( + ISpecification spec, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return (await (await GetDbSetAsync()) + .IncludeDetails(includeDetails) + .Where(spec.ToExpression()) + .SelectMany(oi => oi.OrderItems) + .GroupBy(p => p.ProductCode) + .OrderByDescending(o => o.Sum(p => p.Units)) + .Select(o => o.ToList()) + .ToListAsync()) + .SelectMany(t => t) + .Take(OrderConstants.Top10) + .ToList(); + } + + public async Task> GetPercentOfTotalPayment( + ISpecification spec, + CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .Where(spec.ToExpression()) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async Task> GetCountOfTotalOrderStatus( + ISpecification spec, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()) + .IncludeDetails(includeDetails) + .Where(spec.ToExpression()) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + public async Task GetByOrderNoAsync( int orderNo, bool includeDetails = true, diff --git a/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/OrderClientProxy.Generated.cs b/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/OrderClientProxy.Generated.cs index 26616094..d55571d7 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/OrderClientProxy.Generated.cs +++ b/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/OrderClientProxy.Generated.cs @@ -48,6 +48,14 @@ public partial class OrderClientProxy : ClientProxyBase, IOrde }); } + public virtual async Task GetDashboardAsync(DashboardInput input) + { + return await RequestAsync(nameof(GetDashboardAsync), new ClientProxyRequestTypeValue + { + { typeof(DashboardInput), input } + }); + } + public virtual async Task GetByOrderNoAsync(int orderNo) { return await RequestAsync(nameof(GetByOrderNoAsync), new ClientProxyRequestTypeValue diff --git a/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/ordering-generate-proxy.json b/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/ordering-generate-proxy.json index 37b59d11..9e10fa6b 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/ordering-generate-proxy.json +++ b/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Client/ClientProxies/ordering-generate-proxy.json @@ -195,6 +195,43 @@ "allowAnonymous": false, "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" }, + "GetDashboardAsyncByInput": { + "uniqueName": "GetDashboardAsyncByInput", + "name": "GetDashboardAsync", + "httpMethod": "GET", + "url": "api/ordering/order/dashboard", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "input", + "typeAsString": "EShopOnAbp.OrderingService.Orders.DashboardInput, EShopOnAbp.OrderingService.Application.Contracts", + "type": "EShopOnAbp.OrderingService.Orders.DashboardInput", + "typeSimple": "EShopOnAbp.OrderingService.Orders.DashboardInput", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "input", + "name": "Filter", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "ModelBinding", + "descriptorName": "input" + } + ], + "returnValue": { + "type": "EShopOnAbp.OrderingService.Orders.DashboardDto", + "typeSimple": "EShopOnAbp.OrderingService.Orders.DashboardDto" + }, + "allowAnonymous": null, + "implementFrom": "EShopOnAbp.OrderingService.Orders.IOrderAppService" + }, "GetByOrderNoAsyncByOrderNo": { "uniqueName": "GetByOrderNoAsyncByOrderNo", "name": "GetByOrderNoAsync", diff --git a/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json b/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json index 9def8b83..5d6b54e1 100644 --- a/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json +++ b/services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/appsettings.json @@ -1,7 +1,7 @@ { "App": { "SelfUrl": "https://localhost:44356", - "CorsOrigins": "https://localhost:44372,https://localhost:44373,https://localhost:4200" + "CorsOrigins": "https://localhost:44372,https://localhost:44373,http://localhost:4200" }, "AuthServer": { "Authority": "https://localhost:44330",