code push initiall

This commit is contained in:
Soumen Pal 2024-06-10 13:04:12 +05:30
parent 73c518e3fa
commit ef415acd4b
239 changed files with 43438 additions and 0 deletions

16
angular/.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

50
angular/.eslintrc.json Normal file
View File

@ -0,0 +1,50 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

46
angular/.gitignore vendored Normal file
View File

@ -0,0 +1,46 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.angular/cache
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

5
angular/.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"singleQuote": true,
"printWidth": 100,
"arrowParens": "avoid"
}

27
angular/README.md Normal file
View File

@ -0,0 +1,27 @@
# BookStore
This is a startup project based on the ABP framework. For more information, visit <a href="https://abp.io/" target="_blank">abp.io</a>
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice.
## 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.

190
angular/angular.json Normal file
View File

@ -0,0 +1,190 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"cli": {
"analytics": false,
"schematicCollections": ["@angular-eslint/schematics"]
},
"version": 1,
"newProjectRoot": "projects",
"projects": {
"BookStore": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/BookStore",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"allowedCommonJsDependencies": ["chart.js", "js-sha256"],
"assets": ["src/favicon.ico", "src/assets"],
"styles": [
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.css",
"inject": false,
"bundleName": "bootstrap-dim"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.css",
"inject": false,
"bundleName": "ng-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.css",
"inject": false,
"bundleName": "layout-bundle"
},
{
"input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.css",
"inject": false,
"bundleName": "abp-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/bootstrap-dim.rtl.css",
"inject": false,
"bundleName": "bootstrap-dim.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.rtl.css",
"inject": false,
"bundleName": "font-bundle.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/font-bundle.css",
"inject": false,
"bundleName": "font-bundle"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/ng-bundle.rtl.css",
"inject": false,
"bundleName": "ng-bundle.rtl"
},
{
"input": "node_modules/@volo/ngx-lepton-x.lite/assets/css/side-menu/layout-bundle.rtl.css",
"inject": false,
"bundleName": "layout-bundle.rtl"
},
{
"input": "node_modules/@abp/ng.theme.lepton-x/assets/css/abp-bundle.rtl.css",
"inject": false,
"bundleName": "abp-bundle.rtl"
},
{
"input": "node_modules/bootstrap-icons/font/bootstrap-icons.css",
"inject": true,
"bundleName": "bootstrap-icons"
},
{
"input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"inject": true,
"bundleName": "fontawesome-all.min"
},
{
"input": "node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css",
"inject": true,
"bundleName": "fontawesome-v4-shims.min"
},
{
"input": "node_modules/@swimlane/ngx-datatable/index.css",
"inject": true,
"bundleName": "ngx-datatable-index"
},
{
"input": "node_modules/@swimlane/ngx-datatable/assets/icons.css",
"inject": true,
"bundleName": "ngx-datatable-icons"
},
{
"input": "node_modules/@swimlane/ngx-datatable/themes/material.css",
"inject": true,
"bundleName": "ngx-datatable-material"
},
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "2.5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "BookStore:build:production"
},
"development": {
"buildTarget": "BookStore:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "BookStore:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
}
}
}
}
}

44
angular/karma.conf.js Normal file
View File

@ -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/BookStore'),
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
});
};

62
angular/package.json Normal file
View File

@ -0,0 +1,62 @@
{
"name": "BookStore",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --open",
"build": "ng build",
"build:prod": "ng build --configuration production",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint"
},
"private": true,
"dependencies": {
"@abp/ng.account": "~8.1.3",
"@abp/ng.components": "~8.1.3",
"@abp/ng.core": "~8.1.3",
"@abp/ng.identity": "~8.1.3",
"@abp/ng.oauth": "~8.1.3",
"@abp/ng.setting-management": "~8.1.3",
"@abp/ng.tenant-management": "~8.1.3",
"@abp/ng.theme.lepton-x": "~3.1.3",
"@abp/ng.theme.shared": "~8.1.3",
"@angular/animations": "~17.1.0",
"@angular/common": "~17.1.0",
"@angular/compiler": "~17.1.0",
"@angular/core": "~17.1.0",
"@angular/forms": "~17.1.0",
"@angular/localize": "~17.1.0",
"@angular/platform-browser": "~17.1.0",
"@angular/platform-browser-dynamic": "~17.1.0",
"@angular/router": "~17.1.0",
"bootstrap-icons": "~1.8.0",
"rxjs": "~7.8.0",
"tslib": "^2.0.0",
"zone.js": "~0.14.0"
},
"devDependencies": {
"@abp/ng.schematics": "~8.1.3",
"@angular-devkit/build-angular": "~17.1.0",
"@angular-eslint/builder": "~17.2.0",
"@angular-eslint/eslint-plugin": "~17.2.0",
"@angular-eslint/eslint-plugin-template": "~17.2.0",
"@angular-eslint/schematics": "~17.2.0",
"@angular-eslint/template-parser": "~17.2.0",
"@angular/cli": "~17.1.0",
"@angular/compiler-cli": "~17.1.0",
"@angular/language-service": "~17.1.0",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"@typescript-eslint/eslint-plugin": "6.9.1",
"@typescript-eslint/parser": "6.9.1",
"eslint": "^8.23.0",
"jasmine-core": "~4.0.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.1.0",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.7.0",
"typescript": "~5.3.0"
}
}

View File

@ -0,0 +1,37 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
loadChildren: () => import('./home/home.module').then(m => m.HomeModule),
},
{
path: 'account',
loadChildren: () => import('@abp/ng.account').then(m => m.AccountModule.forLazy()),
},
{
path: 'identity',
loadChildren: () => import('@abp/ng.identity').then(m => m.IdentityModule.forLazy()),
},
{
path: 'tenant-management',
loadChildren: () =>
import('@abp/ng.tenant-management').then(m => m.TenantManagementModule.forLazy()),
},
{
path: 'setting-management',
loadChildren: () =>
import('@abp/ng.setting-management').then(m => m.SettingManagementModule.forLazy()),
},
{ path: 'books', loadChildren: () => import('./book/book.module').then(m => m.BookModule) },
{ path: 'customer', loadChildren: () => import('./customer/customer.module').then(m => m.CustomerModule) },
{ path: 'book-issue', loadChildren: () => import('./book-issue/book-issue.module').then(m => m.BookIssueModule) },
];
@NgModule({
imports: [RouterModule.forRoot(routes, {})],
exports: [RouterModule],
})
export class AppRoutingModule {}

View File

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
<abp-internet-status></abp-internet-status>
`,
})
export class AppComponent {}

View File

@ -0,0 +1,48 @@
import { AccountConfigModule } from '@abp/ng.account/config';
import { CoreModule } from '@abp/ng.core';
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 { InternetConnectionStatusComponent, 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 { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_ROUTE_PROVIDER } from './route.provider';
import { FeatureManagementModule } from '@abp/ng.feature-management';
import { AbpOAuthModule } from '@abp/ng.oauth';
import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x';
import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts';
import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
CoreModule.forRoot({
environment,
registerLocaleFn: registerLocale(),
}),
AbpOAuthModule.forRoot(),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot(),
IdentityConfigModule.forRoot(),
TenantManagementConfigModule.forRoot(),
SettingManagementConfigModule.forRoot(),
FeatureManagementModule.forRoot(),
InternetConnectionStatusComponent,
ThemeLeptonXModule.forRoot(),
SideMenuLayoutModule.forRoot(),
AccountLayoutModule.forRoot()
],
declarations: [AppComponent],
providers: [APP_ROUTE_PROVIDER],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BookIssueComponent } from './book-issue.component';
import { authGuard, permissionGuard } from '@abp/ng.core';
const routes: Routes = [{ path: '', component: BookIssueComponent,canActivate: [authGuard, permissionGuard] }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class BookIssueRoutingModule { }

View File

@ -0,0 +1,97 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
{{ 'Book-Issue' | abpLocalization }}
</h5>
</div>
<div class="text-end col col-md-6">
<!-- Add the "new book" button here -->
<div class="text-lg-end pt-2">
<button id="create" class="btn btn-primary" type="button" (click)="createBookIssue()">
<i class="fa fa-plus me-1"></i>
<span>{{ "New Book Issue" | abpLocalization }}</span>
</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<ngx-datatable [rows]="bookIssueList.items" [count]="bookIssueList.totalCount" [list]="list" default>
<ngx-datatable-column
[name]="'::Actions' | abpLocalization"
[maxWidth]="150"
[sortable]="false"
>
<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="fa fa-cog me-1"></i>{{ '::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button ngbDropdownItem (click)="delete(row.bookIssueId)">
{{ 'un Issue' | abpLocalization }}
</button>
</div>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'Book Name' | abpLocalization" prop="bookName"></ngx-datatable-column>
<ngx-datatable-column [name]="'Customer Name' | abpLocalization" prop="customerName">
</ngx-datatable-column>
<ngx-datatable-column [name]="'Issue Date' | abpLocalization" prop="issueDate">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ row.issueDate | date }}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
<!-- Add the modal here -->
<abp-modal [(visible)]="isModalOpen">
<ng-template #abpHeader>
<h3> Book Issued </h3>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form" (ngSubmit)="save()">
<div class="mt-2">
<label for="book-type">Book Name</label><span> * </span>
<select class="form-control" id="book-type" formControlName="bookId">
<option [ngValue]="null">Select a book name</option>
<option [ngValue]="book.id" *ngFor="let book of dropDownbook"> {{ book.name }}</option>
</select>
</div>
<div class="mt-2">
<label for="customer-name">Customer Name</label><span> * </span>
<select class="form-control" id="customer-name" formControlName="customerId">
<option [ngValue]="null">Select a customer name</option>
<option [ngValue]="customer.id" *ngFor="let customer of dropDownCustomer"> {{ customer.firstName }} {{ customer.lastName}}</option>
</select>
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" abpClose>
{{ '::Close' | abpLocalization }}
</button>
<!--added save button-->
<button class="btn btn-primary" (click)="save()" [disabled]="form.invalid">
<i class="fa fa-check mr-1"></i>
{{ '::Save' | abpLocalization }}
</button>
</ng-template>
</abp-modal>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BookIssueComponent } from './book-issue.component';
describe('BookIssueComponent', () => {
let component: BookIssueComponent;
let fixture: ComponentFixture<BookIssueComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [BookIssueComponent]
})
.compileComponents();
fixture = TestBed.createComponent(BookIssueComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,83 @@
import { Component, OnInit } from '@angular/core';
import { ListService, PagedResultDto } from '@abp/ng.core';
import { BookIssueDto, BookIssueListDto, BookIssueService } from '@proxy/book-issued';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { BookDto, BookService } from '@proxy/books';
import { CustomerDto, CustomerService } from '@proxy/customers';
import { ConfirmationService,Confirmation } from '@abp/ng.theme.shared';
@Component({
selector: 'app-book-issue',
templateUrl: './book-issue.component.html',
styleUrl: './book-issue.component.scss',
providers: [ListService]
})
export class BookIssueComponent implements OnInit {
isModalOpen = false;
form:FormGroup;
dropDownbook= {} as Array<BookDto>;
dropDownCustomer= {} as Array<CustomerDto>;
bookIssueList = { items: [], totalCount: 0 } as PagedResultDto<BookIssueListDto>;
constructor(public readonly list: ListService,
private bookIssuedService: BookIssueService,
private bookService: BookService,
private customerService: CustomerService,
private fb: FormBuilder,
private confirmation: ConfirmationService ) {}
ngOnInit(): void {
this.dropdowninitiale();
const bookStreamCreator = (query) => this.bookIssuedService.getList(query);
this.list.hookToQuery(bookStreamCreator).subscribe((response) => {
this.bookIssueList = response;
});
}
createBookIssue() {
this.buildForm();
this.isModalOpen = true;
}
buildForm() {
this.form = this.fb.group({
bookId: ['', Validators.required],
customerId: ['', Validators.required],
});
}
save() {
if (this.form.invalid) {
return;
}
this.bookIssuedService.create(this.form.value).subscribe(() => {
this.isModalOpen = false;
this.form.reset();
this.list.get();
});
}
dropdowninitiale(){
this.bookService.getBookDropDown().subscribe((response)=>{
this.dropDownbook = response;
});
this.customerService.getcustomerDropDown().subscribe((response)=>{
this.dropDownCustomer = response;
});
}
delete(bookIssueId: number) {
this.confirmation.warn('::AreYouSureToUnIssue', '::AreYouSure').subscribe((status) => {
if (status === Confirmation.Status.confirm) {
this.bookIssuedService.delete(bookIssueId).subscribe(() => this.list.get());
}
});
}
}

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BookIssueRoutingModule } from './book-issue-routing.module';
import { BookIssueComponent } from './book-issue.component';
import{SharedModule} from '../shared/shared.module';
@NgModule({
declarations: [
BookIssueComponent
],
imports: [
CommonModule,
BookIssueRoutingModule,
SharedModule
]
})
export class BookIssueModule { }

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BookComponent } from './book.component';
import { authGuard, permissionGuard } from '@abp/ng.core';
const routes: Routes = [{ path: '', component: BookComponent, canActivate: [authGuard, permissionGuard] }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class BookRoutingModule { }

View File

@ -0,0 +1,122 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
{{ '::Menu:Books' | abpLocalization }}
</h5>
</div>
<div class="text-end col col-md-6">
<!-- Add the "new book" button here -->
<div class="text-lg-end pt-2">
<button id="create" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus me-1"></i>
<span>{{ "::NewBook" | abpLocalization }}</span>
</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<ngx-datatable [rows]="book.items" [count]="book.totalCount" [list]="list" default>
<ngx-datatable-column
[name]="'::Actions' | abpLocalization"
[maxWidth]="150"
[sortable]="false"
>
<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="fa fa-cog me-1"></i>{{ '::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button ngbDropdownItem (click)="editBook(row.id)">
{{ '::Edit' | abpLocalization }}
</button>
<button ngbDropdownItem (click)="delete(row.id)">
{{ '::Delete' | abpLocalization }}
</button>
</div>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Name' | abpLocalization" prop="name"></ngx-datatable-column>
<ngx-datatable-column [name]="'::Type' | abpLocalization" prop="type">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ '::Enum:BookType.' + row.type | abpLocalization }}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::PublishDate' | abpLocalization" prop="publishDate">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ row.publishDate | date }}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Price' | abpLocalization" prop="price">
<ng-template let-row="row" ngx-datatable-cell-template>
{{ row.price | currency }}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
<!-- Add the modal here -->
<abp-modal [(visible)]="isModalOpen">
<ng-template #abpHeader>
<h3>{{ '::NewBook' | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form" (ngSubmit)="save()">
<div class="mt-2">
<label for="book-name">Name</label><span> * </span>
<input type="text" id="book-name" class="form-control" formControlName="name" autofocus />
</div>
<div class="mt-2">
<label for="book-price">Price</label><span> * </span>
<input type="number" id="book-price" class="form-control" formControlName="price" />
</div>
<div class="mt-2">
<label for="book-type">Type</label><span> * </span>
<select class="form-control" id="book-type" formControlName="type">
<option [ngValue]="null">Select a book type</option>
<option [ngValue]="type.value" *ngFor="let type of bookTypes"> {{ '::Enum:BookType.' + type.value | abpLocalization }}</option>
</select>
</div>
<div class="mt-2">
<label>Publish date</label><span> * </span>
<input
#datepicker="ngbDatepicker"
class="form-control"
name="datepicker"
formControlName="publishDate"
ngbDatepicker
(click)="datepicker.toggle()"
/>
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" abpClose>
{{ '::Close' | abpLocalization }}
</button>
<!--added save button-->
<button class="btn btn-primary" (click)="save()" [disabled]="form.invalid">
<i class="fa fa-check mr-1"></i>
{{ '::Save' | abpLocalization }}
</button>
</ng-template>
</abp-modal>

View File

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BookComponent } from './book.component';
describe('BookComponent', () => {
let component: BookComponent;
let fixture: ComponentFixture<BookComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [BookComponent]
})
.compileComponents();
fixture = TestBed.createComponent(BookComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,88 @@
import { ListService, PagedResultDto } from '@abp/ng.core';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { BookService, BookDto, bookTypeOptions } from '@proxy/books';
import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared';
@Component({
selector: 'app-book',
templateUrl: './book.component.html',
styleUrls: ['./book.component.scss'],
providers: [ListService,
{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter } // add this line
],
})
export class BookComponent implements OnInit {
book = { items: [], totalCount: 0 } as PagedResultDto<BookDto>;
selectedBook = {} as BookDto; // declare selectedBook
isModalOpen = false; // add this line
form: FormGroup; // add this line
bookTypes = bookTypeOptions;
constructor(public readonly list: ListService, private bookService: BookService, private fb: FormBuilder,
private confirmation: ConfirmationService
) {}
ngOnInit() {
debugger;
const bookStreamCreator = (query) => this.bookService.getList(query);
this.list.hookToQuery(bookStreamCreator).subscribe((response) => {
this.book = response;
});
}
// add new method
createBook() {
this.buildForm(); // add this line
this.selectedBook = {} as BookDto; // reset the selected book
this.isModalOpen = true;
}
buildForm() {
this.form = this.fb.group({
name: ['', Validators.required],
type: [null, Validators.required],
publishDate: [null, Validators.required],
price: [null, Validators.required],
});
}
save() {
if (this.form.invalid) {
return;
}
const request = this.selectedBook.id
? this.bookService.update(this.selectedBook.id, this.form.value)
: this.bookService.create(this.form.value);
request.subscribe(() => {
this.isModalOpen = false;
this.form.reset();
this.list.get();
});
}
// Add editBook method
editBook(id: string) {
this.bookService.get(id).subscribe((book) => {
this.selectedBook = book;
this.buildForm();
this.form.controls["name"].setValue(this.selectedBook.name);
this.form.controls["type"].setValue(this.selectedBook.type);
this.form.controls["publishDate"].setValue(this.selectedBook.publishDate);
this.form.controls["price"].setValue(this.selectedBook.price);
this.isModalOpen = true;
});
}
delete(id: string) {
this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure').subscribe((status) => {
if (status === Confirmation.Status.confirm) {
this.bookService.delete(id).subscribe(() => this.list.get());
}
});
}
}

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BookRoutingModule } from './book-routing.module';
import { BookComponent } from './book.component';
import{SharedModule} from '../shared/shared.module';
import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; // add this line
@NgModule({
declarations: [
BookComponent
],
imports: [
CommonModule,
BookRoutingModule,
SharedModule,
NgbDatepickerModule
]
})
export class BookModule { }

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomerComponent } from './customer.component';
import { authGuard, permissionGuard } from '@abp/ng.core';
const routes: Routes = [{ path: '', component: CustomerComponent,canActivate: [authGuard, permissionGuard] }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CustomerRoutingModule { }

View File

@ -0,0 +1,101 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
{{ 'customer' | abpLocalization }}
</h5>
</div>
<div class="text-end col col-md-6">
<!-- Add the "new book" button here -->
<div class="text-lg-end pt-2">
<button id="create" class="btn btn-primary" type="button" (click)="createCustomer()">
<i class="fa fa-plus me-1"></i>
<span>{{ "New Customer" | abpLocalization }}</span>
</button>
</div>
</div>
</div>
</div>
<div class="card-body">
<ngx-datatable [rows]="customer.items" [count]="customer.totalCount" [list]="list" default>
<ngx-datatable-column
[name]="'::Actions' | abpLocalization"
[maxWidth]="150"
[sortable]="false"
>
<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="fa fa-cog me-1"></i>{{ '::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button ngbDropdownItem (click)="editCustomer(row.id)">
{{ '::Edit' | abpLocalization }}
</button>
<button ngbDropdownItem (click)="delete(row.id)">
{{ '::Delete' | abpLocalization }}
</button>
</div>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [name]="'first Name' | abpLocalization" prop="firstName"></ngx-datatable-column>
<ngx-datatable-column [name]="'Last Name' | abpLocalization" prop="lastName">
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Address' | abpLocalization" prop="address">
</ngx-datatable-column>
<ngx-datatable-column [name]="'::Phone' | abpLocalization" prop="phone">
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
<!-- Add the modal here -->
<abp-modal [(visible)]="isModalOpen">
<ng-template #abpHeader>
<h3>{{ (selectedcustomer.id ? '::Edit' : '::New Customer' ) | abpLocalization }}</h3>
</ng-template>
<ng-template #abpBody>
<form [formGroup]="form" (ngSubmit)="save()">
<div class="mt-2">
<label for="customer-first-name">First Name</label><span> * </span>
<input type="text" id="customer-first-name" class="form-control" formControlName="firstName" autofocus />
</div>
<div class="mt-2">
<label for="customer-last-name">Last Name</label><span> * </span>
<input type="text" id="customer-last-name" class="form-control" formControlName="lastName" />
</div>
<div class="mt-2">
<label for="customer-phone-name">Phone</label><span> * </span>
<input type="text" id="customer-phone-name" class="form-control" formControlName="phone" />
</div>
<div class="mt-2">
<label for="customer-address-name">Address</label><span> * </span>
<input type="text" id="customer-address-name" class="form-control" formControlName="address" />
</div>
</form>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" abpClose>
{{ '::Close' | abpLocalization }}
</button>
<!--added save button-->
<button class="btn btn-primary" (click)="save()" [disabled]="form.invalid">
<i class="fa fa-check mr-1"></i>
{{ '::Save' | abpLocalization }}
</button>
</ng-template>
</abp-modal>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CustomerComponent } from './customer.component';
describe('CustomerComponent', () => {
let component: CustomerComponent;
let fixture: ComponentFixture<CustomerComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [CustomerComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CustomerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,92 @@
import { Component, OnInit } from '@angular/core';
import { ListService, PagedResultDto } from '@abp/ng.core';
import { CustomerDto, CustomerService } from '@proxy/customers';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared';
@Component({
selector: 'app-customer',
templateUrl: './customer.component.html',
styleUrl: './customer.component.scss',
providers: [ListService]
})
export class CustomerComponent implements OnInit {
customer = { items: [], totalCount: 0 } as PagedResultDto<CustomerDto>;
isModalOpen = false;
form: FormGroup;
selectedcustomer = {} as CustomerDto;
constructor(public readonly list: ListService,
private customerService: CustomerService,
private fb: FormBuilder,
private confirmation: ConfirmationService ) {}
ngOnInit() {
debugger;
const bookStreamCreator = (query) => this.customerService.getList(query);
this.list.hookToQuery(bookStreamCreator).subscribe((response) => {
this.customer = response;
});
}
// add new method
createCustomer() {
this.selectedcustomer = {} as CustomerDto;
this.buildForm();
this.isModalOpen = true;
}
editCustomer(id: number) {
this.customerService.get(id).subscribe((customer) => {
debugger;
this.selectedcustomer = customer;
this.buildForm();
this.isModalOpen = true;
});
}
buildForm() {
this.form = this.fb.group({
firstName: [this.selectedcustomer.firstName || '', Validators.required],
lastName: [this.selectedcustomer.lastName, Validators.required],
phone: [this.selectedcustomer.phone, Validators.required],
address: [this.selectedcustomer.address, Validators.required],
});
}
// add save method
save() {
if (this.form.invalid) {
return;
}
debugger;
const request = this.selectedcustomer.id
? this.customerService.update(this.selectedcustomer.id, this.form.value)
: this.customerService.create(this.form.value);
if(this.selectedcustomer.id){
this.customerService.update(this.selectedcustomer.id,this.form.value).subscribe(() => {
this.isModalOpen = false;
this.form.reset();
this.list.get();
});
}
else{
this.customerService.create(this.form.value).subscribe(() => {
this.isModalOpen = false;
this.form.reset();
this.list.get();
});
}
}
delete(id: number) {
this.confirmation.warn('::AreYouSureToDelete', '::AreYouSure').subscribe((status) => {
if (status === Confirmation.Status.confirm) {
this.customerService.delete(id).subscribe(() => this.list.get());
}
});
}
}

View File

@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomerRoutingModule } from './customer-routing.module';
import { CustomerComponent } from './customer.component';
import{SharedModule} from '../shared/shared.module';
@NgModule({
declarations: [
CustomerComponent
],
imports: [
CommonModule,
CustomerRoutingModule,
SharedModule
]
})
export class CustomerModule { }

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home.component';
const routes: Routes = [{ path: '', component: HomeComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class HomeRoutingModule {}

View File

@ -0,0 +1,362 @@
<div class="container">
<div class="p-5 text-center">
<div class="d-inline-block bg-success text-white p-1 h5 rounded mb-4" role="alert">
<h5 class="m-1">
<i class="fas fa-rocket" aria-hidden="true"></i> Congratulations, <strong>BookStore</strong> is
successfully running!
</h5>
</div>
<h1>{{ '::Welcome' | abpLocalization }}</h1>
<p class="lead px-lg-5 mx-lg-5">{{ '::LongWelcomeMessage' | abpLocalization }}</p>
<a *ngIf="!hasLoggedIn" (click)="login()" class="px-4 btn btn-primary ms-1" role="button"
><i class="fa fa-sign-in" aria-hidden="true"></i> {{ 'AbpAccount::Login' | abpLocalization }}</a
>
</div>
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-auto text-center">
<img src="https://abp.io/assets/png/mastering-abp-framework.webp" style="max-width: 400px;"
class="w-100 mb-5 my-md-3">
</div>
<div class="col-md d-flex align-items-center">
<div class="pe-0 pe-md-4">
<small class="text-uppercase text-muted">THE OFFICIAL GUIDE</small>
<h2 class="mb-4">Mastering ABP Framework</h2>
<p class="mb-4">Written by the creator of the ABP Framework, this book will help you gain a complete
understanding of the framework and modern web application development techniques.</p>
<div class="mb-4">
<a href="https://www.amazon.com/gp/product/B097Z2DM8Q/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0"
class="btn btn-success mb-1">
Buy on Amazon US
</a>
&nbsp;
<a href="https://www.packtpub.com/product/mastering-abp-framework/9781801079242"
class="btn btn-primary mb-1">
Buy on PACKT
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="my-3 text-center">
<h3>Let's improve your application!</h3>
<p>Here are some links to help you get started:</p>
</div>
<div class="card mt-4 mb-5">
<div class="card-body">
<div class="row text-center justify-content-md-center">
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Learn the ABP Framework',
description:
'Explore the compherensive documentation to learn how to build a modern web application.',
links: [
{
href: 'https://docs.abp.io/en/abp/latest?ref=tmpl',
label: 'See Documents'
}
]
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Samples',
description: 'See the example projects built with the ABP Framework.',
links: [
{
href: 'https://docs.abp.io/en/abp/latest/Samples/Index?ref=tmpl',
label: 'All samples'
}
]
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'ABP Community',
description: 'Get involved with a vibrant community and become a contributor.',
links: [
{
href: 'https://community.abp.io/',
label: 'Community'
},
{
href: 'https://docs.abp.io/en/abp/latest/Contribution/Index?ref=tmpl',
label: 'Contribute'
}
]
}
}
"
></ng-container>
</div>
<div class="row text-center mt-lg-3 justify-content-md-center">
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'ABP Blog',
description: 'Take a look at our recently published articles.',
links: [
{
href: 'https://blog.abp.io/abp?ref=tmpl',
label: 'See Blog'
}
]
}
}
"
></ng-container>
<ng-template #githubButtonsTemplate>
<p class="mb-1">
<iframe
scrolling="no"
src="https://buttons.github.io/buttons.html#href=https%3A%2F%2Fgithub.com%2Fabpframework%2Fabp&amp;title=&amp;aria-label=Star%20tabalinas%2Fjsgrid%20on%20GitHub&amp;data-icon=octicon-star&amp;data-text=Star&amp;data-size=large&amp;data-show-count=true"
style="
width: 122px;
height: 28px;
border: none;
display: inline-block;
margin-right: 4px;
"
></iframe>
<iframe
scrolling="no"
src="https://buttons.github.io/buttons.html#href=https%3A%2F%2Fgithub.com%2Fabpframework%2Fabp%2Fissues&amp;title=&amp;aria-label=Issue%20tabalinas%2Fjsgrid%20on%20GitHub&amp;data-icon=octicon-issue-opened&amp;data-text=Issue&amp;data-size=large"
style="
width: 72px;
height: 28px;
border: none;
display: inline-block;
margin-right: 4px;
"
></iframe>
<iframe
scrolling="no"
src="https://buttons.github.io/buttons.html#href=https%3A%2F%2Fgithub.com%2Fabpframework%2Fabp%2Ffork&amp;title=&amp;aria-label=Fork%20tabalinas%2Fjsgrid%20on%20GitHub&amp;data-icon=octicon-repo-forked&amp;data-text=Fork&amp;data-size=large&amp;"
style="width: 72px; height: 28px; border: none; display: inline-block"
></iframe>
</p>
</ng-template>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Github',
description:
'Do you love the ABP Framework? Please <strong>give a star</strong> to support it!',
links: [
{
href: 'https://github.com/abpframework/abp/issues/new?template=feature.md',
label: 'Request a feature'
}
],
customTemplate: githubButtonsTemplate
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
starterLinkTemplate;
context: {
$implicit: {
title: 'Stackoverflow',
description: 'See answers to previously asked questions or ask a new one.',
links: [
{
href: 'https://stackoverflow.com/questions/tagged/abp',
label: 'Questions'
},
{
href: 'https://stackoverflow.com/questions/ask',
label: 'Ask a Question'
}
]
}
}
"
></ng-container>
</div>
</div>
</div>
<div class="mt-5 my-3 text-center">
<h3>Meet the ABP Commercial</h3>
<p>A Complete Web Application Platform Built on the ABP Framework</p>
</div>
<div class="card mt-4 mb-5">
<div class="card-body">
<p class="px-lg-5 mx-lg-5 py-3 text-center">
<a href="https://commercial.abp.io/" target="_blank">ABP Commercial</a> is a platform based
on the open source ABP framework. It provides pre-built application modules, rapid
application development tooling, professional UI themes, premium support and more.
</p>
<div class="row text-center justify-content-md-center">
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Startup Templates',
href: 'https://commercial.abp.io/startup-templates?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Application Modules',
href: 'https://commercial.abp.io/modules?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Developer<br />Tools',
href: 'https://commercial.abp.io/tools?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'UI<br />Themes',
href: 'https://commercial.abp.io/themes?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Premium Support',
href: 'https://support.abp.io/QA/Questions?ref=tmpl'
}
}
"
></ng-container>
<ng-container
*ngTemplateOutlet="
featuresTemplate;
context: {
$implicit: {
title: 'Additional Services',
href: 'https://commercial.abp.io/additional-services?ref=tmpl'
}
}
"
></ng-container>
</div>
</div>
</div>
<div class="mb-5 text-center">
<p class="align-middle">
<a href="https://twitter.com/abpframework" target="_blank" class="mx-2"
><i class="fa fa-twitter" aria-hidden="true"></i><span class="text-secondary"> Abp Framework</span></a
>
<a href="https://twitter.com/abpcommercial" target="_blank" class="mx-2"
><i class="fa fa-twitter" aria-hidden="true"></i><span class="text-secondary"> Abp Commercial</span></a
>
<a href="https://github.com/abpframework/abp" target="_blank" class="mx-2"
><i class="fa fa-github" aria-hidden="true"></i><span class="text-secondary"> abpframework</span></a
>
</p>
</div>
</div>
<ng-template #starterLinkTemplate let-context>
<div class="col-lg-4 border-start">
<div class="p-4">
<h5 class="mb-3">
<i class="fas fa-cubes text-secondary d-block my-3 fa-2x" aria-hidden="true"></i> {{ context.title }}
</h5>
<p [innerHTML]="context.description"></p>
<ng-container
*ngIf="context.customTemplate"
[ngTemplateOutlet]="context.customTemplate"
></ng-container>
<a
*ngFor="let link of context.links"
[href]="link.href"
target="_blank"
class="btn btn-link px-1"
>{{ link.label }} <i class="fas fa-chevron-right" aria-hidden="true"></i
></a>
</div>
</div>
</ng-template>
<ng-template #featuresTemplate let-context>
<div class="col-lg-2 border-start">
<div class="p-3">
<h6>
<i class="fas fa-plus d-block mb-3 fa- 2x text-secondary" aria-hidden="true"></i>
<span [innerHTML]="context.title"></span>
<a [href]="context.href" target="_blank" class="d-block mt-2 btn btn-sm btn-link"
>Details <i class="fas fa-chevron-right" aria-hidden="true"></i
></a>
</h6>
</div>
</div>
</ng-template>
<style scoped>
.col-lg-2.border-start:nth-of-type(6n + 1) {
border-left: 0 !important;
}
.col-lg-4.border-start:nth-of-type(3n + 1) {
border-left: 0 !important;
}
@media (max-width: 991px) {
.border-start {
border-left: 0 !important;
}
}
</style>

View File

@ -0,0 +1 @@
/* Styles for the home component */

View File

@ -0,0 +1,99 @@
import { CoreTestingModule } from "@abp/ng.core/testing";
import { ThemeSharedTestingModule } from "@abp/ng.theme.shared/testing";
import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
import { NgxValidateCoreModule } from "@ngx-validate/core";
import { HomeComponent } from "./home.component";
import { OAuthService } from 'angular-oauth2-oidc';
import { AuthService } from '@abp/ng.core';
describe("HomeComponent", () => {
let fixture: ComponentFixture<HomeComponent>;
const mockOAuthService = jasmine.createSpyObj('OAuthService', ['hasValidAccessToken'])
const mockAuthService = jasmine.createSpyObj('AuthService', ['navigateToLogin'])
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [HomeComponent],
imports: [
CoreTestingModule.withConfig(),
ThemeSharedTestingModule.withConfig(),
NgxValidateCoreModule,
],
providers: [
/* mock providers here */
{
provide: OAuthService,
useValue: mockOAuthService
},
{
provide: AuthService,
useValue: mockAuthService
}
],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
fixture.detectChanges();
});
it("should be initiated", () => {
expect(fixture.componentInstance).toBeTruthy();
});
describe('when login state is true', () => {
beforeAll(() => {
mockOAuthService.hasValidAccessToken.and.returnValue(true)
});
it("hasLoggedIn should be true", () => {
expect(fixture.componentInstance.hasLoggedIn).toBeTrue();
expect(mockOAuthService.hasValidAccessToken).toHaveBeenCalled()
})
it("button should not be exists", () => {
const element = fixture.nativeElement
const button = element.querySelector('[role="button"]')
expect(button).toBeNull()
})
})
describe('when login state is false', () => {
beforeAll(() => {
mockOAuthService.hasValidAccessToken.and.returnValue(false)
});
it("hasLoggedIn should be false", () => {
expect(fixture.componentInstance.hasLoggedIn).toBeFalse();
expect(mockOAuthService.hasValidAccessToken).toHaveBeenCalled()
})
it("button should be exists", () => {
const element = fixture.nativeElement
const button = element.querySelector('[role="button"]')
expect(button).toBeDefined()
})
describe('when button clicked', () => {
beforeEach(() => {
const element = fixture.nativeElement
const button = element.querySelector('[role="button"]')
button.click()
});
it("navigateToLogin have been called", () => {
expect(mockAuthService.navigateToLogin).toHaveBeenCalled()
})
})
})
});

View File

@ -0,0 +1,19 @@
import { AuthService } from '@abp/ng.core';
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent {
get hasLoggedIn(): boolean {
return this.authService.isAuthenticated;
}
constructor(private authService: AuthService) {}
login() {
this.authService.navigateToLogin();
}
}

View File

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { HomeRoutingModule } from './home-routing.module';
import { HomeComponent } from './home.component';
@NgModule({
declarations: [HomeComponent],
imports: [SharedModule, HomeRoutingModule],
})
export class HomeModule {}

View File

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

View File

@ -0,0 +1,56 @@
import type { AuthorDto, CreateAuthorDto, GetAuthorListDto, UpdateAuthorDto } from './models';
import { RestService, Rest } from '@abp/ng.core';
import type { PagedResultDto } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class AuthorService {
apiName = 'Default';
create = (input: CreateAuthorDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, AuthorDto>({
method: 'POST',
url: '/api/app/author',
body: input,
},
{ apiName: this.apiName,...config });
delete = (id: string, config?: Partial<Rest.Config>) =>
this.restService.request<any, void>({
method: 'DELETE',
url: `/api/app/author/${id}`,
},
{ apiName: this.apiName,...config });
get = (id: string, config?: Partial<Rest.Config>) =>
this.restService.request<any, AuthorDto>({
method: 'GET',
url: `/api/app/author/${id}`,
},
{ apiName: this.apiName,...config });
getList = (input: GetAuthorListDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, PagedResultDto<AuthorDto>>({
method: 'GET',
url: '/api/app/author',
params: { filter: input.filter, sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount },
},
{ apiName: this.apiName,...config });
update = (id: string, input: UpdateAuthorDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, void>({
method: 'PUT',
url: `/api/app/author/${id}`,
body: input,
},
{ apiName: this.apiName,...config });
constructor(private restService: RestService) {}
}

View File

@ -0,0 +1,2 @@
export * from './author.service';
export * from './models';

View File

@ -0,0 +1,23 @@
import type { EntityDto, PagedAndSortedResultRequestDto } from '@abp/ng.core';
export interface AuthorDto extends EntityDto<string> {
name?: string;
birthDate?: string;
shortBio?: string;
}
export interface CreateAuthorDto {
name: string;
birthDate: string;
shortBio?: string;
}
export interface GetAuthorListDto extends PagedAndSortedResultRequestDto {
filter?: string;
}
export interface UpdateAuthorDto {
name: string;
birthDate: string;
shortBio?: string;
}

View File

@ -0,0 +1,55 @@
import type { BookIssueDto, BookIssueListDto } from './models';
import { RestService, Rest } from '@abp/ng.core';
import type { PagedResultDto } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class BookIssueService {
apiName = 'Default';
create = (input: BookIssueDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, BookIssueDto>({
method: 'POST',
url: '/api/app/book-issue',
body: input,
},
{ apiName: this.apiName,...config });
delete = (id: number, config?: Partial<Rest.Config>) =>
this.restService.request<any, void>({
method: 'DELETE',
url: `/api/app/book-issue/${id}`,
},
{ apiName: this.apiName,...config });
get = (id: number, config?: Partial<Rest.Config>) =>
this.restService.request<any, BookIssueDto>({
method: 'GET',
url: `/api/app/book-issue/${id}`,
},
{ apiName: this.apiName,...config });
getList = (config?: Partial<Rest.Config>) =>
this.restService.request<any, PagedResultDto<BookIssueListDto>>({
method: 'GET',
url: '/api/app/book-issue',
},
{ apiName: this.apiName,...config });
update = (id: number, input: BookIssueDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, void>({
method: 'PUT',
url: `/api/app/book-issue/${id}`,
body: input,
},
{ apiName: this.apiName,...config });
constructor(private restService: RestService) {}
}

View File

@ -0,0 +1,2 @@
export * from './book-issue.service';
export * from './models';

View File

@ -0,0 +1,13 @@
import type { EntityDto } from '@abp/ng.core';
export interface BookIssueDto extends EntityDto<number> {
bookId?: string;
customerId: number;
}
export interface BookIssueListDto {
bookIssueId: number;
bookName?: string;
customerName?: string;
issueDate?: string;
}

View File

@ -0,0 +1,15 @@
import { mapEnumToOptions } from '@abp/ng.core';
export enum BookType {
Undefined = 0,
Adventure = 1,
Biography = 2,
Dystopia = 3,
Fantastic = 4,
Horror = 5,
Science = 6,
ScienceFiction = 7,
Poetry = 8,
}
export const bookTypeOptions = mapEnumToOptions(BookType);

View File

@ -0,0 +1,64 @@
import type { BookDto, CreateUpdateBookDto } from './models';
import { RestService, Rest } from '@abp/ng.core';
import type { PagedAndSortedResultRequestDto, PagedResultDto } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class BookService {
apiName = 'Default';
create = (input: CreateUpdateBookDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, BookDto>({
method: 'POST',
url: '/api/app/book',
body: input,
},
{ apiName: this.apiName,...config });
delete = (id: string, config?: Partial<Rest.Config>) =>
this.restService.request<any, void>({
method: 'DELETE',
url: `/api/app/book/${id}`,
},
{ apiName: this.apiName,...config });
get = (id: string, config?: Partial<Rest.Config>) =>
this.restService.request<any, BookDto>({
method: 'GET',
url: `/api/app/book/${id}`,
},
{ apiName: this.apiName,...config });
getBookDropDown = (config?: Partial<Rest.Config>) =>
this.restService.request<any, BookDto[]>({
method: 'GET',
url: '/api/app/book/book-drop-down',
},
{ apiName: this.apiName,...config });
getList = (input: PagedAndSortedResultRequestDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, PagedResultDto<BookDto>>({
method: 'GET',
url: '/api/app/book',
params: { sorting: input.sorting, skipCount: input.skipCount, maxResultCount: input.maxResultCount },
},
{ apiName: this.apiName,...config });
update = (id: string, input: CreateUpdateBookDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, BookDto>({
method: 'PUT',
url: `/api/app/book/${id}`,
body: input,
},
{ apiName: this.apiName,...config });
constructor(private restService: RestService) {}
}

View File

@ -0,0 +1,3 @@
export * from './book-type.enum';
export * from './book.service';
export * from './models';

View File

@ -0,0 +1,16 @@
import type { AuditedEntityDto } from '@abp/ng.core';
import type { BookType } from './book-type.enum';
export interface BookDto extends AuditedEntityDto<string> {
name?: string;
type: BookType;
publishDate?: string;
price: number;
}
export interface CreateUpdateBookDto {
name: string;
type: BookType;
publishDate: string;
price: number;
}

View File

@ -0,0 +1,63 @@
import type { CustomerDto } from './models';
import { RestService, Rest } from '@abp/ng.core';
import type { PagedResultDto } from '@abp/ng.core';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class CustomerService {
apiName = 'Default';
create = (input: CustomerDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, CustomerDto>({
method: 'POST',
url: '/api/app/customer',
body: input,
},
{ apiName: this.apiName,...config });
delete = (id: number, config?: Partial<Rest.Config>) =>
this.restService.request<any, void>({
method: 'DELETE',
url: `/api/app/customer/${id}`,
},
{ apiName: this.apiName,...config });
get = (id: number, config?: Partial<Rest.Config>) =>
this.restService.request<any, CustomerDto>({
method: 'GET',
url: `/api/app/customer/${id}`,
},
{ apiName: this.apiName,...config });
getList = (config?: Partial<Rest.Config>) =>
this.restService.request<any, PagedResultDto<CustomerDto>>({
method: 'GET',
url: '/api/app/customer',
},
{ apiName: this.apiName,...config });
getcustomerDropDown = (config?: Partial<Rest.Config>) =>
this.restService.request<any, CustomerDto[]>({
method: 'GET',
url: '/api/app/customer/customer-drop-down',
},
{ apiName: this.apiName,...config });
update = (id: number, input: CustomerDto, config?: Partial<Rest.Config>) =>
this.restService.request<any, void>({
method: 'PUT',
url: `/api/app/customer/${id}`,
body: input,
},
{ apiName: this.apiName,...config });
constructor(private restService: RestService) {}
}

View File

@ -0,0 +1,2 @@
export * from './customer.service';
export * from './models';

View File

@ -0,0 +1,8 @@
import type { EntityDto } from '@abp/ng.core';
export interface CustomerDto extends EntityDto<number> {
firstName?: string;
lastName?: string;
phone?: string;
address?: string;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
import * as Authors from './authors';
import * as BookIssued from './book-issued';
import * as Books from './books';
import * as Customers from './customers';
export { Authors, BookIssued, Books, Customers };

View File

@ -0,0 +1,53 @@
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { APP_INITIALIZER } from '@angular/core';
export const APP_ROUTE_PROVIDER = [
{ provide: APP_INITIALIZER, useFactory: configureRoutes, deps: [RoutesService], multi: true },
];
function configureRoutes(routesService: RoutesService) {
return () => {
routesService.add([
{
path: '/',
name: '::Menu:Home',
iconClass: 'fas fa-home',
order: 1,
layout: eLayoutType.application,
},
{
path: '/book-store',
name: '::Menu:BookStore',
iconClass: 'fas fa-book',
order: 2,
layout: eLayoutType.application,
},
{
path: '/books',
name: '::Menu:Books',
parentName: '::Menu:BookStore',
layout: eLayoutType.application,
requiredPolicy: 'BookStore.Books',
},
{
path: '/customer',
name: 'Customer',
iconClass: 'fas fa-user',
order: 2,
layout: eLayoutType.application,
requiredPolicy: 'BookStore.Customers',
},
{
path: '/book-issue',
name: 'book-issue',
iconClass: 'fas fa-book',
order: 2,
layout: eLayoutType.application,
requiredPolicy: 'BookStore.BookIssued',
},
]);
};
}

View File

@ -0,0 +1,23 @@
import { CoreModule } from '@abp/ng.core';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { NgModule } from '@angular/core';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgxValidateCoreModule } from '@ngx-validate/core';
@NgModule({
declarations: [],
imports: [
CoreModule,
ThemeSharedModule,
NgbDropdownModule,
NgxValidateCoreModule
],
exports: [
CoreModule,
ThemeSharedModule,
NgbDropdownModule,
NgxValidateCoreModule
],
providers: []
})
export class SharedModule {}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,26 @@
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
export const environment = {
production: true,
application: {
baseUrl,
name: 'BookStore',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44356/',
redirectUri: baseUrl,
clientId: 'BookStore_App',
responseType: 'code',
scope: 'offline_access BookStore',
requireHttps: true
},
apis: {
default: {
url: 'https://localhost:44356',
rootNamespace: 'Acme.BookStore',
},
},
} as Environment;

View File

@ -0,0 +1,26 @@
import { Environment } from '@abp/ng.core';
const baseUrl = 'http://localhost:4200';
export const environment = {
production: false,
application: {
baseUrl,
name: 'BookStore',
logoUrl: '',
},
oAuthConfig: {
issuer: 'https://localhost:44356/',
redirectUri: baseUrl,
clientId: 'BookStore_App',
responseType: 'code',
scope: 'offline_access BookStore',
requireHttps: true,
},
apis: {
default: {
url: 'https://localhost:44356',
rootNamespace: 'Acme.BookStore',
},
},
} as Environment;

BIN
angular/src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

16
angular/src/index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>BookStore</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body class="bg-light">
<app-root>
<div class="donut centered"></div>
</app-root>
</body>
</html>

13
angular/src/main.ts Normal file
View File

@ -0,0 +1,13 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));

54
angular/src/polyfills.ts Normal file
View File

@ -0,0 +1,54 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/******************************************************************
* Load `$localize` - used if i18n tags appear in Angular templates.
*/
import '@angular/localize/init';

31
angular/src/styles.scss Normal file
View File

@ -0,0 +1,31 @@
/* You can add global styles to this file, and also import other style files */
@keyframes donut-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
:root {
--lpx-logo: url('/assets/images/logo/logo-light.png');
--lpx-logo-icon: url('/assets/images/logo/logo-light-thumbnail.png');
}
.donut {
display: inline-block;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #7983ff;
border-radius: 50%;
width: 30px;
height: 30px;
animation: donut-spin 1.2s linear infinite;
&.centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}

13
angular/src/test.ts Normal file
View File

@ -0,0 +1,13 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);

2
angular/start.ps1 Normal file
View File

@ -0,0 +1,2 @@
yarn
yarn start

15
angular/tsconfig.app.json Normal file
View File

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

32
angular/tsconfig.json Normal file
View File

@ -0,0 +1,32 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "es2020",
"lib": [
"es2018",
"dom"
],
"paths": {
"@proxy": [
"src/app/proxy/index.ts"
],
"@proxy/*": [
"src/app/proxy/*"
]
},
"useDefineForClassFields": false
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false
}
}

View File

@ -0,0 +1,18 @@
/* 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",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

8765
angular/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

1
aspnet-core/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
**/wwwroot/libs/** linguist-vendored

265
aspnet-core/.gitignore vendored Normal file
View File

@ -0,0 +1,265 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# BookStore
src/Acme.BookStore.Web/Logs/*
src/Acme.BookStore.Web.Host/Logs/*
src/Acme.BookStore.AuthServer/Logs/*
src/Acme.BookStore.HttpApi.Host/Logs/*
src/Acme.BookStore.HttpApi.Host/Logs/*
src/Acme.BookStore.DbMigrator/Logs/*
src/Acme.BookStore.Blazor.Server/Logs/*
src/Acme.BookStore.Blazor.Server.Tiered/Logs/*
# Use abp install-libs to restore.
**/wwwroot/libs/*

5
aspnet-core/.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"singleQuote": true,
"useTabs": false,
"tabWidth": 4
}

View File

@ -0,0 +1,123 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29020.237
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain", "src\Acme.BookStore.Domain\Acme.BookStore.Domain.csproj", "{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Application", "src\Acme.BookStore.Application\Acme.BookStore.Application.csproj", "{1A94A50E-06DC-43C1-80B5-B662820EC3EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.EntityFrameworkCore", "src\Acme.BookStore.EntityFrameworkCore\Acme.BookStore.EntityFrameworkCore.csproj", "{C956DD76-69C8-4A9C-83EA-D17DF83340FD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CA9AC87F-097E-4F15-8393-4BC07735A5B0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{04DBDB01-70F4-4E06-B468-8F87850B22BE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Application.Tests", "test\Acme.BookStore.Application.Tests\Acme.BookStore.Application.Tests.csproj", "{50B2631D-129C-47B3-A587-029CCD6099BC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain.Shared", "src\Acme.BookStore.Domain.Shared\Acme.BookStore.Domain.Shared.csproj", "{42F719ED-8413-4895-B5B4-5AB56079BC66}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Application.Contracts", "src\Acme.BookStore.Application.Contracts\Acme.BookStore.Application.Contracts.csproj", "{520659C8-C734-4298-A3DA-B539DB9DFC0B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.HttpApi", "src\Acme.BookStore.HttpApi\Acme.BookStore.HttpApi.csproj", "{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.HttpApi.Client", "src\Acme.BookStore.HttpApi.Client\Acme.BookStore.HttpApi.Client.csproj", "{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.EntityFrameworkCore.Tests", "test\Acme.BookStore.EntityFrameworkCore.Tests\Acme.BookStore.EntityFrameworkCore.Tests.csproj", "{1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.TestBase", "test\Acme.BookStore.TestBase\Acme.BookStore.TestBase.csproj", "{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain.Tests", "test\Acme.BookStore.Domain.Tests\Acme.BookStore.Domain.Tests.csproj", "{E512F4D9-9375-480F-A2F6-A46509F9D824}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.HttpApi.Client.ConsoleTestApp", "test\Acme.BookStore.HttpApi.Client.ConsoleTestApp\Acme.BookStore.HttpApi.Client.ConsoleTestApp.csproj", "{EF480016-9127-4916-8735-D2466BDBC582}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.DbMigrator", "src\Acme.BookStore.DbMigrator\Acme.BookStore.DbMigrator.csproj", "{AA94D832-1CCC-4715-95A9-A483F23A1A5D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.HttpApi.Host", "src\Acme.BookStore.HttpApi.Host\Acme.BookStore.HttpApi.Host.csproj", "{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Release|Any CPU.Build.0 = Release|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Release|Any CPU.Build.0 = Release|Any CPU
{C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Release|Any CPU.Build.0 = Release|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50B2631D-129C-47B3-A587-029CCD6099BC}.Release|Any CPU.Build.0 = Release|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42F719ED-8413-4895-B5B4-5AB56079BC66}.Release|Any CPU.Build.0 = Release|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{520659C8-C734-4298-A3DA-B539DB9DFC0B}.Release|Any CPU.Build.0 = Release|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Release|Any CPU.Build.0 = Release|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Release|Any CPU.Build.0 = Release|Any CPU
{1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Release|Any CPU.Build.0 = Release|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Release|Any CPU.Build.0 = Release|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E512F4D9-9375-480F-A2F6-A46509F9D824}.Release|Any CPU.Build.0 = Release|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EF480016-9127-4916-8735-D2466BDBC582}.Release|Any CPU.Build.0 = Release|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Release|Any CPU.Build.0 = Release|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{554AD327-6DBA-4F8F-96F8-81CE7A0C863F} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{1A94A50E-06DC-43C1-80B5-B662820EC3EB} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{C956DD76-69C8-4A9C-83EA-D17DF83340FD} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{50B2631D-129C-47B3-A587-029CCD6099BC} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{42F719ED-8413-4895-B5B4-5AB56079BC66} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{520659C8-C734-4298-A3DA-B539DB9DFC0B} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{4164BDF7-F527-4E85-9CE6-E3C2D7426A27} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{3B5A0094-670D-4BB1-BFDD-61B88A8773DC} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{91853F21-9CD9-4132-BC29-A7D5D84FFFE7} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{E512F4D9-9375-480F-A2F6-A46509F9D824} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{EF480016-9127-4916-8735-D2466BDBC582} = {04DBDB01-70F4-4E06-B468-8F87850B22BE}
{AA94D832-1CCC-4715-95A9-A483F23A1A5D} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{748584B1-BA69-4F6A-81AA-F4BDE6BCE29D} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,23 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/Intellisense/CodeCompletion/IntelliSenseCompletingCharacters/CSharpCompletingCharacters/UpgradedFromVSSettings/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceDoWhileStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceFixedStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForeachStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceForStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceIfStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceLockStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceUsingStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=EnforceWhileStatementBraces/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_REDUNDANT/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=Implementations/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Generate/=Implementations/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Implementations/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:Boolean x:Key="/Default/CodeStyle/Generate/=Overrides/@KeyIndexDefined">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
</wpf:ResourceDictionary>

5
aspnet-core/NuGet.Config Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
</packageSources>
</configuration>

70
aspnet-core/README.md Normal file
View File

@ -0,0 +1,70 @@
# Acme.BookStore
## About this solution
This is a layered startup solution based on [Domain Driven Design (DDD)](https://docs.abp.io/en/abp/latest/Domain-Driven-Design) practises. All the fundamental ABP modules are already installed.
### Pre-requirements
* [.NET 8.0+ SDK](https://dotnet.microsoft.com/download/dotnet)
* [Node v18 or 20](https://nodejs.org/en)
### Configurations
The solution comes with a default configuration that works out of the box. However, you may consider to change the following configuration before running your solution:
* Check the `ConnectionStrings` in `appsettings.json` files under the `Acme.BookStore.HttpApi.Host` and `Acme.BookStore.DbMigrator` projects and change it if you need.
### Before running the application
#### Generating a Signing Certificate
In the production environment, you need to use a production signing certificate. ABP Framework sets up signing and encryption certificates in your application and expects an `openiddict.pfx` file in your application.
This certificate is already generated by ABP CLI, so most of the time you don't need to generate it yourself. However, if you need to generate a certificate, you can use the following command:
```bash
dotnet dev-certs https -v -ep openiddict.pfx -p 9d3dbfba-a400-4e7c-b7a2-779cd1b0c44f
```
> `9d3dbfba-a400-4e7c-b7a2-779cd1b0c44f` is the password of the certificate, you can change it to any password you want.
It is recommended to use **two** RSA certificates, distinct from the certificate(s) used for HTTPS: one for encryption, one for signing.
For more information, please refer to: https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html#registering-a-certificate-recommended-for-production-ready-scenarios
> Also, see the [Configuring OpenIddict](https://docs.abp.io/en/abp/latest/Deployment/Configuring-OpenIddict#production-environment) documentation for more information.
#### Install Client-Side Libraries
Run the following command in the directory of your final application:
```bash
abp install-libs
```
> This command installs all NPM packages for MVC/Razor Pages and Blazor Server UIs and this command is already run by the ABP CLI, so most of the time you don't need to run this command manually.
#### Create the Database
Run `Acme.BookStore.DbMigrator` to create the initial database. This should be done in the first run. It is also needed if a new database migration is added to the solution later.
### Solution structure
This is a layered monolith application that consists of the following applications:
* `Acme.BookStore.DbMigrator`: A console application which applies the migrations and also seeds the initial data. It is useful on development as well as on production environment.
* `Acme.BookStore.HttpApi.Host`: ASP.NET Core API application that is used to expose the APIs to the clients.
* `angular`: Angular application.
### Deploying the application
Deploying an ABP application is not different than deploying any .NET or ASP.NET Core application. However, there are some topics that you should care about when you are deploying your applications. You can check ABP's [Deployment documentation](https://docs.abp.io/en/abp/latest/Deployment/Index) before deploying your application.
### Additional resources
You can see the following resources to learn more about your solution and the ABP Framework:
* [Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1)
* [Application Startup Template Structure](https://docs.abp.io/en/abp/latest/Startup-Templates/Application)
* [LeptonX Lite Angular UI](https://docs.abp.io/en/abp/latest/Themes/LeptonXLite/Angular)

19
aspnet-core/common.props Normal file
View File

@ -0,0 +1,19 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Version>1.0.0</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<AbpProjectType>app</AbpProjectType>
</PropertyGroup>
<Target Name="NoWarnOnRazorViewImportedTypeConflicts" BeforeTargets="RazorCoreCompile">
<PropertyGroup>
<NoWarn>$(NoWarn);0436</NoWarn>
</PropertyGroup>
</Target>
<ItemGroup>
<Content Remove="$(UserProfile)\.nuget\packages\*\*\contentFiles\any\*\*.abppkg*" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<RootNamespace>Acme.BookStore</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Acme.BookStore.Domain.Shared\Acme.BookStore.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.ObjectExtending" Version="8.1.3" />
<PackageReference Include="Volo.Abp.Account.Application.Contracts" Version="8.1.3" />
<PackageReference Include="Volo.Abp.Identity.Application.Contracts" Version="8.1.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application.Contracts" Version="8.1.3" />
<PackageReference Include="Volo.Abp.TenantManagement.Application.Contracts" Version="8.1.3" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application.Contracts" Version="8.1.3" />
<PackageReference Include="Volo.Abp.SettingManagement.Application.Contracts" Version="8.1.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.Authors
{
public class AuthorDto : EntityDto<Guid>
{
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public string ShortBio { get; set; }
}
public class GetAuthorListDto : PagedAndSortedResultRequestDto
{
public string? Filter { get; set; }
}
public class CreateAuthorDto
{
[Required]
[StringLength(AuthorConsts.MaxNameLength)]
public string Name { get; set; } = string.Empty;
[Required]
public DateTime BirthDate { get; set; }
public string? ShortBio { get; set; }
}
public class UpdateAuthorDto
{
[Required]
[StringLength(AuthorConsts.MaxNameLength)]
public string Name { get; set; } = string.Empty;
[Required]
public DateTime BirthDate { get; set; }
public string? ShortBio { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore.Authors;
public interface IAuthorAppService : IApplicationService
{
Task<AuthorDto> GetAsync(Guid id);
Task<PagedResultDto<AuthorDto>> GetListAsync(GetAuthorListDto input);
Task<AuthorDto> CreateAsync(CreateAuthorDto input);
Task UpdateAsync(Guid id, UpdateAuthorDto input);
Task DeleteAsync(Guid id);
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.BookIssued
{
public class BookIssueDto : EntityDto<int>
{
[Required]
public Guid bookId { get; set; }
[Required]
public int customerId { get; set; }
}
public class BookIssueListDto
{
public int bookIssueId { get; set; }
public string bookName { get; set; }
public string customerName { get; set; }
public DateTime issueDate { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore.BookIssued
{
public interface IBookIssueAppService : IApplicationService
{
Task<BookIssueDto> GetAsync(int id);
Task<PagedResultDto<BookIssueListDto>> GetListAsync();
Task<BookIssueDto> CreateAsync(BookIssueDto input);
Task UpdateAsync(int id, BookIssueDto input);
Task DeleteAsync(int id);
}
}

View File

@ -0,0 +1,28 @@
using Volo.Abp.Account;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace Acme.BookStore;
[DependsOn(
typeof(BookStoreDomainSharedModule),
typeof(AbpAccountApplicationContractsModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpIdentityApplicationContractsModule),
typeof(AbpPermissionManagementApplicationContractsModule),
typeof(AbpSettingManagementApplicationContractsModule),
typeof(AbpTenantManagementApplicationContractsModule),
typeof(AbpObjectExtendingModule)
)]
public class BookStoreApplicationContractsModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
BookStoreDtoExtensions.Configure();
}
}

View File

@ -0,0 +1,28 @@
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace Acme.BookStore;
public static class BookStoreDtoExtensions
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
/* You can add extension properties to DTOs
* defined in the depended modules.
*
* Example:
*
* ObjectExtensionManager.Instance
* .AddOrUpdateProperty<IdentityRoleDto, string>("Title");
*
* See the documentation for more:
* https://docs.abp.io/en/abp/latest/Object-Extensions
*/
});
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.Books
{
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Acme.BookStore.Books
{
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
public string Name { get; set; } = string.Empty;
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; } = DateTime.Now;
[Required]
public float Price { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore.Books
{
public interface IBookAppService :
ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto> //Used to create/update a book
{
Task<List<BookDto>> GetBookDropDown();
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore.Customers
{
public class CustomerDto : EntityDto<int>
{
[Required]
public string firstName { get; set; }
[Required]
public string lastName { get; set; }
[Required]
public string phone { get; set; }
[Required]
public string address { get; set; }
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore.Customers
{
public interface ICustomerAppService : IApplicationService
{
Task<CustomerDto> GetAsync(int id);
Task<PagedResultDto<CustomerDto>> GetListAsync();
Task<CustomerDto> CreateAsync(CustomerDto input);
Task UpdateAsync(int id, CustomerDto input);
Task DeleteAsync(int id);
Task<List<CustomerDto>> GetcustomerDropDown();
}
}

View File

@ -0,0 +1,54 @@
using Acme.BookStore.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace Acme.BookStore.Permissions;
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var myGroup = context.AddGroup(BookStorePermissions.GroupName);
//Define your own permissions here. Example:
//myGroup.AddPermission(BookStorePermissions.MyPermission1, L("Permission:MyPermission1"));
var booksPermission = myGroup.AddPermission(BookStorePermissions.Books.Default, L("Permission:Books"));
booksPermission.AddChild(BookStorePermissions.Books.Create, L("Permission:Books.Create"));
booksPermission.AddChild(BookStorePermissions.Books.Edit, L("Permission:Books.Edit"));
booksPermission.AddChild(BookStorePermissions.Books.Delete, L("Permission:Books.Delete"));
var authorsPermission = myGroup.AddPermission(
BookStorePermissions.Authors.Default, L("Permission:Authors"));
authorsPermission.AddChild(
BookStorePermissions.Authors.Create, L("Permission:Authors.Create"));
authorsPermission.AddChild(
BookStorePermissions.Authors.Edit, L("Permission:Authors.Edit"));
authorsPermission.AddChild(
BookStorePermissions.Authors.Delete, L("Permission:Authors.Delete"));
var customersPermission = myGroup.AddPermission(
BookStorePermissions.Customers.Default, L("Permission:Customers"));
customersPermission.AddChild(
BookStorePermissions.Customers.Create, L("Permission:Customers.Create"));
customersPermission.AddChild(
BookStorePermissions.Customers.Edit, L("Permission:Customers.Edit"));
customersPermission.AddChild(
BookStorePermissions.Customers.Delete, L("Permission:Customers.Delete"));
var bookissuedPermission = myGroup.AddPermission(
BookStorePermissions.BookIssued.Default, L("Permission:BookIssued"));
bookissuedPermission.AddChild(
BookStorePermissions.BookIssued.Create, L("Permission:BookIssued.Create"));
bookissuedPermission.AddChild(
BookStorePermissions.BookIssued.Edit, L("Permission:BookIssued.Edit"));
bookissuedPermission.AddChild(
BookStorePermissions.BookIssued.Delete, L("Permission:BookIssued.Delete"));
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<BookStoreResource>(name);
}
}

View File

@ -0,0 +1,38 @@
namespace Acme.BookStore.Permissions;
public static class BookStorePermissions
{
public const string GroupName = "BookStore";
public static class Books
{
public const string Default = GroupName + ".Books";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
public static class Authors
{
public const string Default = GroupName + ".Authors";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
public static class Customers
{
public const string Default = GroupName + ".Customers";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
public static class BookIssued
{
public const string Default = GroupName + ".BookIssued";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
//Add your own permission names. Example:
//public const string MyPermission1 = GroupName + ".MyPermission1";
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>Acme.BookStore</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Acme.BookStore.Domain\Acme.BookStore.Domain.csproj" />
<ProjectReference Include="..\Acme.BookStore.Application.Contracts\Acme.BookStore.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Account.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.Identity.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.TenantManagement.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application" Version="8.1.3" />
<PackageReference Include="Volo.Abp.SettingManagement.Application" Version="8.1.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore.Authors
{
[Authorize(BookStorePermissions.Authors.Default)]
public class AuthorAppService : BookStoreAppService, IAuthorAppService
{
private readonly IAuthorRepository _authorRepository;
private readonly AuthorManager _authorManager;
public AuthorAppService(
IAuthorRepository authorRepository,
AuthorManager authorManager)
{
_authorRepository = authorRepository;
_authorManager = authorManager;
}
public async Task<AuthorDto> GetAsync(Guid id)
{
var author = await _authorRepository.GetAsync(id);
return ObjectMapper.Map<Author, AuthorDto>(author);
}
public async Task<PagedResultDto<AuthorDto>> GetListAsync(GetAuthorListDto input)
{
if (input.Sorting.IsNullOrWhiteSpace())
{
input.Sorting = nameof(Author.Name);
}
var authors = await _authorRepository.GetListAsync(
input.SkipCount,
input.MaxResultCount,
input.Sorting,
input.Filter
);
var totalCount = input.Filter == null
? await _authorRepository.CountAsync()
: await _authorRepository.CountAsync(
author => author.Name.Contains(input.Filter));
return new PagedResultDto<AuthorDto>(
totalCount,
ObjectMapper.Map<List<Author>, List<AuthorDto>>(authors)
);
}
[Authorize(BookStorePermissions.Authors.Create)]
public async Task<AuthorDto> CreateAsync(CreateAuthorDto input)
{
var author = await _authorManager.CreateAsync(
input.Name,
input.BirthDate,
input.ShortBio
);
await _authorRepository.InsertAsync(author);
return ObjectMapper.Map<Author, AuthorDto>(author);
}
[Authorize(BookStorePermissions.Authors.Edit)]
public async Task UpdateAsync(Guid id, UpdateAuthorDto input)
{
var author = await _authorRepository.GetAsync(id);
if (author.Name != input.Name)
{
await _authorManager.ChangeNameAsync(author, input.Name);
}
author.BirthDate = input.BirthDate;
author.ShortBio = input.ShortBio;
await _authorRepository.UpdateAsync(author);
}
[Authorize(BookStorePermissions.Authors.Delete)]
public async Task DeleteAsync(Guid id)
{
await _authorRepository.DeleteAsync(id);
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Acme.BookStore.BookIssued;
using Volo.Abp.Application.Dtos;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
namespace Acme.BookStore.BookIssued
{
public class BookIssueAppService : BookStoreAppService, IBookIssueAppService
{
private readonly IBookIssueRepository _bookIssueRepository;
private readonly BookIssueManager _bookIssueManager;
public BookIssueAppService(
IBookIssueRepository bookIssueRepository,
BookIssueManager bookIssueManager)
{
_bookIssueRepository = bookIssueRepository;
_bookIssueManager = bookIssueManager;
}
public async Task<BookIssueDto> GetAsync(int id)
{
var cus = await _bookIssueRepository.GetAsync(id);
return ObjectMapper.Map<BookIssue, BookIssueDto>(cus);
}
public async Task<PagedResultDto<BookIssueListDto>> GetListAsync()
{
var cus = await _bookIssueRepository.BookIssueList();
return new PagedResultDto<BookIssueListDto>(
cus.Count(),
ObjectMapper.Map<List<BookIssueList>, List<BookIssueListDto>>(cus)
);
}
[Authorize(BookStorePermissions.BookIssued.Create)]
public async Task<BookIssueDto> CreateAsync(BookIssueDto input)
{
var cus = ObjectMapper.Map<BookIssueDto, BookIssue>(input);
var cust = await _bookIssueManager.CreateAsync(cus);
var res = await _bookIssueRepository.InsertAsync(cust);
return ObjectMapper.Map<BookIssue, BookIssueDto>(cust);
}
[Authorize(BookStorePermissions.BookIssued.Edit)]
public async Task UpdateAsync(int id, BookIssueDto input)
{
var cus = await _bookIssueRepository.GetAsync(id);
await _bookIssueRepository.UpdateAsync(cus);
}
[Authorize(BookStorePermissions.BookIssued.Delete)]
public async Task DeleteAsync(int id)
{
await _bookIssueRepository.DeleteAsync(id);
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using Acme.BookStore.Localization;
using Volo.Abp.Application.Services;
namespace Acme.BookStore;
/* Inherit your application services from this class.
*/
public abstract class BookStoreAppService : ApplicationService
{
protected BookStoreAppService()
{
LocalizationResource = typeof(BookStoreResource);
}
}

View File

@ -0,0 +1,25 @@
using AutoMapper;
using Acme.BookStore.Books;
using Acme.BookStore.Authors;
using Acme.BookStore.Customers;
using Acme.BookStore.BookIssued;
using System.Collections.Generic;
namespace Acme.BookStore;
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book, BookDto>();
//CreateMap<List<Book>, List<BookDto>>().ReverseMap();
CreateMap<CreateUpdateBookDto, Book>();
CreateMap<Author, AuthorDto>();
CreateMap<Customer, CustomerDto>().ReverseMap();
// CreateMap<List<Customer>, List<CustomerDto>>().ReverseMap();
CreateMap<BookIssue, BookIssueDto>().ReverseMap();
CreateMap<BookIssueList, BookIssueListDto>().ReverseMap();
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
}
}

View File

@ -0,0 +1,31 @@
using Volo.Abp.Account;
using Volo.Abp.AutoMapper;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace Acme.BookStore;
[DependsOn(
typeof(BookStoreDomainModule),
typeof(AbpAccountApplicationModule),
typeof(BookStoreApplicationContractsModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpTenantManagementApplicationModule),
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpSettingManagementApplicationModule)
)]
public class BookStoreApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<BookStoreApplicationModule>();
});
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Acme.BookStore.Permissions;
namespace Acme.BookStore.Books
{
public class BookAppService :
CrudAppService<
Book, //The Book entity
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto>, //Used to create/update a book
IBookAppService //implement the IBookAppService
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
_bookRepository = repository;
GetPolicyName = BookStorePermissions.Books.Default;
GetListPolicyName = BookStorePermissions.Books.Default;
CreatePolicyName = BookStorePermissions.Books.Create;
UpdatePolicyName = BookStorePermissions.Books.Edit;
DeletePolicyName = BookStorePermissions.Books.Delete;
}
public async Task<List<BookDto>> GetBookDropDown()
{
var cus = await _bookRepository.GetListAsync();
cus = cus == null ? new List<Book>() : cus;
return ObjectMapper.Map<List<Book>, List<BookDto>>(cus);
}
}
}

View File

@ -0,0 +1,86 @@
using Acme.BookStore.Customers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.ObjectMapping;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
namespace Acme.BookStore.Customers
{
public class CustomerAppService: BookStoreAppService, ICustomerAppService
{
private readonly ICustomerRepository _customerRepository;
private readonly CustomerManager _customerManager;
public CustomerAppService(
ICustomerRepository customerRepository,
CustomerManager customerManager)
{
_customerRepository = customerRepository;
_customerManager = customerManager;
}
public async Task<CustomerDto> GetAsync(int id)
{
var cus = await _customerRepository.GetAsync(id);
return ObjectMapper.Map<Customer, CustomerDto>(cus);
}
public async Task<List<CustomerDto>> GetcustomerDropDown()
{
var cus = await _customerRepository.GetListAsync();
cus = cus == null ? new List<Customer>() : cus;
return ObjectMapper.Map<List<Customer>, List<CustomerDto>>(cus);
}
public async Task<PagedResultDto<CustomerDto>> GetListAsync()
{
var cus = await _customerRepository.GetListAsync();
return new PagedResultDto<CustomerDto>(
cus.Count(),
ObjectMapper.Map<List<Customer>, List<CustomerDto>>(cus)
);
}
[Authorize(BookStorePermissions.Customers.Create)]
public async Task<CustomerDto> CreateAsync(CustomerDto input)
{
var cus = ObjectMapper.Map<CustomerDto,Customer>(input);
var cust = await _customerManager.CreateAsync(cus);
await _customerRepository.InsertAsync(cust);
return ObjectMapper.Map<Customer, CustomerDto>(cust);
}
[Authorize(BookStorePermissions.Customers.Edit)]
public async Task UpdateAsync(int id, CustomerDto input)
{
var cus = await _customerRepository.GetAsync(id);
if (cus.phone == input.phone)
{
await _customerManager.ChangeNameAsync(cus, input.firstName,input.lastName);
}
await _customerRepository.UpdateAsync(cus);
}
[Authorize(BookStorePermissions.Customers.Delete)]
public async Task DeleteAsync(int id)
{
await _customerRepository.DeleteAsync(id);
}
}
}

View File

@ -0,0 +1,2 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleToAttribute("Acme.BookStore.Application.Tests")]

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