Browse Source

Merge pull request #33 from abpframework/ms-comm-demo

Created initial catalog and basket functionality
pull/38/head
Galip Tolga Erdem 5 years ago
committed by GitHub
parent
commit
3a97af4c27
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 41
      apps/angular/angular.json
  2. 21
      apps/angular/package.json
  3. 24
      apps/angular/projects/catalog/README.md
  4. 7
      apps/angular/projects/catalog/config/ng-package.json
  5. 12
      apps/angular/projects/catalog/config/src/catalog-config.module.ts
  6. 2
      apps/angular/projects/catalog/config/src/enums/index.ts
  7. 8
      apps/angular/projects/catalog/config/src/enums/policy-names.ts
  8. 4
      apps/angular/projects/catalog/config/src/enums/route-names.ts
  9. 1
      apps/angular/projects/catalog/config/src/providers/index.ts
  10. 31
      apps/angular/projects/catalog/config/src/providers/route.provider.ts
  11. 3
      apps/angular/projects/catalog/config/src/public-api.ts
  12. 44
      apps/angular/projects/catalog/karma.conf.js
  13. 7
      apps/angular/projects/catalog/ng-package.json
  14. 14
      apps/angular/projects/catalog/package.json
  15. 16
      apps/angular/projects/catalog/src/catalog-routing.module.ts
  16. 7
      apps/angular/projects/catalog/src/catalog.module.ts
  17. 17
      apps/angular/projects/catalog/src/lib/proxy/README.md
  18. 2221
      apps/angular/projects/catalog/src/lib/proxy/generate-proxy.json
  19. 2
      apps/angular/projects/catalog/src/lib/proxy/index.ts
  20. 3
      apps/angular/projects/catalog/src/lib/proxy/products/index.ts
  21. 24
      apps/angular/projects/catalog/src/lib/proxy/products/models.ts
  22. 58
      apps/angular/projects/catalog/src/lib/proxy/products/product.service.ts
  23. 27
      apps/angular/projects/catalog/src/lib/proxy/products/public-product.service.ts
  24. 1
      apps/angular/projects/catalog/src/pages/index.ts
  25. 3
      apps/angular/projects/catalog/src/pages/product/index.ts
  26. 22
      apps/angular/projects/catalog/src/pages/product/product-routing.module.ts
  27. 0
      apps/angular/projects/catalog/src/pages/product/product.component.css
  28. 167
      apps/angular/projects/catalog/src/pages/product/product.component.html
  29. 106
      apps/angular/projects/catalog/src/pages/product/product.component.ts
  30. 21
      apps/angular/projects/catalog/src/pages/product/product.module.ts
  31. 4
      apps/angular/projects/catalog/src/public-api.ts
  32. 28
      apps/angular/projects/catalog/src/test.ts
  33. 20
      apps/angular/projects/catalog/tsconfig.lib.json
  34. 10
      apps/angular/projects/catalog/tsconfig.lib.prod.json
  35. 17
      apps/angular/projects/catalog/tsconfig.spec.json
  36. 4
      apps/angular/src/app/app-routing.module.ts
  37. 12
      apps/angular/src/app/app.module.ts
  38. 13
      apps/angular/src/environments/environment.ts
  39. 10
      apps/angular/tsconfig.json
  40. 2391
      apps/angular/yarn.lock
  41. 8
      apps/public-web/src/EShopOnAbp.PublicWeb/BasketHub.cs
  42. 22
      apps/public-web/src/EShopOnAbp.PublicWeb/BasketProductEventHandler.cs
  43. 15
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetScriptContributor.cs
  44. 29
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetViewComponent.cs
  45. 41
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml
  46. 16
      apps/public-web/src/EShopOnAbp.PublicWeb/Controllers/WidgetsController.cs
  47. 4
      apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbp.PublicWeb.csproj
  48. 32
      apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbpPublicWebModule.cs
  49. 18
      apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenuContributor.cs
  50. 2
      apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenus.cs
  51. 62
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml
  52. 22
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.cs
  53. 17
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.js
  54. 30
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.css
  55. 8
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml
  56. 19
      apps/public-web/src/EShopOnAbp.PublicWeb/appsettings.json
  57. 3
      apps/public-web/src/EShopOnAbp.PublicWeb/package.json
  58. 48
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/client-proxies/basket-proxy.js
  59. 42
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.css
  60. 59
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.js
  61. 4938
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.js
  62. 1
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.js.map
  63. 17
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.min.js
  64. 1
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.min.js.map
  65. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/asus.jpg
  66. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/beats.jpg
  67. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/bluecat.jpg
  68. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/lego.jpg
  69. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/nikon.jpg
  70. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/noimage.jpg
  71. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/oki.jpg
  72. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/playstation.jpg
  73. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/rampage.jpg
  74. BIN
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/sunny.jpg
  75. 384
      apps/public-web/src/EShopOnAbp.PublicWeb/yarn.lock
  76. 5
      package.json
  77. 2
      services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/AdministrationServiceApplicationContractsModule.cs
  78. 2
      services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/EShopOnAbp.AdministrationService.Application.Contracts.csproj
  79. 12
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/AddProductDto.cs
  80. 14
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/BasketDto.cs
  81. 13
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/BasketItemDto.cs
  82. 12
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IBasketAppService.cs
  83. 10
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/RemoveProductDto.cs
  84. 116
      services/basket/src/EShopOnAbp.BasketService.Application/BasketAppService.cs
  85. 47
      services/basket/src/EShopOnAbp.BasketService.Application/BasketProductEventHandler.cs
  86. 36
      services/basket/src/EShopOnAbp.BasketService.Application/BasketProductService.cs
  87. 7
      services/basket/src/EShopOnAbp.BasketService.Application/BasketServiceApplicationAutoMapperProfile.cs
  88. 6
      services/basket/src/EShopOnAbp.BasketService.Application/BasketServiceApplicationModule.cs
  89. 1
      services/basket/src/EShopOnAbp.BasketService.Application/EShopOnAbp.BasketService.Application.csproj
  90. 12
      services/basket/src/EShopOnAbp.BasketService.Application/IBasketProductService.cs
  91. 9
      services/basket/src/EShopOnAbp.BasketService.Domain.Shared/BasketProductUpdatedEto.cs
  92. 8
      services/basket/src/EShopOnAbp.BasketService.Domain.Shared/OrderAcceptedEto.cs
  93. 9
      services/basket/src/EShopOnAbp.BasketService.Domain.Shared/OrderItemEto.cs
  94. 73
      services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs
  95. 20
      services/basket/src/EShopOnAbp.BasketService.Domain/BasketItem.cs
  96. 25
      services/basket/src/EShopOnAbp.BasketService.Domain/BasketRepository.cs
  97. 23
      services/basket/src/EShopOnAbp.BasketService.Domain/BasketServiceDomainModule.cs
  98. 1
      services/basket/src/EShopOnAbp.BasketService.Domain/EShopOnAbp.BasketService.Domain.csproj
  99. 12
      services/basket/src/EShopOnAbp.BasketService.Domain/IBasketRepository.cs
  100. 4
      services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs

41
apps/angular/angular.json

@ -51,15 +51,13 @@
"bundleName": "ngx-datatable-material"
},
{
"input": "node_modules/bootstrap/dist/css/bootstrap.rtl.min.css",
"input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css",
"inject": false,
"bundleName": "bootstrap-rtl.min"
},
{
"input": "node_modules/bootstrap/dist/css/bootstrap.min.css",
"inject": true,
"bundleName": "bootstrap-ltr.min"
},
"node_modules/@volo/ngx-lepton-x.lite/styles/sidemenu-layout.min.css",
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"src/styles.scss"
],
"scripts": [],
@ -158,6 +156,37 @@
}
}
}
},
"catalog": {
"projectType": "library",
"root": "projects/catalog",
"sourceRoot": "projects/catalog/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "projects/catalog/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "projects/catalog/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "projects/catalog/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/catalog/src/test.ts",
"tsConfig": "projects/catalog/tsconfig.spec.json",
"karmaConfig": "projects/catalog/karma.conf.js"
}
}
}
}
},
"defaultProject": "EShopOnAbp",

21
apps/angular/package.json

@ -12,14 +12,15 @@
},
"private": true,
"dependencies": {
"@abp/ng.account": "~5.0.0-rc.1",
"@abp/ng.components": "~5.0.0-rc.1",
"@abp/ng.core": "~5.0.0-rc.1",
"@abp/ng.identity": "~5.0.0-rc.1",
"@abp/ng.setting-management": "~5.0.0-rc.1",
"@abp/ng.tenant-management": "~5.0.0-rc.1",
"@abp/ng.theme.basic": "~5.0.0-rc.1",
"@abp/ng.theme.shared": "~5.0.0-rc.1",
"@abp/ng.account": "4.4.4",
"@abp/ng.components": "4.4.4",
"@abp/ng.core": "4.4.4",
"@abp/ng.identity": "4.4.4",
"@abp/ng.setting-management": "4.4.4",
"@abp/ng.tenant-management": "4.4.4",
"@abp/ng.theme.basic": "4.4.4",
"@abp/ng.theme.lepton-x": "^1.0.0-alpha.2",
"@abp/ng.theme.shared": "4.4.4",
"@angular/animations": "~12.2.8",
"@angular/common": "~12.2.8",
"@angular/compiler": "~12.2.8",
@ -34,7 +35,7 @@
"zone.js": "~0.11.4"
},
"devDependencies": {
"@abp/ng.schematics": "~5.0.0-rc.1",
"@abp/ng.schematics": "4.4.4",
"@angular-devkit/build-angular": "~12.2.8",
"@angular/cli": "~12.2.8",
"@angular/compiler-cli": "~12.2.8",
@ -56,4 +57,4 @@
"tslint": "~6.1.0",
"typescript": "~4.3.5"
}
}
}

24
apps/angular/projects/catalog/README.md

@ -0,0 +1,24 @@
# Catalog
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
## Code scaffolding
Run `ng generate component component-name --project catalog` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project catalog`.
> Note: Don't forget to add `--project catalog` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build catalog` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build catalog`, go to the dist folder `cd dist/catalog` and run `npm publish`.
## Running unit tests
Run `ng test catalog` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

7
apps/angular/projects/catalog/config/ng-package.json

@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/catalog/config",
"lib": {
"entryFile": "src/public-api.ts"
}
}

12
apps/angular/projects/catalog/config/src/catalog-config.module.ts

@ -0,0 +1,12 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { CATALOG_ROUTE_PROVIDERS } from './providers/route.provider';
@NgModule()
export class CatalogConfigModule {
static forRoot(): ModuleWithProviders<CatalogConfigModule> {
return {
ngModule: CatalogConfigModule,
providers: [CATALOG_ROUTE_PROVIDERS],
};
}
}

2
apps/angular/projects/catalog/config/src/enums/index.ts

@ -0,0 +1,2 @@
export * from './policy-names';
export * from './route-names';

8
apps/angular/projects/catalog/config/src/enums/policy-names.ts

@ -0,0 +1,8 @@
export const enum eCatalogPolicyNames {
Catalog = 'CatalogService.Products',
ProductManagement = 'CatalogService.Products',
ProductManagementCreate = 'CatalogService.Products.Create',
ProductManagementUpdate = 'CatalogService.Products.Update',
ProductManagementDelete = 'CatalogService.Products.Delete',
}

4
apps/angular/projects/catalog/config/src/enums/route-names.ts

@ -0,0 +1,4 @@
export const enum eCatalogRouteNames {
Catalog = 'CatalogService::Menu:CatalogManagement',
Products = 'CatalogService::Menu:Products',
}

1
apps/angular/projects/catalog/config/src/providers/index.ts

@ -0,0 +1 @@
export * from './route.provider';

31
apps/angular/projects/catalog/config/src/providers/route.provider.ts

@ -0,0 +1,31 @@
import { eLayoutType, RoutesService } from '@abp/ng.core';
import { APP_INITIALIZER } from '@angular/core';
import { eCatalogPolicyNames } from '../enums/policy-names';
import { eCatalogRouteNames } from '../enums/route-names';
export const CATALOG_ROUTE_PROVIDERS = [
{ provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true },
];
export function configureRoutes(routesService: RoutesService) {
return () => {
routesService.add([
{
path: '/catalog',
name: eCatalogRouteNames.Catalog,
layout: eLayoutType.application,
parentName: null,
iconClass: 'bi bi-collection-fill',
requiredPolicy: eCatalogPolicyNames.Catalog,
},
{
path: '/catalog/products',
name: eCatalogRouteNames.Products,
parentName: eCatalogRouteNames.Catalog,
order: 1,
requiredPolicy: eCatalogPolicyNames.ProductManagement,
iconClass: 'bi bi-tag-fill',
},
]);
};
}

3
apps/angular/projects/catalog/config/src/public-api.ts

@ -0,0 +1,3 @@
export * from './catalog-config.module';
export * from './enums';
export * from './providers';

44
apps/angular/projects/catalog/karma.conf.js

@ -0,0 +1,44 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, '../../coverage/catalog'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

7
apps/angular/projects/catalog/ng-package.json

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/catalog",
"lib": {
"entryFile": "src/public-api.ts"
}
}

14
apps/angular/projects/catalog/package.json

@ -0,0 +1,14 @@
{
"name": "@eshoponabp/catalog",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^12.2.0",
"@angular/core": "^12.2.0",
"@abp/ng.theme.shared": "~5.0.0-rc.2",
"@abp/ng.core": "~5.0.0-rc.2",
"@ngx-validate/core": "~0.1.1"
},
"dependencies": {
"tslib": "^2.3.0"
}
}

16
apps/angular/projects/catalog/src/catalog-routing.module.ts

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: 'products', pathMatch: 'full' },
{
path: 'products',
loadChildren: () => import('./pages/product/product.module').then(m => m.ProductModule),
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class CatalogRoutingModule {}

7
apps/angular/projects/catalog/src/catalog.module.ts

@ -0,0 +1,7 @@
import { NgModule } from '@angular/core';
import { CatalogRoutingModule } from './catalog-routing.module';
@NgModule({
imports: [CatalogRoutingModule],
})
export class CatalogModule {}

17
apps/angular/projects/catalog/src/lib/proxy/README.md

@ -0,0 +1,17 @@
# Proxy Generation Output
This directory includes the output of the latest proxy generation.
The files and folders in it will be overwritten when proxy generation is run again.
Therefore, please do not place your own content in this folder.
In addition, `generate-proxy.json` works like a lock file.
It includes information used by the proxy generator, so please do not delete or modify it.
Finally, the name of the files and folders should not be changed for two reasons:
- Proxy generator will keep creating them at those paths and you will have multiple copies of the same content.
- ABP Suite generates files which include imports from this folder.
> **Important Notice:** If you are building a module and are planning to publish to npm,
> some of the generated proxies are likely to be exported from public-api.ts file. In such a case,
> please make sure you export files directly and not from barrel exports. In other words,
> do not include index.ts exports in your public-api.ts exports.

2221
apps/angular/projects/catalog/src/lib/proxy/generate-proxy.json

File diff suppressed because it is too large

2
apps/angular/projects/catalog/src/lib/proxy/index.ts

@ -0,0 +1,2 @@
import * as Products from './products';
export { Products };

3
apps/angular/projects/catalog/src/lib/proxy/products/index.ts

@ -0,0 +1,3 @@
export * from './models';
export * from './product.service';
export * from './public-product.service';

24
apps/angular/projects/catalog/src/lib/proxy/products/models.ts

@ -0,0 +1,24 @@
import type { AuditedEntityDto } from '@abp/ng.core';
export interface CreateProductDto {
code: string;
name: string;
imageName?: string;
price: number;
stockCount: number;
}
export interface ProductDto extends AuditedEntityDto<string> {
code?: string;
name?: string;
imageName?: string;
price: number;
stockCount: number;
}
export interface UpdateProductDto {
name: string;
imageName?: string;
price: number;
stockCount: number;
}

58
apps/angular/projects/catalog/src/lib/proxy/products/product.service.ts

@ -0,0 +1,58 @@
import type { CreateProductDto, ProductDto, UpdateProductDto } from './models';
import { RestService } from '@abp/ng.core';
import type { ListResultDto, PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class ProductService {
apiName = 'Catalog';
create = (input: CreateProductDto) =>
this.restService.request<any, ProductDto>({
method: 'POST',
url: '/api/catalog/product',
body: input,
},
{ apiName: this.apiName });
delete = (id: string) =>
this.restService.request<any, void>({
method: 'DELETE',
url: `/api/catalog/product/${id}`,
},
{ apiName: this.apiName });
get = (id: string) =>
this.restService.request<any, ProductDto>({
method: 'GET',
url: `/api/catalog/product/${id}`,
},
{ apiName: this.apiName });
getList = () =>
this.restService.request<any, ListResultDto<ProductDto>>({
method: 'GET',
url: '/api/catalog/product',
},
{ apiName: this.apiName });
getListPaged = (input: PagedAndSortedResultRequestDto) =>
this.restService.request<any, PagedResultDto<ProductDto>>({
method: 'GET',
url: '/api/catalog/product/paged',
params: { sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount },
},
{ apiName: this.apiName });
update = (id: string, input: UpdateProductDto) =>
this.restService.request<any, ProductDto>({
method: 'PUT',
url: `/api/catalog/product/${id}`,
body: input,
},
{ apiName: this.apiName });
constructor(private restService: RestService) {}
}

27
apps/angular/projects/catalog/src/lib/proxy/products/public-product.service.ts

@ -0,0 +1,27 @@
import type { ProductDto } from './models';
import { RestService } from '@abp/ng.core';
import type { ListResultDto } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class PublicProductService {
apiName = 'Catalog';
get = (id: string) =>
this.restService.request<any, ProductDto>({
method: 'GET',
url: `/api/catalog/public-product/${id}`,
},
{ apiName: this.apiName });
getList = () =>
this.restService.request<any, ListResultDto<ProductDto>>({
method: 'GET',
url: '/api/catalog/public-product',
},
{ apiName: this.apiName });
constructor(private restService: RestService) {}
}

1
apps/angular/projects/catalog/src/pages/index.ts

@ -0,0 +1 @@
export * from './product';

3
apps/angular/projects/catalog/src/pages/product/index.ts

@ -0,0 +1,3 @@
export * from './product-routing.module';
export * from './product.component';
export * from './product.module';

22
apps/angular/projects/catalog/src/pages/product/product-routing.module.ts

@ -0,0 +1,22 @@
import { AuthGuard, PermissionGuard } from '@abp/ng.core';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { eCatalogPolicyNames } from '@eshoponabp/catalog/config';
import { ProductComponent } from './product.component';
const routes: Routes = [
{
path: '',
component: ProductComponent,
canActivate: [AuthGuard, PermissionGuard],
data: {
requiredPolicy: eCatalogPolicyNames.ProductManagement,
},
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProductRoutingModule {}

0
apps/angular/projects/catalog/src/pages/product/product.component.css

167
apps/angular/projects/catalog/src/pages/product/product.component.html

@ -0,0 +1,167 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">{{ 'CatalogService::Products' | abpLocalization }}</h5>
</div>
<div class="text-right col col-md-6">
<button class="btn btn-primary" (click)="onCreate()" *abpPermission="permissions.create">
{{ 'CatalogService::NewProduct' | abpLocalization }}
</button>
</div>
</div>
</div>
<div class="card-body">
<ngx-datatable [rows]="items" [count]="count" [list]="list" default>
<ngx-datatable-column
[name]="'AbpUi::Actions' | abpLocalization"
[sortable]="false"
*abpPermission="permissions.update + ' || ' + permissions.delete"
>
<ng-template let-row="row" ngx-datatable-cell-template>
<div ngbDropdown container="body" class="d-inline-block">
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
>
<i class="mr-1 fa fa-cog"></i>{{ 'AbpUi::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
class="dropdown-item"
(click)="onEdit(row)"
*abpPermission="permissions.update"
>
{{ 'AbpUi::Edit' | abpLocalization }}
</button>
<button
class="dropdown-item"
(click)="onDelete(row)"
*abpPermission="permissions.delete"
>
{{ 'AbpUi::Delete' | abpLocalization }}
</button>
</div>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'CatalogService::DisplayName:Code' | abpLocalization"
prop="code"
></ngx-datatable-column>
<ngx-datatable-column
[name]="'CatalogService::DisplayName:Name' | abpLocalization"
prop="name"
></ngx-datatable-column>
<ngx-datatable-column
[name]="'CatalogService::DisplayName:ImageName' | abpLocalization"
prop="imageName"
></ngx-datatable-column>
<ngx-datatable-column
[name]="'CatalogService::DisplayName:Price' | abpLocalization"
prop="price"
></ngx-datatable-column>
<ngx-datatable-column
[name]="'CatalogService::DisplayName:StockCount' | abpLocalization"
prop="stockCount"
></ngx-datatable-column>
</ngx-datatable>
</div>
</div>
<abp-modal [(visible)]="isModalVisible" [busy]="modalBusy">
<ng-template #abpHeader>
<h3>
{{ (selected?.id ? 'AbpUi::Edit' : 'CatalogService::NewProduct') | abpLocalization }}
</h3>
</ng-template>
<ng-template #abpBody>
<ng-template #loaderRef
><div class="text-center"><i class="fa fa-pulse fa-spinner"></i></div
></ng-template>
<form *ngIf="form; else loaderRef" [formGroup]="form" (ngSubmit)="save()">
<div class="mb-3">
<label class="form-label" for="code">
{{ 'CatalogService::DisplayName:Code' | abpLocalization }} *
</label>
<input
type="text"
class="form-control"
id="code"
formControlName="code"
[readonly]="selected?.id"
[placeholder]="'CatalogService::DisplayName:Code' | abpLocalization"
/>
</div>
<div class="mb-3">
<label class="form-label" for="name">
{{ 'CatalogService::DisplayName:Name' | abpLocalization }} *
</label>
<input
type="text"
class="form-control"
id="name"
formControlName="name"
[placeholder]="'CatalogService::DisplayName:Name' | abpLocalization"
/>
</div>
<div class="mb-3">
<label class="form-label" for="imageName">
{{ 'CatalogService::DisplayName:ImageName' | abpLocalization }} *
</label>
<input
type="text"
class="form-control"
id="imageName"
formControlName="imageName"
[placeholder]="'CatalogService::DisplayName:ImageName' | abpLocalization"
/>
</div>
<div class="mb-3">
<label class="form-label" for="price">
{{ 'CatalogService::DisplayName:Price' | abpLocalization }} *
</label>
<input
type="number"
class="form-control"
id="price"
formControlName="price"
[placeholder]="'CatalogService::DisplayName:Price' | abpLocalization"
/>
</div>
<div class="mb-3">
<label class="form-label" for="stockCount">
{{ 'CatalogService::DisplayName:StockCount' | abpLocalization }} *
</label>
<input
type="number"
class="form-control"
id="stockCount"
formControlName="stockCount"
[placeholder]="'CatalogService::DisplayName:StockCount' | abpLocalization"
/>
</div>
</form>
<ng-template #loaderRef
><div class="text-center"><i class="fa fa-pulse fa-spinner"></i></div
></ng-template>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" abpClose>
{{ 'AbpUi::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" [disabled]="form?.invalid" (click)="save()">{{
'AbpUi::Save' | abpLocalization
}}</abp-button>
</ng-template>
</abp-modal>

106
apps/angular/projects/catalog/src/pages/product/product.component.ts

@ -0,0 +1,106 @@
import { ListService } from '@abp/ng.core';
import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { eCatalogPolicyNames } from '@eshoponabp/catalog/config';
import { finalize } from 'rxjs/operators';
import { ProductDto } from '../../lib/proxy/products/models';
import { ProductService } from '../../lib/proxy/products/product.service';
@Component({
selector: 'lib-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css'],
providers: [ListService],
})
export class ProductComponent implements OnInit {
permissions = {
create: eCatalogPolicyNames.ProductManagementCreate,
update: eCatalogPolicyNames.ProductManagementUpdate,
delete: eCatalogPolicyNames.ProductManagementDelete,
};
items: ProductDto[] = [];
count = 0;
selected: ProductDto;
isModalVisible: boolean;
modalBusy = false;
form: FormGroup;
constructor(
public readonly list: ListService,
private readonly service: ProductService,
private confirmationService: ConfirmationService,
private fb: FormBuilder
) {
this.list.maxResultCount = 10;
}
ngOnInit(): void {
const productStreamCreator = query => this.service.getListPaged(query);
this.list.hookToQuery(productStreamCreator).subscribe(response => {
this.items = response.items;
this.count = response.totalCount;
});
}
buildForm() {
this.form = this.fb.group({
name: [this.selected.name, { validators: [Validators.required] }],
code: [this.selected.code, { validators: [Validators.required] }],
imageName: [this.selected.imageName, { validators: [Validators.required] }],
price: [this.selected.price, { validators: [Validators.required] }],
stockCount: [this.selected.stockCount, { validators: [Validators.required] }],
});
}
onEdit(product: ProductDto) {
this.selected = product;
this.openModal();
}
onCreate() {
this.selected = {} as ProductDto;
this.openModal();
}
openModal() {
this.isModalVisible = true;
this.buildForm();
}
onDelete(product: ProductDto) {
this.confirmationService
.warn('CatalogService::ProductDeletionConfirmationMessage', 'AbpUi::AreYouSure', {
messageLocalizationParams: [product.name],
})
.subscribe((status: Confirmation.Status) => {
if (status === Confirmation.Status.confirm) {
this.service.delete(product.id).subscribe(() => this.list.get());
}
});
}
save() {
if (!this.form.valid || this.modalBusy) return;
this.modalBusy = true;
const { id } = this.selected;
(id
? this.service.update(id, {
...this.selected,
...this.form.value,
})
: this.service.create({ ...this.form.value })
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.isModalVisible = false;
this.list.get();
});
}
}

21
apps/angular/projects/catalog/src/pages/product/product.module.ts

@ -0,0 +1,21 @@
import { CoreModule } from '@abp/ng.core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgxValidateCoreModule } from '@ngx-validate/core';
import { ProductRoutingModule } from './product-routing.module';
import { ProductComponent } from './product.component';
@NgModule({
declarations: [ProductComponent],
imports: [
CommonModule,
ProductRoutingModule,
ThemeSharedModule,
CoreModule,
NgbDropdownModule,
NgxValidateCoreModule,
],
})
export class ProductModule {}

4
apps/angular/projects/catalog/src/public-api.ts

@ -0,0 +1,4 @@
export * from './catalog-routing.module';
export * from './catalog.module';
export * from './lib/proxy/products';
export * from './pages';

28
apps/angular/projects/catalog/src/test.ts

@ -0,0 +1,28 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
{ teardown: { destroyAfterEach: true }},
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

20
apps/angular/projects/catalog/tsconfig.lib.json

@ -0,0 +1,20 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

10
apps/angular/projects/catalog/tsconfig.lib.prod.json

@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

17
apps/angular/projects/catalog/tsconfig.spec.json

@ -0,0 +1,17 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

4
apps/angular/src/app/app-routing.module.ts

@ -25,6 +25,10 @@ const routes: Routes = [
loadChildren: () =>
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
},
{
path: 'catalog',
loadChildren: () => import('@eshoponabp/catalog').then(m => m.CatalogModule),
},
];
@NgModule({

12
apps/angular/src/app/app.module.ts

@ -4,11 +4,15 @@ import { registerLocale } from '@abp/ng.core/locale';
import { IdentityConfigModule } from '@abp/ng.identity/config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config';
import { ThemeBasicModule } from '@abp/ng.theme.basic';
import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x';
import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account';
import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CatalogConfigModule } from '@eshoponabp/catalog/config';
import { NgxsModule } from '@ngxs/store';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@ -28,7 +32,11 @@ import { APP_ROUTE_PROVIDER } from './route.provider';
IdentityConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
SettingManagementConfigModule.forRoot(),
ThemeBasicModule.forRoot(),
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
AccountLayoutModule.forRoot(),
CatalogConfigModule.forRoot(),
NgxsModule.forRoot(),
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER],

13
apps/angular/src/environments/environment.ts

@ -12,15 +12,18 @@ export const environment = {
issuer: 'https://localhost:44330',
redirectUri: baseUrl,
clientId: 'Web',
responseType: 'code',
scope:
'offline_access openid profile email phone IdentityService AdministrationService',
requireHttps: true,
//responseType: 'code',
scope: 'offline_access openid profile email phone IdentityService AdministrationService',
//requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44372',
rootNamespace: 'EShopOnAbp',
}
},
Catalog: {
url: 'https://localhost:44354',
rootNamespace: 'EShopOnAbp.CatalogService',
},
},
} as Environment;

10
apps/angular/tsconfig.json

@ -14,8 +14,14 @@
"typeRoots": ["node_modules/@types"],
"lib": ["es2018", "dom"],
"paths": {
"@proxy": ["src/app/proxy/index.ts"],
"@proxy/*": ["src/app/proxy/*"]
"@eshoponabp/catalog/config": [
"node_modules/@eshoponabp/catalog/config",
"projects/catalog/config/src/public-api.ts"
],
"@eshoponabp/catalog": [
"node_modules/@eshoponabp/catalog",
"projects/catalog/src/public-api.ts",
],
}
},
"angularCompilerOptions": {

2391
apps/angular/yarn.lock

File diff suppressed because it is too large

8
apps/public-web/src/EShopOnAbp.PublicWeb/BasketHub.cs

@ -0,0 +1,8 @@
using Volo.Abp.AspNetCore.SignalR;
namespace EShopOnAbp.PublicWeb;
public class BasketHub : AbpHub
{
}

22
apps/public-web/src/EShopOnAbp.PublicWeb/BasketProductEventHandler.cs

@ -0,0 +1,22 @@
using System.Threading.Tasks;
using EShopOnAbp.BasketService;
using Microsoft.AspNetCore.SignalR;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
namespace EShopOnAbp.PublicWeb;
public class BasketProductEventHandler : IDistributedEventHandler<BasketProductUpdatedEto>, ITransientDependency
{
private readonly IHubContext<BasketHub> _hubContext;
public BasketProductEventHandler(IHubContext<BasketHub> hubContext)
{
_hubContext = hubContext;
}
public async Task HandleEventAsync(BasketProductUpdatedEto eventData)
{
await _hubContext.Clients.All.SendAsync("BasketProductUpdated", eventData);
}
}

15
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetScriptContributor.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR;
using Volo.Abp.Modularity;
namespace EShopOnAbp.PublicWeb.Components.Basket;
[DependsOn(typeof(SignalRBrowserScriptContributor))]
public class BasketWidgetScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/components/basket-widget.js");
}
}

29
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetViewComponent.cs

@ -0,0 +1,29 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using EShopOnAbp.BasketService;
namespace EShopOnAbp.PublicWeb.Components.Basket;
[Widget(
AutoInitialize = true,
RefreshUrl = "/Widgets/Basket",
StyleFiles = new[] { "/components/basket-widget.css" },
ScriptTypes = new[] { typeof(BasketWidgetScriptContributor) }
)]
public class BasketWidgetViewComponent : AbpViewComponent
{
private readonly IBasketAppService _basketAppService;
public BasketWidgetViewComponent(IBasketAppService basketAppService)
{
_basketAppService = basketAppService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var basketDto = await _basketAppService.GetAsync();
return View("~/Components/Basket/Default.cshtml", basketDto);
}
}

41
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml

@ -0,0 +1,41 @@
@model EShopOnAbp.BasketService.BasketDto
<div>
@if (!Model.Items.Any())
{
<div class="basket-list">
<p class="text-center"><strong><em>Your basket is empty. Click to a product to add it!</em></strong></p>
</div>
}
else
{
<div class="basket-list">
@foreach (var item in Model.Items)
{
<div class="basket-list-item" data-product-id="@item.ProductId">
@if (!item.ImageName.IsNullOrEmpty())
{
<img src="/product-images/@item.ImageName"/>
}
else
{
<img src="/product-images/noimage.jpg"/>
}
@item.ProductName
@if (item.Count > 1)
{
<text> (@("x" + item.Count))</text>
}
- $@item.TotalPrice.ToString("0.00")
<i class="fa fa-remove basket-item-remove" title="Remove from basket"></i>
</div>
}
<hr/>
<div class="basket-total-area">
Total: $@Model.TotalPrice.ToString("0.00")
</div>
<div>
<abp-button class="basket-purchase-button" text="Purchase" button-type="Primary" />
</div>
</div>
}
</div>

16
apps/public-web/src/EShopOnAbp.PublicWeb/Controllers/WidgetsController.cs

@ -0,0 +1,16 @@
using EShopOnAbp.PublicWeb.Components.Basket;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace EShopOnAbp.PublicWeb.Controllers;
[Route("Widgets")]
public class WidgetsController : AbpController
{
[HttpGet]
[Route("Basket")]
public IActionResult Counters()
{
return ViewComponent(typeof(BasketWidgetViewComponent));
}
}

4
apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbp.PublicWeb.csproj

@ -6,6 +6,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.0" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
@ -17,11 +18,14 @@
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.Account.HttpApi" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.Account.HttpApi.Client" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.AspNetCore.SignalR" Version="5.0.0-rc.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\shared\EShopOnAbp.Shared.Hosting.AspNetCore\EShopOnAbp.Shared.Hosting.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\..\shared\EShopOnAbp.Shared.Localization\EShopOnAbp.Shared.Localization.csproj" />
<ProjectReference Include="..\..\..\..\services\catalog\src\EShopOnAbp.CatalogService.HttpApi.Client\EShopOnAbp.CatalogService.HttpApi.Client.csproj" />
<ProjectReference Include="..\..\..\..\services\basket\src\EShopOnAbp.BasketService.HttpApi.Client\EShopOnAbp.BasketService.HttpApi.Client.csproj" />
<!-- <ProjectReference Include="..\..\..\..\services\product\src\MyCompanyName.MyProjectName.ProductService.HttpApi.Client\MyCompanyName.MyProjectName.ProductService.HttpApi.Client.csproj" />-->
</ItemGroup>

32
apps/public-web/src/EShopOnAbp.PublicWeb/EShopOnAbpPublicWebModule.cs

@ -1,9 +1,14 @@
using System;
using System.Net.Http.Headers;
using EShopOnAbp.BasketService;
using EShopOnAbp.CatalogService;
using EShopOnAbp.Localization;
using EShopOnAbp.PublicWeb.Menus;
using EShopOnAbp.Shared.Hosting.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
@ -16,6 +21,7 @@ using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars;
using Volo.Abp.AspNetCore.SignalR;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.EventBus.RabbitMq;
@ -24,6 +30,7 @@ using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Yarp.ReverseProxy.Transforms;
namespace EShopOnAbp.PublicWeb
{
@ -37,8 +44,10 @@ namespace EShopOnAbp.PublicWeb
typeof(AbpAccountHttpApiModule),
typeof(AbpAccountHttpApiClientModule),
typeof(EShopOnAbpSharedHostingAspNetCoreModule),
typeof(EShopOnAbpSharedLocalizationModule)
// typeof(ProductServiceHttpApiClientModule)
typeof(EShopOnAbpSharedLocalizationModule),
typeof(CatalogServiceHttpApiClientModule),
typeof(BasketServiceHttpApiClientModule),
typeof(AbpAspNetCoreSignalRModule)
)]
public class EShopOnAbpPublicWebModule : AbpModule
{
@ -99,7 +108,8 @@ namespace EShopOnAbp.PublicWeb
options.Scope.Add("email");
options.Scope.Add("phone");
options.Scope.Add("AdministrationService");
// options.Scope.Add("ProductService");
options.Scope.Add("BasketService");
options.Scope.Add("CatalogService");
});
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
@ -117,6 +127,21 @@ namespace EShopOnAbp.PublicWeb
{
options.Contributors.Add(new EShopOnAbpPublicWebToolbarContributor());
});
context.Services
.AddReverseProxy()
.LoadFromConfig(configuration.GetSection("ReverseProxy"))
.AddTransforms(builderContext =>
{
builderContext.AddRequestTransform(async (transformContext) =>
{
transformContext.ProxyRequest.Headers
.Authorization = new AuthenticationHeaderValue(
"Bearer",
await transformContext.HttpContext.GetTokenAsync("access_token")
);
});
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -151,6 +176,7 @@ namespace EShopOnAbp.PublicWeb
app.UseAuthorization();
app.UseConfiguredEndpoints(endpoints =>
{
endpoints.MapReverseProxy();
// endpoints.MapMetrics();
});
}

18
apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenuContributor.cs

@ -45,15 +45,15 @@ namespace EShopOnAbp.PublicWeb.Menus
)
);
// Products
// context.Menu.AddItem(
// new ApplicationMenuItem(
// EShopOnAbpPublicWebMenus.ProductPage,
// "Products",
// "/Products",
// icon: "fa fa-product-hunt",
// order: 1
// )
// );
context.Menu.AddItem(
new ApplicationMenuItem(
EShopOnAbpPublicWebMenus.Catalog,
l["Catalog"],
"/Catalog",
icon: "fa fa-product-hunt",
order: 1
)
);
return Task.CompletedTask;
}

2
apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenus.cs

@ -5,6 +5,6 @@
private const string Prefix = "EShopOnAbp.Public";
public const string HomePage = Prefix + ".HomePage";
// public const string ProductPage = Prefix + ".ProductPage";
public const string Catalog = Prefix + ".Catalog";
}
}

62
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml

@ -0,0 +1,62 @@
@page
@using EShopOnAbp.PublicWeb.Components.Basket
@model EShopOnAbp.PublicWeb.Pages.Catalog
@{
const int productsColumnSize = 2;
}
@section styles{
<abp-style src="/Pages/Catalog.css"/>
}
@section scripts {
<abp-script src="/client-proxies/basket-proxy.js" />
<abp-script src="/Pages/Catalog.cshtml.js" />
}
<div class="row">
<div class="col-lg-9">
<h3 class="pt-5 pb-4 text-center">Our Products</h3>
<div class="product-list">
@for (int i = 0; i < Math.Ceiling(Model.Products.Count / (double)productsColumnSize); i++)
{
<abp-row>
@for (int j = 0; j < productsColumnSize; j++)
{
<abp-column size-lg="_6" size-sm="_12">
@if ((i * productsColumnSize) + j < Model.Products.Count)
{
var product = Model.Products[(i * productsColumnSize) + j];
<div class="product-list-item" data-product-id="@product.Id.ToString()">
@if (!product.ImageName.IsNullOrEmpty())
{
<img src="/product-images/@product.ImageName"/>
}
else
{
<img src="/product-images/noimage.jpg"/>
}
<div class="row mt-5">
<div class="col">
<div class="product-info-box">
<h6>@product.Name</h6>
<em class="d-block text-muted">@product.Code - Stock count: @product.StockCount</em>
</div>
</div>
<div class="col-auto text-right text-danger">
<strong class="product-price">$@product.Price</strong>
</div>
</div>
</div>
}
</abp-column>
}
</abp-row>
}
</div>
</div>
<div class="col-lg-3">
<h3 class="pt-5 pb-4 text-center">Your Basket</h3>
<div id="BasketArea">
@await Component.InvokeAsync(typeof(BasketWidgetViewComponent))
</div>
</div>
</div>

22
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.cs

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
using EShopOnAbp.CatalogService.Products;
namespace EShopOnAbp.PublicWeb.Pages;
public class Catalog : PageModel
{
public IReadOnlyList<ProductDto> Products { get; private set; }
private readonly IPublicProductAppService _productAppService;
public Catalog(IPublicProductAppService productAppService)
{
_productAppService = productAppService;
}
public async Task OnGetAsync()
{
Products = (await _productAppService.GetListAsync()).Items;
}
}

17
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.js

@ -0,0 +1,17 @@
(function(){
$(function(){
var basketWidget = new abp.WidgetManager('#BasketArea');
$('.product-list-item').click(function(){
var $this = $(this);
var productId = $this.attr('data-product-id');
eShopOnAbp.basketService.basket.addProduct({
productId: productId
}).then(function(){
basketWidget.refresh();
abp.notify.success("Added product to your basket.", "Successfully added");
});
});
});
})();

30
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.css

@ -0,0 +1,30 @@
body {
background-color: #f9f9f9;
}
.product-list-item {
background-color: #fff;
padding: 2em;
border: 1px solid #eee;
border-radius: 3px;
box-shadow: 0 0 10px rgba(0, 0, 0, .05);
margin-bottom: 2em;
transition: all .2s;
}
.product-list-item:hover {
box-shadow: 0 0 20px rgba(0, 0, 0, .1);
cursor: pointer;
}
.product-list-item img {
width: 100%;
}
.product-list-item .text-muted {
opacity: .8
}
.product-info-box {
min-height: 80px;
}

8
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml

@ -17,7 +17,7 @@
<i class="fas fa-chess-rook fa-4x text-primary d-block my-3"></i>
<h2>Heading</h2>
<p class="lead">Vestibulum id ligula porta.</p>
<p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p>
<p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p>
<p><a class="btn btn-outline-primary" href="#" role="button">View details »</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4 py-4">
@ -43,7 +43,7 @@
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p>
</div>
<div class="col-md-6">
<img src="https://i.picsum.photos/id/59/600/400.jpg?hmac=hCS_vJsQfuK1hUlwEUlGbUAnb_pFljGkD-2jdYxB1y8" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;" />
<img src="https://i.picsum.photos/id/59/600/400.jpg?hmac=hCS_vJsQfuK1hUlwEUlGbUAnb_pFljGkD-2jdYxB1y8" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/>
</div>
</div>
@ -54,7 +54,7 @@
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p>
</div>
<div class="col-md-6 order-md-1">
<img src="https://i.picsum.photos/id/437/600/400.jpg?hmac=JNv4AQJrQn53c1Xh1C7q0NBNoJgRXpdKtzIDJ1w2lqA" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;" />
<img src="https://i.picsum.photos/id/437/600/400.jpg?hmac=JNv4AQJrQn53c1Xh1C7q0NBNoJgRXpdKtzIDJ1w2lqA" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/>
</div>
</div>
@ -65,7 +65,7 @@
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p>
</div>
<div class="col-md-6">
<img src="https://i.picsum.photos/id/835/600/400.jpg?hmac=HR6Bi8aji8auDURA6j9xiZC6QgcFwwBTMJ8X4Cf6bPg" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;" />
<img src="https://i.picsum.photos/id/835/600/400.jpg?hmac=HR6Bi8aji8auDURA6j9xiZC6QgcFwwBTMJ8X4Cf6bPg" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/>
</div>
</div>

19
apps/public-web/src/EShopOnAbp.PublicWeb/appsettings.json

@ -40,5 +40,24 @@
"RequireHttpsMetadata": "true",
"ClientId": "PublicWeb",
"ClientSecret": "1q2w3e*"
},
"ReverseProxy": {
"Routes": {
"route1" : {
"ClusterId": "cluster1",
"Match": {
"Path": "/api/{**anypath}"
}
}
},
"Clusters": {
"cluster1": {
"Destinations": {
"destination1": {
"Address": "https://localhost:44373/"
}
}
}
}
}
}

3
apps/public-web/src/EShopOnAbp.PublicWeb/package.json

@ -3,6 +3,7 @@
"name": "EShopOnAbpPublicWeb",
"private": true,
"dependencies": {
"@abp/aspnetcore.mvc.ui.theme.basic": "~5.0.0-rc.1"
"@abp/aspnetcore.mvc.ui.theme.basic": "~5.0.0-rc.1",
"@abp/signalr": "~5.0.0-rc.1"
}
}

48
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/client-proxies/basket-proxy.js

@ -0,0 +1,48 @@
/* This file is automatically generated by ABP framework to use MVC Controllers from javascript. */
// module basket
(function(){
// controller eShopOnAbp.basketService.basket
(function(){
abp.utils.createNamespace(window, 'eShopOnAbp.basketService.basket');
eShopOnAbp.basketService.basket.get = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/basket/basket',
type: 'GET'
}, ajaxParams));
};
eShopOnAbp.basketService.basket.addProduct = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/basket/basket/product',
type: 'POST',
data: JSON.stringify(input)
}, ajaxParams));
};
eShopOnAbp.basketService.basket.removeProduct = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/basket/basket/product' + abp.utils.buildQueryString([{ name: 'productId', value: input.productId }, { name: 'count', value: input.count }]) + '',
type: 'DELETE'
}, ajaxParams));
};
eShopOnAbp.basketService.basket.purchase = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/basket/basket/purchase',
type: 'POST',
dataType: null
}, ajaxParams));
};
})();
})();

42
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.css

@ -0,0 +1,42 @@
.basket-list
{
background-color: #fff;
padding: 1em;
border: 1px solid #666;
border-radius: 3px;
box-shadow: 0 0 10px rgba(0, 0, 0, .05);
margin-bottom: 0.5em;
transition: all .2s;
}
.basket-list:hover {
box-shadow: 0 0 20px rgba(0, 0, 0, .1);
}
.basket-list-item {
margin-top: 1em;
}
.basket-list-item:first-child{
margin-top: 0;
}
.basket-list-item img {
height: 1em;
}
.basket-total-area
{
margin-bottom: 1em;
font-weight: bold;
}
.basket-item-remove {
cursor: pointer;
color: red;
opacity: 0.25;
}
.basket-list-item:hover .basket-item-remove {
opacity: 1.0;
}

59
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.js

@ -0,0 +1,59 @@
(function (){
abp.widgets.BasketWidget = function ($wrapper) {
var widgetManager = $wrapper.data('abp-widget-manager');
var init = function (filters) {
$wrapper
.find('.basket-item-remove')
.click(function(){
var $this = $(this);
var productId = $this.parent('.basket-list-item').attr('data-product-id');
eShopOnAbp.basketService.basket.removeProduct({
productId: productId
}).then(function(){
widgetManager.refresh();
abp.notify.info("Removed the product from your basket.", "Removed basket item");
});
});
$wrapper
.find('.basket-purchase-button')
.click(function(){
eShopOnAbp.basketService.basket.purchase().then(function(){
widgetManager.refresh();
abp.notify.info("Thank you for the payment!", "Purchase completed!");
});
});
};
return {
init: init
};
};
var connection = new signalR.HubConnectionBuilder()
.withUrl("/signalr-hubs/basket")
.build();
connection.on("BasketProductUpdated", function (data) {
$('.basket-list')
.closest('.abp-widget-wrapper')
.each(function(){
var $wrapper = $(this);
if ($wrapper.find('[data-product-id=' + data.productId + ']').length){
var widgetManager = new abp.WidgetManager({
wrapper: $wrapper
});
widgetManager.refresh();
abp.notify.info('The product "' + data.productName + '" has been changed!', 'Your basket has been updated!');
}
});
});
connection.start().then(function () {
}).catch(function (err) {
return console.error(err.toString());
});
})();

4938
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.js

File diff suppressed because it is too large

1
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.js.map

File diff suppressed because one or more lines are too long

17
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.min.js

File diff suppressed because one or more lines are too long

1
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/libs/signalr/browser/signalr.min.js.map

File diff suppressed because one or more lines are too long

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/asus.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/beats.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/bluecat.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/lego.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/nikon.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/noimage.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/oki.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/playstation.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/rampage.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/product-images/sunny.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

384
apps/public-web/src/EShopOnAbp.PublicWeb/yarn.lock

@ -64,6 +64,13 @@
dependencies:
"@abp/utils" "~5.0.0-rc.1"
"@abp/core@~5.0.0-rc.2":
version "5.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@abp/core/-/core-5.0.0-rc.2.tgz#44e773fe2b6e17b037859cb99e14dbd17a3ec1cd"
integrity sha512-sdxKIEnIxnVXmai2zPvrMsF4t8CZ5K7p5yw64QsuGfy2jMOz+wWrzS6Zbhv8aUdgwvHy/d6GyNYRV///Kxel8g==
dependencies:
"@abp/utils" "~5.0.0-rc.2"
"@abp/datatables.net-bs5@~5.0.0-rc.1":
version "5.0.0-rc.1"
resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs5/-/datatables.net-bs5-5.0.0-rc.1.tgz#45998d0469ee79eb006973981b26b541cd1bc6d0"
@ -152,6 +159,14 @@
"@abp/core" "~5.0.0-rc.1"
select2 "^4.0.13"
"@abp/signalr@~5.0.0-rc.1":
version "5.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@abp/signalr/-/signalr-5.0.0-rc.2.tgz#bb99e65f547cee8032ccfd86a9239dea4c40330d"
integrity sha512-Tm7EjgTVE7iLC/2eJLTC17Nv68FQbV1WWvWqblZOBQIKu/rDIFMX5otdVklrjte2hjWqyFYopXXpBeWNP0mq4A==
dependencies:
"@abp/core" "~5.0.0-rc.2"
"@microsoft/signalr" "~3.1.5"
"@abp/sweetalert2@~5.0.0-rc.1":
version "5.0.0-rc.1"
resolved "https://registry.yarnpkg.com/@abp/sweetalert2/-/sweetalert2-5.0.0-rc.1.tgz#f677853dd5e1e7bdba5d06cff816befcdf0e58b9"
@ -183,11 +198,37 @@
dependencies:
just-compare "^1.3.0"
"@abp/utils@~5.0.0-rc.2":
version "5.0.0-rc.2"
resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-5.0.0-rc.2.tgz#4ffc8e937c2e83f91c60a2f1222d8e046ab7dd54"
integrity sha512-/6QL6EdCatyz4igdPA0K0Cs/0fe3AatGyYYtwb+SBnZBcySqF+BUBWVlwMDJks8V/oUrJMQNy41foNuLGJb3jQ==
dependencies:
just-compare "^1.3.0"
"@fortawesome/fontawesome-free@^5.13.0":
version "5.15.3"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.3.tgz#c36ffa64a2a239bf948541a97b6ae8d729e09a9a"
integrity sha512-rFnSUN/QOtnOAgqFRooTA3H57JLDm0QEG/jPdk+tLQNL/eWd+Aok8g3qCI+Q1xuDPWpGW/i9JySpJVsq8Q0s9w==
"@microsoft/signalr@~3.1.5":
version "3.1.21"
resolved "https://registry.yarnpkg.com/@microsoft/signalr/-/signalr-3.1.21.tgz#e86f58726e40070caf454328464b0cec3ee8af39"
integrity sha512-OD65T7ecSa2lkXrg3hcZSYGuXcyzRwelBUcJCrc5oYR/b1ZuT5GwdxjrY/DOq/CjhGv1wsdLif8gw1yCzbF3tw==
dependencies:
eventsource "^1.0.7"
request "^2.88.0"
ws "^6.0.0"
ajv@^6.12.3:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ansi-colors@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9"
@ -305,6 +346,18 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
asn1@~0.2.3:
version "0.2.6"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
dependencies:
safer-buffer "~2.1.0"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
@ -325,6 +378,11 @@ async-each@^1.0.1:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
async-limiter@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async-settle@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b"
@ -332,11 +390,26 @@ async-settle@^1.0.0:
dependencies:
async-done "^1.2.2"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
atob@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
bach@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880"
@ -370,6 +443,13 @@ base@^0.11.1:
mixin-deep "^1.2.0"
pascalcase "^0.1.1"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
dependencies:
tweetnacl "^0.14.3"
binary-extensions@^1.0.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
@ -468,6 +548,11 @@ camelcase@^3.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
chokidar@^2.0.0:
version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@ -557,6 +642,13 @@ color-support@^1.1.3:
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
component-emitter@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
@ -597,7 +689,7 @@ copy-props@^2.0.1:
each-props "^1.3.2"
is-plain-object "^5.0.0"
core-util-is@~1.0.0:
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
@ -610,6 +702,13 @@ d@1, d@^1.0.1:
es5-ext "^0.10.50"
type "^1.0.1"
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
datatables.net-bs5@^1.10.25:
version "1.11.3"
resolved "https://registry.yarnpkg.com/datatables.net-bs5/-/datatables.net-bs5-1.11.3.tgz#939d0e66fbf518718a519534a88fc88cd29405b0"
@ -690,6 +789,11 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
detect-file@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
@ -713,6 +817,14 @@ each-props@^1.3.2:
is-plain-object "^2.0.1"
object.defaults "^1.1.0"
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@ -763,6 +875,13 @@ es6-weak-map@^2.0.1:
es6-iterator "^2.0.3"
es6-symbol "^3.1.1"
eventsource@^1.0.7:
version "1.1.0"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf"
integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==
dependencies:
original "^1.0.0"
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@ -810,7 +929,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
extend@^3.0.0:
extend@^3.0.0, extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
@ -829,6 +948,16 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
extsprintf@^1.2.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
fancy-log@^1.3.2:
version "1.3.3"
resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7"
@ -839,6 +968,16 @@ fancy-log@^1.3.2:
parse-node-version "^1.0.0"
time-stamp "^1.0.0"
fast-deep-equal@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9"
@ -930,6 +1069,20 @@ for-own@^1.0.0:
dependencies:
for-in "^1.0.1"
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
@ -982,6 +1135,13 @@ get-value@^2.0.3, get-value@^2.0.6:
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
dependencies:
assert-plus "^1.0.0"
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@ -1104,6 +1264,19 @@ gulplog@^1.0.0:
dependencies:
glogg "^1.0.0"
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.3:
version "5.1.5"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
dependencies:
ajv "^6.12.3"
har-schema "^2.0.0"
has-symbols@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
@ -1159,6 +1332,15 @@ hosted-git-info@^2.1.4:
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@ -1344,6 +1526,11 @@ is-relative@^1.0.0:
dependencies:
is-unc-path "^1.0.0"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
is-unc-path@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d"
@ -1388,6 +1575,11 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jquery-form@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/jquery-form/-/jquery-form-4.3.0.tgz#7d3961c314a1f2d15298f4af1d3943f54f4149c6"
@ -1418,11 +1610,41 @@ jquery@>=1.12.0, "jquery@>=1.5.0 <4.0", jquery@>=1.7, "jquery@>=1.7.1 <4.0.0", j
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
jsprim@^1.2.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb"
integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.4.0"
verror "1.10.0"
just-compare@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/just-compare/-/just-compare-1.4.0.tgz#079b7f37312c7a22517b3bf2aa7131bfd24c29dd"
@ -1589,6 +1811,18 @@ micromatch@^4.0.2:
braces "^3.0.1"
picomatch "^2.2.3"
mime-db@1.51.0:
version "1.51.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c"
integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==
mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.34"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24"
integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==
dependencies:
mime-db "1.51.0"
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@ -1675,6 +1909,11 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
object-copy@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
@ -1753,6 +1992,13 @@ ordered-read-streams@^1.0.0:
dependencies:
readable-stream "^2.0.1"
original@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
dependencies:
url-parse "^1.4.3"
os-locale@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
@ -1834,6 +2080,11 @@ path-type@^1.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
picomatch@^2.2.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
@ -1871,6 +2122,11 @@ process-nextick-args@^2.0.0, process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
psl@^1.1.28:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
pump@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
@ -1888,6 +2144,21 @@ pumpify@^1.3.5:
inherits "^2.0.3"
pump "^2.0.0"
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
querystringify@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@ -1988,6 +2259,32 @@ replace-homedir@^1.0.0:
is-absolute "^1.0.0"
remove-trailing-separator "^1.1.0"
request@^2.88.0:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.5.0"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@ -1998,6 +2295,11 @@ require-main-filename@^1.0.1:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
resolve-dir@^1.0.0, resolve-dir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43"
@ -2031,7 +2333,7 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
safe-buffer@^5.1.0:
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@ -2048,6 +2350,11 @@ safe-regex@^1.1.0:
dependencies:
ret "~0.1.10"
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
select2@^4.0.13:
version "4.0.13"
resolved "https://registry.yarnpkg.com/select2/-/select2-4.0.13.tgz#0dbe377df3f96167c4c1626033e924372d8ef44d"
@ -2169,6 +2476,21 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
stack-trace@0.0.10:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
@ -2317,6 +2639,26 @@ toastr@^2.1.4:
dependencies:
jquery ">=1.12.0"
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
dependencies:
psl "^1.1.28"
punycode "^2.1.1"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
type@^1.0.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
@ -2389,11 +2731,26 @@ upath@^1.1.1:
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
dependencies:
punycode "^2.1.0"
urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url-parse@^1.4.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
@ -2404,6 +2761,11 @@ util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
v8flags@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656"
@ -2424,6 +2786,15 @@ value-or-function@^3.0.0:
resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813"
integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
vinyl-fs@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7"
@ -2497,6 +2868,13 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@^6.0.0:
version "6.2.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
dependencies:
async-limiter "~1.0.0"
xtend@~4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"

5
package.json

@ -0,0 +1,5 @@
{
"dependencies": {
"@ngxs/store": "^3.7.3"
}
}

2
services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/AdministrationServiceApplicationContractsModule.cs

@ -2,10 +2,12 @@
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using EShopOnAbp.CatalogService;
namespace EShopOnAbp.AdministrationService
{
[DependsOn(
typeof(CatalogServiceApplicationContractsModule),
typeof(AdministrationServiceDomainSharedModule),
typeof(AbpPermissionManagementApplicationContractsModule),
typeof(AbpFeatureManagementApplicationContractsModule),

2
services/administration/src/EShopOnAbp.AdministrationService.Application.Contracts/EShopOnAbp.AdministrationService.Application.Contracts.csproj

@ -7,12 +7,14 @@
<ItemGroup>
<ProjectReference Include="..\EShopOnAbp.AdministrationService.Domain.Shared\EShopOnAbp.AdministrationService.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\catalog\src\EShopOnAbp.CatalogService.Application.Contracts\EShopOnAbp.CatalogService.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.PermissionManagement.Application.Contracts" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application.Contracts" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.SettingManagement.Application.Contracts" Version="5.0.0-rc.1" />
</ItemGroup>
</Project>

12
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/AddProductDto.cs

@ -0,0 +1,12 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace EShopOnAbp.BasketService;
public class AddProductDto
{
public Guid ProductId { get; set; }
[Range(1, int.MaxValue)]
public int Count { get; set; } = 1;
}

14
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/BasketDto.cs

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace EShopOnAbp.BasketService;
public class BasketDto
{
public float TotalPrice { get; set; }
public List<BasketItemDto> Items { get; set; }
public BasketDto()
{
Items = new List<BasketItemDto>();
}
}

13
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/BasketItemDto.cs

@ -0,0 +1,13 @@
using System;
namespace EShopOnAbp.BasketService;
public class BasketItemDto
{
public Guid ProductId { get; set; }
public string ProductName { get; set; }
public string ProductCode { get; set; }
public string ImageName { get; set; }
public int Count { get; set; }
public float TotalPrice { get; set; }
}

12
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IBasketAppService.cs

@ -0,0 +1,12 @@
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace EShopOnAbp.BasketService;
public interface IBasketAppService : IApplicationService
{
Task<BasketDto> GetAsync();
Task<BasketDto> AddProductAsync(AddProductDto input);
Task<BasketDto> RemoveProductAsync(RemoveProductDto input);
Task PurchaseAsync();
}

10
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/RemoveProductDto.cs

@ -0,0 +1,10 @@
using System;
namespace EShopOnAbp.BasketService;
public class RemoveProductDto
{
public Guid ProductId { get; set; }
public int? Count { get; set; }
}

116
services/basket/src/EShopOnAbp.BasketService.Application/BasketAppService.cs

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Users;
using EShopOnAbp.CatalogService.Products;
using Volo.Abp;
using Volo.Abp.EventBus.Distributed;
namespace EShopOnAbp.BasketService;
[Authorize]
public class BasketAppService : BasketServiceAppService, IBasketAppService
{
private readonly IBasketRepository _basketRepository;
private readonly IBasketProductService _basketProductService;
private readonly IDistributedEventBus _distributedEventBus;
public BasketAppService(
IBasketRepository basketRepository,
IBasketProductService basketProductService,
IDistributedEventBus distributedEventBus)
{
_basketRepository = basketRepository;
_basketProductService = basketProductService;
_distributedEventBus = distributedEventBus;
}
public async Task<BasketDto> GetAsync()
{
var basket = await _basketRepository.GetAsync(CurrentUser.GetId());
return await GetBasketDtoAsync(basket);
}
public async Task<BasketDto> AddProductAsync(AddProductDto input)
{
var basket = await _basketRepository.GetAsync(CurrentUser.GetId());
var product = await _basketProductService.GetAsync(input.ProductId);
if (basket.GetProductCount(product.Id) >= product.StockCount)
{
throw new UserFriendlyException("There is not enough product in stock, sorry :(");
}
basket.AddProduct(product.Id);
await _basketRepository.UpdateAsync(basket);
return await GetBasketDtoAsync(basket);
}
public async Task<BasketDto> RemoveProductAsync(RemoveProductDto input)
{
var basket = await _basketRepository.GetAsync(CurrentUser.GetId());
var product = await _basketProductService.GetAsync(input.ProductId);
basket.RemoveProduct(product.Id, input.Count);
await _basketRepository.UpdateAsync(basket);
return await GetBasketDtoAsync(basket);
}
public async Task PurchaseAsync()
{
var basket = await _basketRepository.GetAsync(CurrentUser.GetId());
var orderAcceptedEto = ObjectMapper.Map<Basket, OrderAcceptedEto>(basket);
await _distributedEventBus.PublishAsync(orderAcceptedEto);
basket.Clear();
await _basketRepository.UpdateAsync(basket);
}
private async Task<BasketDto> GetBasketDtoAsync(Basket basket)
{
var products = new Dictionary<Guid, ProductDto>();
var basketDto = new BasketDto();
var basketChanged = false;
foreach (var basketItem in basket.Items)
{
var productDto = products.GetOrDefault(basketItem.ProductId);
if (productDto == null)
{
productDto = await _basketProductService.GetAsync(basketItem.ProductId);
products[productDto.Id] = productDto;
}
//Removing the products if not available in the stock
if (basketItem.Count > productDto.StockCount)
{
basket.RemoveProduct(basketItem.ProductId, basketItem.Count - productDto.StockCount);
basketChanged = true;
}
basketDto.Items.Add(new BasketItemDto
{
ProductId = basketItem.ProductId,
Count = basketItem.Count,
ProductCode = productDto.Code,
ImageName = productDto.ImageName,
ProductName = productDto.Name,
TotalPrice = productDto.Price * basketItem.Count
});
}
basketDto.TotalPrice = basketDto.Items.Sum(x => x.TotalPrice);
if (basketChanged)
{
await _basketRepository.UpdateAsync(basket);
}
return basketDto;
}
}

47
services/basket/src/EShopOnAbp.BasketService.Application/BasketProductEventHandler.cs

@ -0,0 +1,47 @@
using System;
using System.Threading.Tasks;
using EShopOnAbp.CatalogService.Products;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.ObjectMapping;
namespace EShopOnAbp.BasketService;
public class BasketProductEventHandler : IDistributedEventHandler<EntityUpdatedEto<ProductEto>>, ITransientDependency
{
private readonly IDistributedCache<ProductDto, Guid> _cache;
private readonly IObjectMapper _objectMapper;
private readonly IDistributedEventBus _distributedEventBus;
public BasketProductEventHandler(
IDistributedCache<ProductDto, Guid> cache,
IObjectMapper objectMapper,
IDistributedEventBus distributedEventBus)
{
_cache = cache;
_objectMapper = objectMapper;
_distributedEventBus = distributedEventBus;
}
public async Task HandleEventAsync(EntityUpdatedEto<ProductEto> eventData)
{
var cachedProductDto = await _cache.GetAsync(eventData.Entity.Id);
if (cachedProductDto == null)
{
return;
}
_objectMapper.Map(eventData.Entity, cachedProductDto);
await _cache.SetAsync(eventData.Entity.Id, cachedProductDto);
await _distributedEventBus.PublishAsync(
new BasketProductUpdatedEto
{
ProductId = eventData.Entity.Id,
ProductName = eventData.Entity.Name
});
}
}

36
services/basket/src/EShopOnAbp.BasketService.Application/BasketProductService.cs

@ -0,0 +1,36 @@
using System;
using System.Threading.Tasks;
using EShopOnAbp.CatalogService.Products;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.BasketService;
public class BasketProductService : IBasketProductService, ITransientDependency
{
private readonly IPublicProductAppService _productAppService;
private readonly IDistributedCache<ProductDto, Guid> _cache;
public BasketProductService(
IPublicProductAppService productAppService,
IDistributedCache<ProductDto, Guid> cache)
{
_productAppService = productAppService;
_cache = cache;
}
public async Task<ProductDto> GetAsync(Guid productId)
{
return await _cache.GetOrAddAsync(
productId,
() => GetProductAsync(productId)
);
}
private Task<ProductDto> GetProductAsync(Guid productId)
{
return _productAppService.GetAsync(productId) ??
throw new UserFriendlyException("Could not find the product!"); //TODO: Business exception with localization;
}
}

7
services/basket/src/EShopOnAbp.BasketService.Application/BasketServiceApplicationAutoMapperProfile.cs

@ -1,4 +1,5 @@
using AutoMapper;
using EShopOnAbp.CatalogService.Products;
namespace EShopOnAbp.BasketService
{
@ -6,9 +7,9 @@ namespace EShopOnAbp.BasketService
{
public BasketServiceApplicationAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
CreateMap<ProductEto, ProductDto>();
CreateMap<Basket, OrderAcceptedEto>();
CreateMap<BasketItem, OrderItemEto>();
}
}
}

6
services/basket/src/EShopOnAbp.BasketService.Application/BasketServiceApplicationModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.Application;
using EShopOnAbp.CatalogService;
using Volo.Abp.Application;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
@ -8,7 +9,8 @@ namespace EShopOnAbp.BasketService
typeof(BasketServiceDomainModule),
typeof(BasketServiceApplicationContractsModule),
typeof(AbpDddApplicationModule),
typeof(AbpAutoMapperModule)
typeof(AbpAutoMapperModule),
typeof(CatalogServiceHttpApiClientModule)
)]
public class BasketServiceApplicationModule : AbpModule
{

1
services/basket/src/EShopOnAbp.BasketService.Application/EShopOnAbp.BasketService.Application.csproj

@ -10,6 +10,7 @@
<ItemGroup>
<ProjectReference Include="..\EShopOnAbp.BasketService.Domain\EShopOnAbp.BasketService.Domain.csproj" />
<ProjectReference Include="..\EShopOnAbp.BasketService.Application.Contracts\EShopOnAbp.BasketService.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\catalog\src\EShopOnAbp.CatalogService.HttpApi.Client\EShopOnAbp.CatalogService.HttpApi.Client.csproj" />
</ItemGroup>
<ItemGroup>

12
services/basket/src/EShopOnAbp.BasketService.Application/IBasketProductService.cs

@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
using EShopOnAbp.CatalogService.Products;
using JetBrains.Annotations;
namespace EShopOnAbp.BasketService;
public interface IBasketProductService
{
[ItemNotNull]
Task<ProductDto> GetAsync(Guid productId);
}

9
services/basket/src/EShopOnAbp.BasketService.Domain.Shared/BasketProductUpdatedEto.cs

@ -0,0 +1,9 @@
using System;
namespace EShopOnAbp.BasketService;
public class BasketProductUpdatedEto
{
public Guid ProductId { get; set; }
public string ProductName { get; set; }
}

8
services/basket/src/EShopOnAbp.BasketService.Domain.Shared/OrderAcceptedEto.cs

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace EShopOnAbp.BasketService;
public class OrderAcceptedEto
{
public List<OrderItemEto> Items { get; set; }
}

9
services/basket/src/EShopOnAbp.BasketService.Domain.Shared/OrderItemEto.cs

@ -0,0 +1,9 @@
using System;
namespace EShopOnAbp.BasketService;
public class OrderItemEto
{
public Guid ProductId { get; set; }
public int Count { get; set; }
}

73
services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.Domain.Entities;
namespace EShopOnAbp.BasketService;
public class Basket : AggregateRoot<Guid>
{
public List<BasketItem> Items { get; set; }
private Basket()
{
}
public Basket(Guid id)
: base(id)
{
Items = new List<BasketItem>();
}
public void AddProduct(Guid productId, int count = 1)
{
if (count < 1)
{
throw new ArgumentOutOfRangeException(nameof(count), "Product count should be 1 or more!");
}
var item = Items.FirstOrDefault(x => x.ProductId == productId);
if (item == null)
{
Items.Add(new BasketItem(productId, count));
}
else
{
item.Count += count;
}
}
public void RemoveProduct(Guid productId, int? count = null)
{
if (count is < 1)
{
throw new ArgumentOutOfRangeException(nameof(count), "Product count should be null, 1 or more!");
}
var item = Items.FirstOrDefault(x => x.ProductId == productId);
if (item == null)
{
return;
}
if (count == null || item.Count <= count)
{
Items.Remove(item);
return;
}
item.Count -= count.Value;
}
public int GetProductCount(Guid productId)
{
var item = Items.FirstOrDefault(x => x.ProductId == productId);
return item?.Count ?? 0;
}
public void Clear()
{
Items.Clear();
}
}

20
services/basket/src/EShopOnAbp.BasketService.Domain/BasketItem.cs

@ -0,0 +1,20 @@
using System;
namespace EShopOnAbp.BasketService;
public class BasketItem
{
public Guid ProductId { get; private set; }
public int Count { get; internal set; }
private BasketItem()
{
}
public BasketItem(Guid productId, int count = 1)
{
ProductId = productId;
Count = count;
}
}

25
services/basket/src/EShopOnAbp.BasketService.Domain/BasketRepository.cs

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
namespace EShopOnAbp.BasketService;
public class BasketRepository : IBasketRepository
{
private readonly IDistributedCache<Basket, Guid> _cache;
public BasketRepository(IDistributedCache<Basket, Guid> cache)
{
_cache = cache;
}
public async Task<Basket> GetAsync(Guid id)
{
return await _cache.GetOrAddAsync(id, () => Task.FromResult(new Basket(id)));
}
public async Task UpdateAsync(Basket basket)
{
await _cache.SetAsync(basket.Id, basket);
}
}

23
services/basket/src/EShopOnAbp.BasketService.Domain/BasketServiceDomainModule.cs

@ -1,16 +1,35 @@
using Volo.Abp.Domain;
using System;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp.Caching;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;
namespace EShopOnAbp.BasketService
{
[DependsOn(
typeof(BasketServiceDomainSharedModule),
typeof(AbpDddDomainModule)
typeof(AbpDddDomainModule),
typeof(AbpCachingModule)
)]
public class BasketServiceDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDistributedCacheOptions>(options =>
{
options.CacheConfigurators.Add(cacheName =>
{
if (cacheName == CacheNameAttribute.GetCacheName(typeof(Basket)))
{
return new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(7)
};
}
return null;
});
});
}
}
}

1
services/basket/src/EShopOnAbp.BasketService.Domain/EShopOnAbp.BasketService.Domain.csproj

@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="5.0.0-rc.1" />
<PackageReference Include="Volo.Abp.Caching" Version="5.0.0-rc.1" />
</ItemGroup>
</Project>

12
services/basket/src/EShopOnAbp.BasketService.Domain/IBasketRepository.cs

@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace EShopOnAbp.BasketService;
public interface IBasketRepository : IRepository
{
Task<Basket> GetAsync(Guid id);
Task UpdateAsync(Basket basket);
}

4
services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs

@ -11,11 +11,11 @@ namespace EShopOnAbp.BasketService
)]
public class BasketServiceHttpApiClientModule : AbpModule
{
public const string RemoteServiceName = "Default";
public const string RemoteServiceName = "Basket";
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(
context.Services.AddStaticHttpClientProxies(
typeof(BasketServiceApplicationContractsModule).Assembly,
RemoteServiceName
);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save