agregar vista publica de vehiculos aun faltan cambios
This commit is contained in:
16
apiferia/.editorconfig
Normal file
16
apiferia/.editorconfig
Normal 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
|
||||||
42
apiferia/.gitignore
vendored
Normal file
42
apiferia/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# Compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# Node
|
||||||
|
/node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
.idea/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
/.angular/cache
|
||||||
|
.sass-cache/
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
9
apiferia/.prettierrc
Normal file
9
apiferia/.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
||||||
27
apiferia/README.md
Normal file
27
apiferia/README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Reback
|
||||||
|
|
||||||
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.3.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
|
||||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application 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.
|
||||||
|
|
||||||
|
## 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. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||||
|
|
||||||
|
## 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.
|
||||||
167
apiferia/angular.json
Normal file
167
apiferia/angular.json
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"Reback": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:application",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/reback",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"browser": "src/main.ts",
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js",
|
||||||
|
"src/polyfills.ts",
|
||||||
|
"@angular/localize/init"
|
||||||
|
],
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"inlineStyleLanguage": "scss",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"node_modules/swiper/swiper-bundle.min.css",
|
||||||
|
"node_modules/flatpickr/dist/flatpickr.css",
|
||||||
|
"src/assets/scss/icons.scss",
|
||||||
|
"node_modules/ngx-toastr/toastr.css",
|
||||||
|
"src/assets/scss/app.scss",
|
||||||
|
"src/assets/css/bootstrap.css",
|
||||||
|
"src/assets/css/swiper-bundle.min.css",
|
||||||
|
"src/assets/css/animate.css",
|
||||||
|
"src/assets/css/jquery.fancybox.min.css",
|
||||||
|
"src/assets/css/magnific-popup.css",
|
||||||
|
"src/assets/css/nice-select.css",
|
||||||
|
"src/assets/css/font-awesome.css",
|
||||||
|
"src/assets/css/styles.css",
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
"node_modules/gumshoejs/dist/gumshoe.polyfills.js",
|
||||||
|
"node_modules/jsvectormap/dist/js/jsvectormap.min.js",
|
||||||
|
"src/assets/js/jquery.min.js",
|
||||||
|
"src/assets/js/bootstrap.min.js",
|
||||||
|
"src/assets/js/swiper-bundle.min.js",
|
||||||
|
"src/assets/js/jquery.fancybox.js",
|
||||||
|
"src/assets/js/jquery.nice-select.min.js",
|
||||||
|
"src/assets/js/lazysize.min.js",
|
||||||
|
"src/assets/js/main.js"
|
||||||
|
],
|
||||||
|
"allowedCommonJsDependencies": [
|
||||||
|
"jsvectormap",
|
||||||
|
"jsvectormap/dist/maps/world.js",
|
||||||
|
"jsvectormap/dist/maps/russia.js",
|
||||||
|
"jsvectormap/dist/maps/canada.js",
|
||||||
|
"jsvectormap/dist/maps/iraq.js",
|
||||||
|
"jsvectormap/dist/maps/spain.js",
|
||||||
|
"dropzone",
|
||||||
|
"deepmerge",
|
||||||
|
"can-use-dom",
|
||||||
|
"choices.js",
|
||||||
|
"gumshoejs",
|
||||||
|
"quill-delta",
|
||||||
|
"apexcharts",
|
||||||
|
"dayjs",
|
||||||
|
"moment",
|
||||||
|
"sweetalert2",
|
||||||
|
"jquery",
|
||||||
|
"bootstrap",
|
||||||
|
"swiper"
|
||||||
|
],
|
||||||
|
"stylePreprocessorOptions": {
|
||||||
|
"sass": {
|
||||||
|
"silenceDeprecations": [
|
||||||
|
"color-functions",
|
||||||
|
"global-builtin",
|
||||||
|
"import",
|
||||||
|
"mixed-decls"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "8mb",
|
||||||
|
"maximumError": "8mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "2kb",
|
||||||
|
"maximumError": "4kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all",
|
||||||
|
"optimization": {
|
||||||
|
"scripts": true,
|
||||||
|
"styles": {
|
||||||
|
"minify": true,
|
||||||
|
"inlineCritical": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"optimization": false,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "Reback:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "Reback:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"buildTarget": "hyper:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js",
|
||||||
|
"zone.js/testing"
|
||||||
|
],
|
||||||
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
"inlineStyleLanguage": "scss",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"analytics": false
|
||||||
|
}
|
||||||
|
}
|
||||||
17957
apiferia/package-lock.json
generated
Normal file
17957
apiferia/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
89
apiferia/package.json
Normal file
89
apiferia/package.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "reback",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"test": "ng test",
|
||||||
|
"format": "prettier --write src/**/*.{ts,html}"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^19.0.6",
|
||||||
|
"@angular/cdk": "^19.0.5",
|
||||||
|
"@angular/common": "^19.0.6",
|
||||||
|
"@angular/compiler": "^19.0.6",
|
||||||
|
"@angular/core": "^19.0.6",
|
||||||
|
"@angular/forms": "^19.0.6",
|
||||||
|
"@angular/google-maps": "^19.0.2",
|
||||||
|
"@angular/platform-browser": "^19.0.6",
|
||||||
|
"@angular/platform-browser-dynamic": "^19.0.6",
|
||||||
|
"@angular/router": "^19.0.6",
|
||||||
|
"@ckeditor/ckeditor5-angular": "^9.1.0",
|
||||||
|
"@ckeditor/ckeditor5-build-classic": "^44.3.0",
|
||||||
|
"@ctrl/ngx-emoji-mart": "^9.2.0",
|
||||||
|
"@fullcalendar/angular": "^6.1.17",
|
||||||
|
"@fullcalendar/bootstrap": "^6.1.17",
|
||||||
|
"@fullcalendar/core": "^6.1.17",
|
||||||
|
"@fullcalendar/daygrid": "^6.1.17",
|
||||||
|
"@fullcalendar/interaction": "^6.1.17",
|
||||||
|
"@fullcalendar/list": "^6.1.17",
|
||||||
|
"@fullcalendar/timegrid": "^6.1.17",
|
||||||
|
"@iconify-json/iconamoon": "^1.2.2",
|
||||||
|
"@iconify/utils": "^2.3.0",
|
||||||
|
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
||||||
|
"@ngrx/effects": "^19.1.0",
|
||||||
|
"@ngrx/store": "^19.1.0",
|
||||||
|
"@ngrx/store-devtools": "^19.1.0",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
|
"angularx-flatpickr": "^8.1.0",
|
||||||
|
"apexcharts": "^4.5.0",
|
||||||
|
"bootstrap": "^5.3.5",
|
||||||
|
"choices.js": "^11.1.0",
|
||||||
|
"ckeditor5": "^44.3.0",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
|
"flatpickr": "^4.6.13",
|
||||||
|
"gumshoejs": "^5.1.2",
|
||||||
|
"iconify-icon": "^2.3.0",
|
||||||
|
"jsvectormap": "1.3.3",
|
||||||
|
"moment": "^2.30.1",
|
||||||
|
"ng-apexcharts": "^1.15.0",
|
||||||
|
"ng2-nouislider": "^2.0.0",
|
||||||
|
"ngrx-store-localstorage": "^19.0.0",
|
||||||
|
"ngx-clipboard": "^16.0.0",
|
||||||
|
"ngx-cookie-service": "^19.1.2",
|
||||||
|
"ngx-dropzone-wrapper": "^17.0.0",
|
||||||
|
"ngx-mask": "^19.0.6",
|
||||||
|
"ngx-progressbar": "^14.0.0",
|
||||||
|
"ngx-quill": "^27.0.1",
|
||||||
|
"ngx-toastr": "^19.0.0",
|
||||||
|
"nouislider": "^15.8.1",
|
||||||
|
"npm": "^11.2.0",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"quill": "^2.0.3",
|
||||||
|
"quill-delta": "^5.1.0",
|
||||||
|
"rxjs": "~7.8.2",
|
||||||
|
"simplebar-angular": "3.2.4",
|
||||||
|
"sweetalert2": "^11.17.2",
|
||||||
|
"swiper": "^11.2.6",
|
||||||
|
"tslib": "^2.8.1",
|
||||||
|
"zone.js": "~0.15.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "^19.0.7",
|
||||||
|
"@angular/cli": "^19.0.7",
|
||||||
|
"@angular/compiler-cli": "^19.0.0",
|
||||||
|
"@angular/localize": "^19.0.0",
|
||||||
|
"@types/jasmine": "~5.1.0",
|
||||||
|
"jasmine-core": "~5.1.0",
|
||||||
|
"karma": "~6.4.0",
|
||||||
|
"karma-chrome-launcher": "~3.2.0",
|
||||||
|
"karma-coverage": "~2.2.0",
|
||||||
|
"karma-jasmine": "~5.1.0",
|
||||||
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
|
"typescript": "~5.8.3",
|
||||||
|
"vite": "^6.2.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
apiferia/src/app/app.component.html
Normal file
8
apiferia/src/app/app.component.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<ng-progress
|
||||||
|
#progressBar
|
||||||
|
[trickleSpeed]="10"
|
||||||
|
[speed]="10"
|
||||||
|
[spinner]="false"
|
||||||
|
color="#1C84EE"
|
||||||
|
></ng-progress>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
0
apiferia/src/app/app.component.scss
vendored
Normal file
0
apiferia/src/app/app.component.scss
vendored
Normal file
29
apiferia/src/app/app.component.spec.ts
Normal file
29
apiferia/src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing'
|
||||||
|
import { AppComponent } from './app.component'
|
||||||
|
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [AppComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent)
|
||||||
|
const app = fixture.componentInstance
|
||||||
|
expect(app).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should have the 'Reback' title`, () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent)
|
||||||
|
const app = fixture.componentInstance
|
||||||
|
// expect(app.title).toEqual('Reback')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render title', () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent)
|
||||||
|
fixture.detectChanges()
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement
|
||||||
|
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, Reback')
|
||||||
|
})
|
||||||
|
})
|
||||||
53
apiferia/src/app/app.component.ts
Normal file
53
apiferia/src/app/app.component.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { Component, inject, ViewChild, type OnInit } from '@angular/core'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import {
|
||||||
|
NavigationCancel,
|
||||||
|
NavigationEnd,
|
||||||
|
NavigationError,
|
||||||
|
NavigationStart,
|
||||||
|
Router,
|
||||||
|
RouterOutlet,
|
||||||
|
type Event,
|
||||||
|
} from '@angular/router'
|
||||||
|
import { TitleService } from '@core/services/title.service'
|
||||||
|
import { NgProgressbar, NgProgressRef } from 'ngx-progressbar'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, RouterOutlet, NgProgressbar],
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrl: './app.component.scss',
|
||||||
|
})
|
||||||
|
export class AppComponent implements OnInit {
|
||||||
|
@ViewChild(NgProgressRef) progressBar!: NgProgressRef
|
||||||
|
|
||||||
|
private titleService = inject(TitleService)
|
||||||
|
private router = inject(Router)
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.router.events.subscribe((event: Event) => {
|
||||||
|
this.checkRouteChange(event)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.titleService.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// show Loader when route change
|
||||||
|
checkRouteChange(routerEvent: Event) {
|
||||||
|
if (routerEvent instanceof NavigationStart) {
|
||||||
|
this.progressBar.start()
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
routerEvent instanceof NavigationEnd ||
|
||||||
|
routerEvent instanceof NavigationCancel ||
|
||||||
|
routerEvent instanceof NavigationError
|
||||||
|
) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.progressBar.complete()
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
apiferia/src/app/app.config.ts
Normal file
57
apiferia/src/app/app.config.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import {
|
||||||
|
ApplicationConfig,
|
||||||
|
importProvidersFrom,
|
||||||
|
isDevMode,
|
||||||
|
provideExperimentalZonelessChangeDetection,
|
||||||
|
provideZoneChangeDetection,
|
||||||
|
} from '@angular/core'
|
||||||
|
import {
|
||||||
|
provideRouter,
|
||||||
|
withInMemoryScrolling,
|
||||||
|
type InMemoryScrollingFeature,
|
||||||
|
type InMemoryScrollingOptions,
|
||||||
|
} from '@angular/router'
|
||||||
|
|
||||||
|
import {
|
||||||
|
HTTP_INTERCEPTORS,
|
||||||
|
provideHttpClient,
|
||||||
|
withFetch,
|
||||||
|
withInterceptorsFromDi,
|
||||||
|
} from '@angular/common/http'
|
||||||
|
import { provideStore } from '@ngrx/store'
|
||||||
|
import { provideStoreDevtools } from '@ngrx/store-devtools'
|
||||||
|
import { routes } from './app.routes'
|
||||||
|
import { rootReducer } from './store'
|
||||||
|
import { localStorageSyncReducer } from './store/layout/layout-reducers'
|
||||||
|
import { DecimalPipe } from '@angular/common'
|
||||||
|
import { provideEffects } from '@ngrx/effects'
|
||||||
|
import { CalendarEffects } from './store/calendar/calendar.effects'
|
||||||
|
import { FakeBackendProvider } from './helpers/fake-backend'
|
||||||
|
import { AuthenticationEffects } from './store/authentication/authentication.effects'
|
||||||
|
import { provideToastr } from 'ngx-toastr'
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||||
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
|
|
||||||
|
// scroll
|
||||||
|
const scrollConfig: InMemoryScrollingOptions = {
|
||||||
|
scrollPositionRestoration: 'top',
|
||||||
|
anchorScrolling: 'enabled',
|
||||||
|
}
|
||||||
|
|
||||||
|
const inMemoryScrollingFeatures: InMemoryScrollingFeature =
|
||||||
|
withInMemoryScrolling(scrollConfig)
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
FakeBackendProvider,
|
||||||
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||||
|
provideRouter(routes, inMemoryScrollingFeatures),
|
||||||
|
DecimalPipe,
|
||||||
|
provideStore(rootReducer, { metaReducers: [localStorageSyncReducer] }),
|
||||||
|
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
|
||||||
|
provideEffects(AuthenticationEffects, CalendarEffects),
|
||||||
|
provideHttpClient(withFetch(), withInterceptorsFromDi()),
|
||||||
|
importProvidersFrom(BrowserAnimationsModule, BrowserModule),
|
||||||
|
provideToastr({}),
|
||||||
|
],
|
||||||
|
}
|
||||||
47
apiferia/src/app/app.routes.ts
Normal file
47
apiferia/src/app/app.routes.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { RedirectCommand, Router, Routes, type UrlTree } from '@angular/router'
|
||||||
|
import { AuthLayoutComponent } from './layouts/auth-layout/auth-layout.component'
|
||||||
|
import { PrivateLayoutComponent } from './layouts/private-layout/private-layout.component'
|
||||||
|
import { AuthenticationService } from './core/services/auth.service'
|
||||||
|
import { inject } from '@angular/core'
|
||||||
|
import { CarViewComponent } from './views/car-view/car-view.component';
|
||||||
|
|
||||||
|
export const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'dashboard/analytics',
|
||||||
|
pathMatch: 'full',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: PrivateLayoutComponent,
|
||||||
|
canActivate: [
|
||||||
|
() => {
|
||||||
|
const currentUser = inject(AuthenticationService).session
|
||||||
|
const router: Router = inject(Router)
|
||||||
|
if (currentUser) return true
|
||||||
|
const urlTree: UrlTree = router.parseUrl('/auth/sign-in')
|
||||||
|
return new RedirectCommand(urlTree, { skipLocationChange: true })
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./views/views.route').then((mod) => mod.VIEW_ROUTES),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: AuthLayoutComponent,
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./views/other-pages/other-page.route').then(
|
||||||
|
(mod) => mod.OTHER_PAGES_ROUTES
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'auth',
|
||||||
|
component: AuthLayoutComponent,
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./views/auth/auth.route').then((mod) => mod.AUTH_ROUTES),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'cars',
|
||||||
|
component: CarViewComponent
|
||||||
|
},
|
||||||
|
]
|
||||||
44
apiferia/src/app/common/apexchart.model.ts
Normal file
44
apiferia/src/app/common/apexchart.model.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
ApexAxisChartSeries,
|
||||||
|
ApexNonAxisChartSeries,
|
||||||
|
ApexChart,
|
||||||
|
ChartComponent,
|
||||||
|
ApexDataLabels,
|
||||||
|
ApexPlotOptions,
|
||||||
|
ApexYAxis,
|
||||||
|
ApexLegend,
|
||||||
|
ApexStroke,
|
||||||
|
ApexXAxis,
|
||||||
|
ApexFill,
|
||||||
|
ApexTooltip,
|
||||||
|
ApexTitleSubtitle,
|
||||||
|
ApexResponsive,
|
||||||
|
ApexAnnotations,
|
||||||
|
ApexGrid,
|
||||||
|
ApexStates,
|
||||||
|
ApexMarkers,
|
||||||
|
ApexTheme,
|
||||||
|
} from 'ng-apexcharts'
|
||||||
|
|
||||||
|
export type ChartOptions = {
|
||||||
|
series: ApexAxisChartSeries | ApexNonAxisChartSeries
|
||||||
|
chart: ApexChart
|
||||||
|
xaxis: ApexXAxis
|
||||||
|
fill: ApexFill
|
||||||
|
stroke: ApexStroke
|
||||||
|
tooltip: ApexTooltip
|
||||||
|
dataLabels: ApexDataLabels
|
||||||
|
plotOptions: ApexPlotOptions
|
||||||
|
markers: ApexMarkers
|
||||||
|
responsive: ApexResponsive[]
|
||||||
|
colors: string[]
|
||||||
|
labels: string[]
|
||||||
|
annotations: ApexAnnotations
|
||||||
|
yaxis: ApexYAxis | ApexYAxis[]
|
||||||
|
grid: ApexGrid
|
||||||
|
legend: ApexLegend
|
||||||
|
title: ApexTitleSubtitle
|
||||||
|
subtitle: ApexTitleSubtitle
|
||||||
|
states: ApexStates
|
||||||
|
theme: ApexTheme
|
||||||
|
}
|
||||||
12
apiferia/src/app/common/constants.ts
Normal file
12
apiferia/src/app/common/constants.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
type CurrencyType = '₹' | '$' | '€'
|
||||||
|
|
||||||
|
export const currency: CurrencyType = '$'
|
||||||
|
|
||||||
|
export const currentYear = new Date().getFullYear()
|
||||||
|
|
||||||
|
export const credits = {
|
||||||
|
name: 'Techzaa',
|
||||||
|
buyLink: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const basePath: string = '/'
|
||||||
911
apiferia/src/app/common/menu-meta.ts
Normal file
911
apiferia/src/app/common/menu-meta.ts
Normal file
@@ -0,0 +1,911 @@
|
|||||||
|
export type MenuItem = {
|
||||||
|
key?: string
|
||||||
|
label?: string
|
||||||
|
icon?: string
|
||||||
|
link?: string
|
||||||
|
collapsed?: boolean
|
||||||
|
subMenu?: any
|
||||||
|
isTitle?: boolean
|
||||||
|
badge?: any
|
||||||
|
parentKey?: string
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MENU: MenuItem[] = [
|
||||||
|
{
|
||||||
|
key: 'general',
|
||||||
|
label: 'GENERAL',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboards',
|
||||||
|
icon: 'iconamoon:home-duotone',
|
||||||
|
label: 'Dashboards',
|
||||||
|
collapsed: false,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'dashboard-analytics',
|
||||||
|
label: 'Analytics',
|
||||||
|
link: '/dashboard/analytics',
|
||||||
|
parentKey: 'dashboards',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboard-finance',
|
||||||
|
label: 'Finance',
|
||||||
|
link: '/dashboard/finance',
|
||||||
|
parentKey: 'dashboards',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboard-sales',
|
||||||
|
label: 'Sales',
|
||||||
|
link: '/dashboard/sales',
|
||||||
|
parentKey: 'dashboards',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps',
|
||||||
|
label: 'APPS',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce',
|
||||||
|
icon: 'iconamoon:shopping-bag-duotone',
|
||||||
|
label: 'Ecommerce',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'ecommerce-products',
|
||||||
|
label: 'Products',
|
||||||
|
link: '/ecommerce/products',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-productsdetails',
|
||||||
|
label: 'Product Details',
|
||||||
|
link: '/ecommerce/product/1',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-createproduct',
|
||||||
|
label: 'Create Product',
|
||||||
|
link: '/ecommerce/create',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-customers',
|
||||||
|
label: 'Customers',
|
||||||
|
link: '/ecommerce/customers',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-sellers',
|
||||||
|
label: 'Sellers',
|
||||||
|
link: '/ecommerce/sellers',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-orders',
|
||||||
|
label: 'Orders',
|
||||||
|
link: '/ecommerce/orders',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-orderdetails',
|
||||||
|
label: 'Order Details',
|
||||||
|
link: '/ecommerce/orders/10001',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-inventory',
|
||||||
|
label: 'Inventory',
|
||||||
|
link: '/ecommerce/inventory',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-chat',
|
||||||
|
icon: 'iconamoon:comment-dots-duotone',
|
||||||
|
label: 'Chat',
|
||||||
|
link: '/apps/chat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-email',
|
||||||
|
icon: 'iconamoon:email-duotone',
|
||||||
|
label: 'Email',
|
||||||
|
link: '/apps/email',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-calendar',
|
||||||
|
icon: 'iconamoon:calendar-1-duotone',
|
||||||
|
label: 'Calendar',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'calendar-schedule',
|
||||||
|
label: 'Schedule',
|
||||||
|
link: '/calendar/schedule',
|
||||||
|
parentKey: 'apps-calendar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'calendar-integration',
|
||||||
|
label: 'Integration',
|
||||||
|
link: '/calendar/integration',
|
||||||
|
parentKey: 'apps-calendar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'calendar-help',
|
||||||
|
label: 'Help',
|
||||||
|
link: '/calendar/help',
|
||||||
|
parentKey: 'apps-calendar',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-todo',
|
||||||
|
icon: 'iconamoon:ticket-duotone',
|
||||||
|
label: 'Todo',
|
||||||
|
link: '/apps/todo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-social',
|
||||||
|
icon: 'iconamoon:squinting-face-duotone',
|
||||||
|
label: 'Social',
|
||||||
|
link: '/apps/social',
|
||||||
|
badge: {
|
||||||
|
variant: 'danger',
|
||||||
|
text: 'Hot',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-contacts',
|
||||||
|
icon: 'iconamoon:profile-circle-duotone',
|
||||||
|
label: 'Contacts',
|
||||||
|
link: '/apps/contacts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-invoices',
|
||||||
|
icon: 'iconamoon:invoice-duotone',
|
||||||
|
label: 'Invoices',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'invoices',
|
||||||
|
label: 'Invoices',
|
||||||
|
link: '/invoices',
|
||||||
|
parentKey: 'apps-invoices',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'invoices-details',
|
||||||
|
label: 'Invoice Details',
|
||||||
|
link: '/invoice/RB6985',
|
||||||
|
parentKey: 'apps-invoices',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'feria',
|
||||||
|
label: 'FERIA DE VEHÍCULOS',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'vehicles',
|
||||||
|
icon: 'iconamoon:car-duotone',
|
||||||
|
label: 'Vehículos',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'vehicles-list',
|
||||||
|
label: 'Lista de Vehículos',
|
||||||
|
link: '/vehicles',
|
||||||
|
parentKey: 'vehicles',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'vehicles-create',
|
||||||
|
label: 'Nuevo Vehículo',
|
||||||
|
link: '/vehicles/create',
|
||||||
|
parentKey: 'vehicles',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'vehicles-import',
|
||||||
|
label: 'Importar Vehículos',
|
||||||
|
link: '/vehicles/import',
|
||||||
|
parentKey: 'vehicles',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'finance',
|
||||||
|
icon: 'iconamoon:certificate-badge-duotone',
|
||||||
|
label: 'Financiamiento',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'finance-applications',
|
||||||
|
label: 'Aplicaciones',
|
||||||
|
link: '/finance/applications',
|
||||||
|
parentKey: 'finance',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'finance-companies',
|
||||||
|
label: 'Financieras',
|
||||||
|
link: '/finance/companies',
|
||||||
|
parentKey: 'finance',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'finance-calculator',
|
||||||
|
label: 'Calculadora',
|
||||||
|
link: '/finance/calculator',
|
||||||
|
parentKey: 'finance',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'insurance',
|
||||||
|
icon: 'iconamoon:shield-yes-duotone',
|
||||||
|
label: 'Seguros',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'insurance-quotes',
|
||||||
|
label: 'Cotizaciones',
|
||||||
|
link: '/insurance/quotes',
|
||||||
|
parentKey: 'insurance',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'insurance-companies',
|
||||||
|
label: 'Aseguradoras',
|
||||||
|
link: '/insurance/companies',
|
||||||
|
parentKey: 'insurance',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sales',
|
||||||
|
icon: 'iconamoon:shopping-cart-duotone',
|
||||||
|
label: 'Ventas',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'sales-list',
|
||||||
|
label: 'Lista de Ventas',
|
||||||
|
link: '/sales',
|
||||||
|
parentKey: 'sales',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sales-reports',
|
||||||
|
label: 'Reportes',
|
||||||
|
link: '/sales/reports',
|
||||||
|
parentKey: 'sales',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sales-commissions',
|
||||||
|
label: 'Comisiones',
|
||||||
|
link: '/sales/commissions',
|
||||||
|
parentKey: 'sales',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'custom',
|
||||||
|
label: 'Custom',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pages',
|
||||||
|
label: 'Pages',
|
||||||
|
isTitle: false,
|
||||||
|
icon: 'iconamoon:copy-duotone',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'page-welcome',
|
||||||
|
label: 'Welcome',
|
||||||
|
link: '/pages/welcome',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-faqs',
|
||||||
|
label: 'FAQs',
|
||||||
|
link: '/pages/faqs',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-profile',
|
||||||
|
label: 'Profile',
|
||||||
|
link: '/pages/profile',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-coming-soon',
|
||||||
|
label: 'Coming Soon',
|
||||||
|
link: '/coming-soon',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-contact-us',
|
||||||
|
label: 'Contact Us',
|
||||||
|
link: '/pages/contact-us',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-about-us',
|
||||||
|
label: 'About Us',
|
||||||
|
link: '/pages/about-us',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-our-team',
|
||||||
|
label: 'Our Team',
|
||||||
|
link: '/pages/our-team',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-timeline',
|
||||||
|
label: 'Timeline',
|
||||||
|
link: '/pages/timeline',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-pricing',
|
||||||
|
label: 'Pricing',
|
||||||
|
link: '/pages/pricing',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-maintenance',
|
||||||
|
label: 'Maintenance',
|
||||||
|
link: '/maintenance',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-404-error',
|
||||||
|
label: '404 Error',
|
||||||
|
link: '/error-404',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-404-error2',
|
||||||
|
label: '404 Error 2',
|
||||||
|
link: '/error-404-2',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-error-404-alt',
|
||||||
|
label: 'Error 404 (alt)',
|
||||||
|
link: '/pages/error-404-alt',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-authentication',
|
||||||
|
label: 'Authentication',
|
||||||
|
isTitle: false,
|
||||||
|
icon: 'iconamoon:lock-duotone',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'sign-in',
|
||||||
|
label: 'Sign In',
|
||||||
|
link: '/auth/sign-in',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sign-in-2',
|
||||||
|
label: 'Sign In 2',
|
||||||
|
link: '/auth/sign-in-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'signup',
|
||||||
|
label: 'Sign Up',
|
||||||
|
link: '/auth/sign-up',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'signup2',
|
||||||
|
label: 'Sign Up 2',
|
||||||
|
link: '/auth/sign-up-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'reset-pass',
|
||||||
|
label: 'Reset Password',
|
||||||
|
link: '/auth/reset-pass',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'reset-pass2',
|
||||||
|
label: 'Reset Password 2',
|
||||||
|
link: '/auth/reset-pass-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lock-screen',
|
||||||
|
label: 'Lock Screen',
|
||||||
|
link: '/auth/lock-screen',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lock-screen-2',
|
||||||
|
label: 'Lock Screen 2',
|
||||||
|
link: '/auth/lock-screen-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'widgets',
|
||||||
|
icon: 'iconamoon:gift-duotone',
|
||||||
|
label: 'Widgets',
|
||||||
|
link: '/widgets',
|
||||||
|
badge: {
|
||||||
|
variant: 'info',
|
||||||
|
text: '9+',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'components',
|
||||||
|
label: 'COMPONENTS',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui',
|
||||||
|
icon: 'iconamoon:briefcase-duotone',
|
||||||
|
label: 'Base UI',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'base-ui-accordions',
|
||||||
|
label: 'Accordion',
|
||||||
|
link: '/ui/accordions',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-alerts',
|
||||||
|
label: 'Alerts',
|
||||||
|
link: '/ui/alerts',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-avatars',
|
||||||
|
label: 'Avatar',
|
||||||
|
link: '/ui/avatars',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-badges',
|
||||||
|
label: 'Badge',
|
||||||
|
link: '/ui/badges',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-breadcrumb',
|
||||||
|
label: 'Breadcrumb',
|
||||||
|
link: '/ui/breadcrumb',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-buttons',
|
||||||
|
label: 'Buttons',
|
||||||
|
link: '/ui/buttons',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-cards',
|
||||||
|
label: 'Card',
|
||||||
|
link: '/ui/cards',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-carousel',
|
||||||
|
label: 'Carousel',
|
||||||
|
link: '/ui/carousel',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-collapse',
|
||||||
|
label: 'Collapse',
|
||||||
|
link: '/ui/collapse',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-dropdowns',
|
||||||
|
label: 'Dropdown',
|
||||||
|
link: '/ui/dropdowns',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-list-group',
|
||||||
|
label: 'List Group',
|
||||||
|
link: '/ui/list-group',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-modals',
|
||||||
|
label: 'Modal',
|
||||||
|
link: '/ui/modals',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-tabs',
|
||||||
|
label: 'Tabs',
|
||||||
|
link: '/ui/tabs',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-offcanvas',
|
||||||
|
label: 'Offcanvas',
|
||||||
|
link: '/ui/offcanvas',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-pagination',
|
||||||
|
label: 'Pagination',
|
||||||
|
link: '/ui/pagination',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-placeholders',
|
||||||
|
label: 'Placeholders',
|
||||||
|
link: '/ui/placeholders',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-popovers',
|
||||||
|
label: 'Popovers',
|
||||||
|
link: '/ui/popovers',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-progress',
|
||||||
|
label: 'Progress',
|
||||||
|
link: '/ui/progress',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-scrollspy',
|
||||||
|
label: 'Scrollspy',
|
||||||
|
link: '/ui/scrollspy',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-spinners',
|
||||||
|
label: 'Spinners',
|
||||||
|
link: '/ui/spinners',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-toasts',
|
||||||
|
label: 'Toasts',
|
||||||
|
link: '/ui/toasts',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-tooltips',
|
||||||
|
label: 'Tooltips',
|
||||||
|
link: '/ui/tooltips',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui',
|
||||||
|
icon: 'iconamoon:component-duotone',
|
||||||
|
label: 'Advanced UI',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-ratings',
|
||||||
|
label: 'Ratings',
|
||||||
|
link: '/advanced/ratings',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-sweet-alert',
|
||||||
|
label: 'Sweet Alert',
|
||||||
|
link: '/advanced/alert',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-swiper-slider',
|
||||||
|
label: 'Swiper Slider',
|
||||||
|
link: '/advanced/swiper',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-scrollbar',
|
||||||
|
label: 'Scrollbar',
|
||||||
|
link: '/advanced/scrollbar',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-toastify',
|
||||||
|
label: 'Toastify',
|
||||||
|
link: '/advanced/toastify',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts',
|
||||||
|
icon: 'iconamoon:3d-duotone',
|
||||||
|
label: 'Charts',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'charts-area',
|
||||||
|
label: 'Area',
|
||||||
|
link: '/charts/area',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-bar',
|
||||||
|
label: 'Bar',
|
||||||
|
link: '/charts/bar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-bubble',
|
||||||
|
label: 'Bubble',
|
||||||
|
link: '/charts/bubble',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-candl-stick',
|
||||||
|
label: 'Candlestick',
|
||||||
|
link: '/charts/candlestick',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-column',
|
||||||
|
label: 'Column',
|
||||||
|
link: '/charts/column',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-heatmap',
|
||||||
|
label: 'Heatmap',
|
||||||
|
link: '/charts/heatmap',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-line',
|
||||||
|
label: 'Line',
|
||||||
|
link: '/charts/line',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-mixed',
|
||||||
|
label: 'Mixed',
|
||||||
|
link: '/charts/mixed',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-timeline',
|
||||||
|
label: 'Timeline',
|
||||||
|
link: '/charts/timeline',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-boxplot',
|
||||||
|
label: 'Boxplot',
|
||||||
|
link: '/charts/boxplot',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-treemap',
|
||||||
|
label: 'Treemap',
|
||||||
|
link: '/charts/treemap',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-pie',
|
||||||
|
label: 'Pie',
|
||||||
|
link: '/charts/pie',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-radar',
|
||||||
|
label: 'Radar',
|
||||||
|
link: '/charts/radar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-radial-bar',
|
||||||
|
label: 'RadialBar',
|
||||||
|
link: '/charts/radial-bar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-scatter',
|
||||||
|
label: 'Scatter',
|
||||||
|
link: '/charts/scatter',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-polar-area',
|
||||||
|
label: 'Polar Area',
|
||||||
|
link: '/charts/polar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms',
|
||||||
|
icon: 'iconamoon:cheque-duotone',
|
||||||
|
label: 'Forms',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'forms-basic-elements',
|
||||||
|
label: 'Basic Elements',
|
||||||
|
link: '/forms/basic',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-checkbox&radio',
|
||||||
|
label: 'Checkbox & Radio',
|
||||||
|
link: '/forms/checkbox',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-choice-select',
|
||||||
|
label: 'Choice Select',
|
||||||
|
link: '/forms/select',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-clipboard',
|
||||||
|
label: 'Clipboard',
|
||||||
|
link: '/forms/clipboard',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-flat-picker',
|
||||||
|
label: 'Flatpicker',
|
||||||
|
link: '/forms/flat-picker',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-validation',
|
||||||
|
label: 'Validation',
|
||||||
|
link: '/forms/validation',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-wizard',
|
||||||
|
label: 'Wizard',
|
||||||
|
link: '/forms/wizard',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-file-uploads',
|
||||||
|
label: 'File Upload',
|
||||||
|
link: '/forms/file-uploads',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-editors',
|
||||||
|
label: 'Editors',
|
||||||
|
link: '/forms/editors',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-input-mask',
|
||||||
|
label: 'Input Mask',
|
||||||
|
link: '/forms/input-mask',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-slider',
|
||||||
|
label: 'Slider',
|
||||||
|
link: '/forms/slider',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tables',
|
||||||
|
icon: 'iconamoon:box-duotone',
|
||||||
|
label: 'Tables',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'tables-basic',
|
||||||
|
label: 'Basic Tables',
|
||||||
|
link: '/tables/basic',
|
||||||
|
parentKey: 'tables',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tables-grid-js',
|
||||||
|
label: 'Datatables',
|
||||||
|
link: '/tables/datatable',
|
||||||
|
parentKey: 'tables',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'icons',
|
||||||
|
icon: 'iconamoon:lightning-1-duotone',
|
||||||
|
label: 'Icons',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'icons-boxicons',
|
||||||
|
label: 'Boxicons',
|
||||||
|
link: '/icons/boxicons',
|
||||||
|
parentKey: 'icons',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'icons-iconamoon',
|
||||||
|
label: 'IconaMoon Icons',
|
||||||
|
link: '/icons/iconamoon',
|
||||||
|
parentKey: 'icons',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'maps',
|
||||||
|
icon: 'iconamoon:location-pin-duotone',
|
||||||
|
label: 'Maps',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'maps-google',
|
||||||
|
label: 'Google Maps',
|
||||||
|
link: '/maps/google',
|
||||||
|
parentKey: 'maps',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'maps-vector',
|
||||||
|
label: 'Vector Maps',
|
||||||
|
link: '/maps/vector',
|
||||||
|
parentKey: 'maps',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'badge-menu',
|
||||||
|
icon: 'iconamoon:badge-duotone',
|
||||||
|
label: 'Badge Menu',
|
||||||
|
badge: {
|
||||||
|
variant: 'danger',
|
||||||
|
text: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'menuitem',
|
||||||
|
icon: 'iconamoon:folder-add-duotone',
|
||||||
|
label: 'Menu Item',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'menu-item-1',
|
||||||
|
label: 'Menu Item 1',
|
||||||
|
parentKey: 'menuitem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'menu-item-2',
|
||||||
|
label: 'Menu Item 2',
|
||||||
|
collapsed: true,
|
||||||
|
parentKey: 'menuitem',
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'menu-sub-item',
|
||||||
|
label: 'Menu Sub Item',
|
||||||
|
parentKey: 'menu-item-2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'disabled-item',
|
||||||
|
icon: 'iconamoon:unavailable-duotone',
|
||||||
|
label: 'Disabled Item',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
808
apiferia/src/app/common/menu-meta.ts.backup
Normal file
808
apiferia/src/app/common/menu-meta.ts.backup
Normal file
@@ -0,0 +1,808 @@
|
|||||||
|
export type MenuItem = {
|
||||||
|
key?: string
|
||||||
|
label?: string
|
||||||
|
icon?: string
|
||||||
|
link?: string
|
||||||
|
collapsed?: boolean
|
||||||
|
subMenu?: any
|
||||||
|
isTitle?: boolean
|
||||||
|
badge?: any
|
||||||
|
parentKey?: string
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MENU: MenuItem[] = [
|
||||||
|
{
|
||||||
|
key: 'general',
|
||||||
|
label: 'GENERAL',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboards',
|
||||||
|
icon: 'iconamoon:home-duotone',
|
||||||
|
label: 'Dashboards',
|
||||||
|
collapsed: false,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'dashboard-analytics',
|
||||||
|
label: 'Analytics',
|
||||||
|
link: '/dashboard/analytics',
|
||||||
|
parentKey: 'dashboards',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboard-finance',
|
||||||
|
label: 'Finance',
|
||||||
|
link: '/dashboard/finance',
|
||||||
|
parentKey: 'dashboards',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboard-sales',
|
||||||
|
label: 'Sales',
|
||||||
|
link: '/dashboard/sales',
|
||||||
|
parentKey: 'dashboards',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps',
|
||||||
|
label: 'APPS',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce',
|
||||||
|
icon: 'iconamoon:shopping-bag-duotone',
|
||||||
|
label: 'Ecommerce',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'ecommerce-products',
|
||||||
|
label: 'Products',
|
||||||
|
link: '/ecommerce/products',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-productsdetails',
|
||||||
|
label: 'Product Details',
|
||||||
|
link: '/ecommerce/product/1',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-createproduct',
|
||||||
|
label: 'Create Product',
|
||||||
|
link: '/ecommerce/create',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-customers',
|
||||||
|
label: 'Customers',
|
||||||
|
link: '/ecommerce/customers',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-sellers',
|
||||||
|
label: 'Sellers',
|
||||||
|
link: '/ecommerce/sellers',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-orders',
|
||||||
|
label: 'Orders',
|
||||||
|
link: '/ecommerce/orders',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-orderdetails',
|
||||||
|
label: 'Order Details',
|
||||||
|
link: '/ecommerce/orders/10001',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ecommerce-inventory',
|
||||||
|
label: 'Inventory',
|
||||||
|
link: '/ecommerce/inventory',
|
||||||
|
parentKey: 'ecommerce',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-chat',
|
||||||
|
icon: 'iconamoon:comment-dots-duotone',
|
||||||
|
label: 'Chat',
|
||||||
|
link: '/apps/chat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-email',
|
||||||
|
icon: 'iconamoon:email-duotone',
|
||||||
|
label: 'Email',
|
||||||
|
link: '/apps/email',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-calendar',
|
||||||
|
icon: 'iconamoon:calendar-1-duotone',
|
||||||
|
label: 'Calendar',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'calendar-schedule',
|
||||||
|
label: 'Schedule',
|
||||||
|
link: '/calendar/schedule',
|
||||||
|
parentKey: 'apps-calendar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'calendar-integration',
|
||||||
|
label: 'Integration',
|
||||||
|
link: '/calendar/integration',
|
||||||
|
parentKey: 'apps-calendar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'calendar-help',
|
||||||
|
label: 'Help',
|
||||||
|
link: '/calendar/help',
|
||||||
|
parentKey: 'apps-calendar',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-todo',
|
||||||
|
icon: 'iconamoon:ticket-duotone',
|
||||||
|
label: 'Todo',
|
||||||
|
link: '/apps/todo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-social',
|
||||||
|
icon: 'iconamoon:squinting-face-duotone',
|
||||||
|
label: 'Social',
|
||||||
|
link: '/apps/social',
|
||||||
|
badge: {
|
||||||
|
variant: 'danger',
|
||||||
|
text: 'Hot',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-contacts',
|
||||||
|
icon: 'iconamoon:profile-circle-duotone',
|
||||||
|
label: 'Contacts',
|
||||||
|
link: '/apps/contacts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'apps-invoices',
|
||||||
|
icon: 'iconamoon:invoice-duotone',
|
||||||
|
label: 'Invoices',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'invoices',
|
||||||
|
label: 'Invoices',
|
||||||
|
link: '/invoices',
|
||||||
|
parentKey: 'apps-invoices',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'invoices-details',
|
||||||
|
label: 'Invoice Details',
|
||||||
|
link: '/invoice/RB6985',
|
||||||
|
parentKey: 'apps-invoices',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'custom',
|
||||||
|
label: 'Custom',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pages',
|
||||||
|
label: 'Pages',
|
||||||
|
isTitle: false,
|
||||||
|
icon: 'iconamoon:copy-duotone',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'page-welcome',
|
||||||
|
label: 'Welcome',
|
||||||
|
link: '/pages/welcome',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-faqs',
|
||||||
|
label: 'FAQs',
|
||||||
|
link: '/pages/faqs',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-profile',
|
||||||
|
label: 'Profile',
|
||||||
|
link: '/pages/profile',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-coming-soon',
|
||||||
|
label: 'Coming Soon',
|
||||||
|
link: '/coming-soon',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-contact-us',
|
||||||
|
label: 'Contact Us',
|
||||||
|
link: '/pages/contact-us',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-about-us',
|
||||||
|
label: 'About Us',
|
||||||
|
link: '/pages/about-us',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-our-team',
|
||||||
|
label: 'Our Team',
|
||||||
|
link: '/pages/our-team',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-timeline',
|
||||||
|
label: 'Timeline',
|
||||||
|
link: '/pages/timeline',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-pricing',
|
||||||
|
label: 'Pricing',
|
||||||
|
link: '/pages/pricing',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-maintenance',
|
||||||
|
label: 'Maintenance',
|
||||||
|
link: '/maintenance',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-404-error',
|
||||||
|
label: '404 Error',
|
||||||
|
link: '/error-404',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-404-error2',
|
||||||
|
label: '404 Error 2',
|
||||||
|
link: '/error-404-2',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-error-404-alt',
|
||||||
|
label: 'Error 404 (alt)',
|
||||||
|
link: '/pages/error-404-alt',
|
||||||
|
parentKey: 'pages',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'page-authentication',
|
||||||
|
label: 'Authentication',
|
||||||
|
isTitle: false,
|
||||||
|
icon: 'iconamoon:lock-duotone',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'sign-in',
|
||||||
|
label: 'Sign In',
|
||||||
|
link: '/auth/sign-in',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sign-in-2',
|
||||||
|
label: 'Sign In 2',
|
||||||
|
link: '/auth/sign-in-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'signup',
|
||||||
|
label: 'Sign Up',
|
||||||
|
link: '/auth/sign-up',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'signup2',
|
||||||
|
label: 'Sign Up 2',
|
||||||
|
link: '/auth/sign-up-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'reset-pass',
|
||||||
|
label: 'Reset Password',
|
||||||
|
link: '/auth/reset-pass',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'reset-pass2',
|
||||||
|
label: 'Reset Password 2',
|
||||||
|
link: '/auth/reset-pass-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lock-screen',
|
||||||
|
label: 'Lock Screen',
|
||||||
|
link: '/auth/lock-screen',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'lock-screen-2',
|
||||||
|
label: 'Lock Screen 2',
|
||||||
|
link: '/auth/lock-screen-2',
|
||||||
|
parentKey: 'page-authentication',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'widgets',
|
||||||
|
icon: 'iconamoon:gift-duotone',
|
||||||
|
label: 'Widgets',
|
||||||
|
link: '/widgets',
|
||||||
|
badge: {
|
||||||
|
variant: 'info',
|
||||||
|
text: '9+',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'components',
|
||||||
|
label: 'COMPONENTS',
|
||||||
|
isTitle: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui',
|
||||||
|
icon: 'iconamoon:briefcase-duotone',
|
||||||
|
label: 'Base UI',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'base-ui-accordions',
|
||||||
|
label: 'Accordion',
|
||||||
|
link: '/ui/accordions',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-alerts',
|
||||||
|
label: 'Alerts',
|
||||||
|
link: '/ui/alerts',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-avatars',
|
||||||
|
label: 'Avatar',
|
||||||
|
link: '/ui/avatars',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-badges',
|
||||||
|
label: 'Badge',
|
||||||
|
link: '/ui/badges',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-breadcrumb',
|
||||||
|
label: 'Breadcrumb',
|
||||||
|
link: '/ui/breadcrumb',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-buttons',
|
||||||
|
label: 'Buttons',
|
||||||
|
link: '/ui/buttons',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-cards',
|
||||||
|
label: 'Card',
|
||||||
|
link: '/ui/cards',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-carousel',
|
||||||
|
label: 'Carousel',
|
||||||
|
link: '/ui/carousel',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-collapse',
|
||||||
|
label: 'Collapse',
|
||||||
|
link: '/ui/collapse',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-dropdowns',
|
||||||
|
label: 'Dropdown',
|
||||||
|
link: '/ui/dropdowns',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-list-group',
|
||||||
|
label: 'List Group',
|
||||||
|
link: '/ui/list-group',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-modals',
|
||||||
|
label: 'Modal',
|
||||||
|
link: '/ui/modals',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-tabs',
|
||||||
|
label: 'Tabs',
|
||||||
|
link: '/ui/tabs',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-offcanvas',
|
||||||
|
label: 'Offcanvas',
|
||||||
|
link: '/ui/offcanvas',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-pagination',
|
||||||
|
label: 'Pagination',
|
||||||
|
link: '/ui/pagination',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-placeholders',
|
||||||
|
label: 'Placeholders',
|
||||||
|
link: '/ui/placeholders',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-popovers',
|
||||||
|
label: 'Popovers',
|
||||||
|
link: '/ui/popovers',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-progress',
|
||||||
|
label: 'Progress',
|
||||||
|
link: '/ui/progress',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-scrollspy',
|
||||||
|
label: 'Scrollspy',
|
||||||
|
link: '/ui/scrollspy',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-spinners',
|
||||||
|
label: 'Spinners',
|
||||||
|
link: '/ui/spinners',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-toasts',
|
||||||
|
label: 'Toasts',
|
||||||
|
link: '/ui/toasts',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'base-ui-tooltips',
|
||||||
|
label: 'Tooltips',
|
||||||
|
link: '/ui/tooltips',
|
||||||
|
parentKey: 'base-ui',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui',
|
||||||
|
icon: 'iconamoon:component-duotone',
|
||||||
|
label: 'Advanced UI',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-ratings',
|
||||||
|
label: 'Ratings',
|
||||||
|
link: '/advanced/ratings',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-sweet-alert',
|
||||||
|
label: 'Sweet Alert',
|
||||||
|
link: '/advanced/alert',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-swiper-slider',
|
||||||
|
label: 'Swiper Slider',
|
||||||
|
link: '/advanced/swiper',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-scrollbar',
|
||||||
|
label: 'Scrollbar',
|
||||||
|
link: '/advanced/scrollbar',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced-ui-toastify',
|
||||||
|
label: 'Toastify',
|
||||||
|
link: '/advanced/toastify',
|
||||||
|
parentKey: 'advanced-ui',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts',
|
||||||
|
icon: 'iconamoon:3d-duotone',
|
||||||
|
label: 'Charts',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'charts-area',
|
||||||
|
label: 'Area',
|
||||||
|
link: '/charts/area',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-bar',
|
||||||
|
label: 'Bar',
|
||||||
|
link: '/charts/bar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-bubble',
|
||||||
|
label: 'Bubble',
|
||||||
|
link: '/charts/bubble',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-candl-stick',
|
||||||
|
label: 'Candlestick',
|
||||||
|
link: '/charts/candlestick',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-column',
|
||||||
|
label: 'Column',
|
||||||
|
link: '/charts/column',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-heatmap',
|
||||||
|
label: 'Heatmap',
|
||||||
|
link: '/charts/heatmap',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-line',
|
||||||
|
label: 'Line',
|
||||||
|
link: '/charts/line',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-mixed',
|
||||||
|
label: 'Mixed',
|
||||||
|
link: '/charts/mixed',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-timeline',
|
||||||
|
label: 'Timeline',
|
||||||
|
link: '/charts/timeline',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-boxplot',
|
||||||
|
label: 'Boxplot',
|
||||||
|
link: '/charts/boxplot',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-treemap',
|
||||||
|
label: 'Treemap',
|
||||||
|
link: '/charts/treemap',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-pie',
|
||||||
|
label: 'Pie',
|
||||||
|
link: '/charts/pie',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-radar',
|
||||||
|
label: 'Radar',
|
||||||
|
link: '/charts/radar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-radial-bar',
|
||||||
|
label: 'RadialBar',
|
||||||
|
link: '/charts/radial-bar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-scatter',
|
||||||
|
label: 'Scatter',
|
||||||
|
link: '/charts/scatter',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'charts-polar-area',
|
||||||
|
label: 'Polar Area',
|
||||||
|
link: '/charts/polar',
|
||||||
|
parentKey: 'charts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms',
|
||||||
|
icon: 'iconamoon:cheque-duotone',
|
||||||
|
label: 'Forms',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'forms-basic-elements',
|
||||||
|
label: 'Basic Elements',
|
||||||
|
link: '/forms/basic',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-checkbox&radio',
|
||||||
|
label: 'Checkbox & Radio',
|
||||||
|
link: '/forms/checkbox',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-choice-select',
|
||||||
|
label: 'Choice Select',
|
||||||
|
link: '/forms/select',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-clipboard',
|
||||||
|
label: 'Clipboard',
|
||||||
|
link: '/forms/clipboard',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-flat-picker',
|
||||||
|
label: 'Flatpicker',
|
||||||
|
link: '/forms/flat-picker',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-validation',
|
||||||
|
label: 'Validation',
|
||||||
|
link: '/forms/validation',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-wizard',
|
||||||
|
label: 'Wizard',
|
||||||
|
link: '/forms/wizard',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-file-uploads',
|
||||||
|
label: 'File Upload',
|
||||||
|
link: '/forms/file-uploads',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-editors',
|
||||||
|
label: 'Editors',
|
||||||
|
link: '/forms/editors',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-input-mask',
|
||||||
|
label: 'Input Mask',
|
||||||
|
link: '/forms/input-mask',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'forms-slider',
|
||||||
|
label: 'Slider',
|
||||||
|
link: '/forms/slider',
|
||||||
|
parentKey: 'forms',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tables',
|
||||||
|
icon: 'iconamoon:box-duotone',
|
||||||
|
label: 'Tables',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'tables-basic',
|
||||||
|
label: 'Basic Tables',
|
||||||
|
link: '/tables/basic',
|
||||||
|
parentKey: 'tables',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tables-grid-js',
|
||||||
|
label: 'Datatables',
|
||||||
|
link: '/tables/datatable',
|
||||||
|
parentKey: 'tables',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'icons',
|
||||||
|
icon: 'iconamoon:lightning-1-duotone',
|
||||||
|
label: 'Icons',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'icons-boxicons',
|
||||||
|
label: 'Boxicons',
|
||||||
|
link: '/icons/boxicons',
|
||||||
|
parentKey: 'icons',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'icons-iconamoon',
|
||||||
|
label: 'IconaMoon Icons',
|
||||||
|
link: '/icons/iconamoon',
|
||||||
|
parentKey: 'icons',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'maps',
|
||||||
|
icon: 'iconamoon:location-pin-duotone',
|
||||||
|
label: 'Maps',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'maps-google',
|
||||||
|
label: 'Google Maps',
|
||||||
|
link: '/maps/google',
|
||||||
|
parentKey: 'maps',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'maps-vector',
|
||||||
|
label: 'Vector Maps',
|
||||||
|
link: '/maps/vector',
|
||||||
|
parentKey: 'maps',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'badge-menu',
|
||||||
|
icon: 'iconamoon:badge-duotone',
|
||||||
|
label: 'Badge Menu',
|
||||||
|
badge: {
|
||||||
|
variant: 'danger',
|
||||||
|
text: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'menuitem',
|
||||||
|
icon: 'iconamoon:folder-add-duotone',
|
||||||
|
label: 'Menu Item',
|
||||||
|
collapsed: true,
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'menu-item-1',
|
||||||
|
label: 'Menu Item 1',
|
||||||
|
parentKey: 'menuitem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'menu-item-2',
|
||||||
|
label: 'Menu Item 2',
|
||||||
|
collapsed: true,
|
||||||
|
parentKey: 'menuitem',
|
||||||
|
subMenu: [
|
||||||
|
{
|
||||||
|
key: 'menu-sub-item',
|
||||||
|
label: 'Menu Sub Item',
|
||||||
|
parentKey: 'menu-item-2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'disabled-item',
|
||||||
|
icon: 'iconamoon:unavailable-duotone',
|
||||||
|
label: 'Disabled Item',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
7
apiferia/src/app/components/commonFunction.ts
Normal file
7
apiferia/src/app/components/commonFunction.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// shuffle chart series
|
||||||
|
export function shuffleArray(array: any[]): void {
|
||||||
|
for (let i = array.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1))
|
||||||
|
;[array[i], array[j]] = [array[j], array[i]]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import { Component, Input } from '@angular/core'
|
||||||
|
import {
|
||||||
|
DROPZONE_CONFIG,
|
||||||
|
DropzoneConfigInterface,
|
||||||
|
DropzoneModule,
|
||||||
|
} from 'ngx-dropzone-wrapper'
|
||||||
|
|
||||||
|
type UploadedFile = {
|
||||||
|
name: string
|
||||||
|
size: number
|
||||||
|
type: string
|
||||||
|
dataURL?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_DROPZONE_CONFIG: DropzoneConfigInterface = {
|
||||||
|
// Change this to your upload POST address:
|
||||||
|
url: 'https://httpbin.org/post',
|
||||||
|
maxFilesize: 50,
|
||||||
|
acceptedFiles: 'image/*',
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'FileUploader',
|
||||||
|
standalone: true,
|
||||||
|
imports: [DropzoneModule],
|
||||||
|
template: `
|
||||||
|
<dropzone
|
||||||
|
class="dropzone"
|
||||||
|
[config]="dropzoneConfig"
|
||||||
|
[message]="dropzone"
|
||||||
|
(success)="onUploadSuccess($event)"
|
||||||
|
></dropzone>
|
||||||
|
|
||||||
|
@if (showPreview && uploadedFiles) {
|
||||||
|
<ul class="list-unstyled mb-0" id="dropzone-preview">
|
||||||
|
@for (file of uploadedFiles; track $index) {
|
||||||
|
<li class="mt-2" id="dropzone-preview-list">
|
||||||
|
<div class="border rounded">
|
||||||
|
<div class="d-flex align-items-center p-2">
|
||||||
|
<div class="flex-shrink-0 me-3">
|
||||||
|
<div class="avatar-sm bg-light rounded">
|
||||||
|
<img
|
||||||
|
data-dz-thumbnail
|
||||||
|
[src]="file.dataURL"
|
||||||
|
class="img-fluid rounded d-block"
|
||||||
|
src="#"
|
||||||
|
alt="Dropzone-Image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="pt-1">
|
||||||
|
<h5 class="fs-14 mb-1" data-dz-name>
|
||||||
|
{{ file.name }}
|
||||||
|
</h5>
|
||||||
|
<p class="fs-13 text-muted mb-0" data-dz-size>
|
||||||
|
{{ file.size }}
|
||||||
|
</p>
|
||||||
|
<strong
|
||||||
|
class="error text-danger"
|
||||||
|
data-dz-errormessage
|
||||||
|
></strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-shrink-0 ms-3">
|
||||||
|
<button
|
||||||
|
(click)="removeFile($index)"
|
||||||
|
data-dz-remove
|
||||||
|
class="btn btn-sm btn-danger"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: DROPZONE_CONFIG,
|
||||||
|
useValue: DEFAULT_DROPZONE_CONFIG,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class FileUploaderComponent {
|
||||||
|
@Input() showPreview: boolean = false
|
||||||
|
|
||||||
|
dropzone = `<div class="dz-message needsclick">
|
||||||
|
<i class="h1 bx bx-cloud-upload"></i>
|
||||||
|
<h3 class="mb-0">
|
||||||
|
Drop files here or click to
|
||||||
|
upload.
|
||||||
|
</h3>
|
||||||
|
<span class="text-muted fs-13">
|
||||||
|
(This is just a demo
|
||||||
|
dropzone. Selected files are
|
||||||
|
<strong>not</strong>
|
||||||
|
actually uploaded.)
|
||||||
|
</span>
|
||||||
|
</div>`
|
||||||
|
dropzoneConfig: DropzoneConfigInterface = {
|
||||||
|
url: 'https://httpbin.org/post',
|
||||||
|
maxFilesize: 50,
|
||||||
|
clickable: true,
|
||||||
|
addRemoveLinks: true,
|
||||||
|
}
|
||||||
|
uploadedFiles: any[] = []
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.showPreview == true) {
|
||||||
|
this.dropzoneConfig.previewsContainer = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// File Upload
|
||||||
|
imageURL: string = ''
|
||||||
|
onUploadSuccess(event: UploadedFile[]) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.uploadedFiles.push(event[0])
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// File Remove
|
||||||
|
removeFile(index: number) {
|
||||||
|
this.uploadedFiles.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
apiferia/src/app/components/file-uploader/index.ts
Normal file
1
apiferia/src/app/components/file-uploader/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { FileUploaderComponent } from './file-uploader.component'
|
||||||
46
apiferia/src/app/components/logo-box.component.ts
Normal file
46
apiferia/src/app/components/logo-box.component.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { Component, Input } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-logo-box',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
template: `
|
||||||
|
<div [class]="className">
|
||||||
|
<a href="index.html" class="logo-dark">
|
||||||
|
<img
|
||||||
|
src="assets/images/logo-sm.png"
|
||||||
|
[ngClass]="className == 'logo-box' ? 'logo-sm' : 'me-1'"
|
||||||
|
[height]="logoHeight"
|
||||||
|
alt="logo sm"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="assets/images/logo-dark.png"
|
||||||
|
[ngClass]="className == 'logo-box' ? 'logo-lg' : ''"
|
||||||
|
[height]="height"
|
||||||
|
alt="logo dark"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="index.html" class="logo-light">
|
||||||
|
<img
|
||||||
|
src="assets/images/logo-sm.png"
|
||||||
|
[ngClass]="className == 'logo-box' ? 'logo-sm' : 'me-1'"
|
||||||
|
[height]="logoHeight"
|
||||||
|
alt="logo sm"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="assets/images/logo-light.png"
|
||||||
|
[ngClass]="className == 'logo-box' ? 'logo-lg' : ''"
|
||||||
|
[height]="height"
|
||||||
|
alt="logo light"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class LogoBoxComponent {
|
||||||
|
@Input() className: string = ''
|
||||||
|
@Input() height: string = ''
|
||||||
|
@Input() logoHeight: string = ''
|
||||||
|
}
|
||||||
25
apiferia/src/app/components/page-title.component.ts
Normal file
25
apiferia/src/app/components/page-title.component.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Component, Input } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-title',
|
||||||
|
standalone: true,
|
||||||
|
template: `
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="page-title-box">
|
||||||
|
<h4 class="mb-0 fw-semibold">{{ subtitle }}</h4>
|
||||||
|
<ol class="breadcrumb mb-0">
|
||||||
|
<li class="breadcrumb-item">
|
||||||
|
<a href="javascript: void(0);">{{ title }}</a>
|
||||||
|
</li>
|
||||||
|
<li class="breadcrumb-item active">{{ subtitle }}</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class PageTitleComponent {
|
||||||
|
@Input() title: string = ''
|
||||||
|
@Input() subtitle: string = ''
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { SocialBtnComponent } from './social-btn.component'
|
||||||
|
|
||||||
|
describe('SocialBtnComponent', () => {
|
||||||
|
let component: SocialBtnComponent
|
||||||
|
let fixture: ComponentFixture<SocialBtnComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SocialBtnComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SocialBtnComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Component } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-social-btn',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
template: `
|
||||||
|
<p class="mt-3 fw-semibold no-span">OR sign with</p>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="javascript:void(0);" class="btn btn-light shadow-none me-1"
|
||||||
|
><i class="bx bxl-google fs-20"></i
|
||||||
|
></a>
|
||||||
|
<a href="javascript:void(0);" class="btn btn-light shadow-none me-1"
|
||||||
|
><i class="bx bxl-facebook fs-20"></i
|
||||||
|
></a>
|
||||||
|
<a href="javascript:void(0);" class="btn btn-light shadow-none"
|
||||||
|
><i class="bx bxl-github fs-20"></i
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class SocialBtnComponent {}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<div class="row">
|
||||||
|
@for (data of stateData; track $index; let last = $last) {
|
||||||
|
<div class="col-xl col-lg-4 col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body overflow-hidden position-relative">
|
||||||
|
<iconify-icon
|
||||||
|
[icon]="data.icon"
|
||||||
|
class="fs-36 text-{{ data.iconColor }}"
|
||||||
|
></iconify-icon>
|
||||||
|
<h3 class="mb-0 fw-bold mt-3 mb-1">${{ data.amount }}k</h3>
|
||||||
|
<p class="text-muted">{{ data.title }}</p>
|
||||||
|
<span class="badge fs-12 badge-soft-{{ data.badgeColor }}"
|
||||||
|
><i class="ti ti-arrow-badge-up"></i> {{ data.badge }}%</span
|
||||||
|
>
|
||||||
|
<i class="{{ data.badgeIcon }} widget-icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { StateCardComponent } from './state-card.component'
|
||||||
|
|
||||||
|
describe('StateCardComponent', () => {
|
||||||
|
let component: StateCardComponent
|
||||||
|
let fixture: ComponentFixture<StateCardComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [StateCardComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(StateCardComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'state-card',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './state-card.component.html',
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
})
|
||||||
|
export class StateCardComponent {
|
||||||
|
stateData = [
|
||||||
|
{
|
||||||
|
icon: 'iconamoon:shopping-card-add-duotone',
|
||||||
|
iconColor: 'info',
|
||||||
|
amount: '59.6',
|
||||||
|
title: 'Total Sales',
|
||||||
|
badge: '8.72',
|
||||||
|
badgeColor: 'success',
|
||||||
|
badgeIcon: 'bx bx-doughnut-chart',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'iconamoon:link-external-duotone',
|
||||||
|
iconColor: 'success',
|
||||||
|
amount: '24.03',
|
||||||
|
title: 'Total Expenses',
|
||||||
|
badge: '3.28',
|
||||||
|
badgeColor: 'danger',
|
||||||
|
badgeIcon: 'bx bx-bar-chart-alt-2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'iconamoon:store-duotone',
|
||||||
|
iconColor: 'purple',
|
||||||
|
amount: '48.7',
|
||||||
|
title: 'Investments',
|
||||||
|
badge: '5.69',
|
||||||
|
badgeColor: 'danger',
|
||||||
|
badgeIcon: 'bx bx-building-house',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'iconamoon:gift-duotone',
|
||||||
|
iconColor: 'orange',
|
||||||
|
amount: '11.3',
|
||||||
|
title: 'Profit',
|
||||||
|
badge: '10.58',
|
||||||
|
badgeColor: 'success',
|
||||||
|
badgeIcon: 'bx bx-bowl-hot',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'iconamoon:certificate-badge-duotone',
|
||||||
|
iconColor: 'warning',
|
||||||
|
amount: '5.06',
|
||||||
|
title: 'Savings',
|
||||||
|
badge: '8.72',
|
||||||
|
badgeColor: 'success',
|
||||||
|
badgeIcon: 'bx bx-cricket-ball',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
24
apiferia/src/app/components/swiper-directive.component.ts
Normal file
24
apiferia/src/app/components/swiper-directive.component.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { AfterViewInit, Directive, ElementRef, Input } from '@angular/core'
|
||||||
|
import type { SwiperOptions } from 'swiper/types/swiper-options'
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'swiper-container',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class SwiperDirective implements AfterViewInit {
|
||||||
|
private readonly swiperElement: HTMLElement
|
||||||
|
|
||||||
|
@Input('config') config?: SwiperOptions
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private el: ElementRef<HTMLElement & { initialize: () => void }>
|
||||||
|
) {
|
||||||
|
this.swiperElement = el.nativeElement
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
Object.assign(this.el.nativeElement, this.config)
|
||||||
|
|
||||||
|
this.el.nativeElement.initialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<div class="card docs-nav">
|
||||||
|
<ul class="nav bg-transparent flex-column">
|
||||||
|
@for (item of linkList; track $index) {
|
||||||
|
<li class="nav-item">
|
||||||
|
<a
|
||||||
|
href="{{ item.link }}"
|
||||||
|
(click)="scrollToSection($event, item.link)"
|
||||||
|
class="nav-link"
|
||||||
|
>{{ item.label }}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
import { UIExamplesListComponent } from './ui-examples-list.component'
|
||||||
|
|
||||||
|
describe('UIExamplesListComponent', () => {
|
||||||
|
let component: UIExamplesListComponent
|
||||||
|
let fixture: ComponentFixture<UIExamplesListComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [UIExamplesListComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(UIExamplesListComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { AfterViewInit, Component, Input } from '@angular/core'
|
||||||
|
import Gumshoe from 'gumshoejs'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ui-examples-list',
|
||||||
|
standalone: true,
|
||||||
|
templateUrl: './ui-examples-list.component.html',
|
||||||
|
})
|
||||||
|
export class UIExamplesListComponent implements AfterViewInit {
|
||||||
|
@Input() linkList: { label: string; link: string }[] | undefined
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
if (document.querySelector('.docs-nav a')) new Gumshoe('.docs-nav a')
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToSection(event: Event, link: string) {
|
||||||
|
event.preventDefault()
|
||||||
|
const targetId = link.substring(1)
|
||||||
|
const targetElement = document.getElementById(targetId)
|
||||||
|
if (targetElement) {
|
||||||
|
targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { AfterViewInit, Component, Input, OnDestroy } from '@angular/core'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
jsVectorMap?: any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-world-vector-map',
|
||||||
|
standalone: true,
|
||||||
|
template:
|
||||||
|
'<div [id]="mapId" [style.width]="width" [style.height]="height"></div>',
|
||||||
|
})
|
||||||
|
export class WorldVectorMapComponent implements AfterViewInit {
|
||||||
|
@Input() width: string = ''
|
||||||
|
@Input() height: string = ''
|
||||||
|
@Input() options: Record<string, unknown> = {}
|
||||||
|
@Input() type: string = ''
|
||||||
|
@Input() mapId: string = 'map'
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
new (window as Window).jsVectorMap({
|
||||||
|
selector: '#' + this.mapId,
|
||||||
|
map: this.type,
|
||||||
|
...this.options,
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
27
apiferia/src/app/core/guards/role.guard.ts
Normal file
27
apiferia/src/app/core/guards/role.guard.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Router, CanActivate, ActivatedRouteSnapshot } from '@angular/router';
|
||||||
|
import { AuthenticationService } from '../services/auth.service';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class RoleGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private authService: AuthenticationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
canActivate(route: ActivatedRouteSnapshot): boolean {
|
||||||
|
const requiredRoles = route.data['roles'] as string[];
|
||||||
|
|
||||||
|
if (!requiredRoles || requiredRoles.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.authService.hasRole(requiredRoles)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No tiene el rol requerido
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
apiferia/src/app/core/interceptors/auth.interceptor.ts
Normal file
45
apiferia/src/app/core/interceptors/auth.interceptor.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import {
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpInterceptor,
|
||||||
|
HttpErrorResponse
|
||||||
|
} from '@angular/common/http'
|
||||||
|
import { Observable, throwError } from 'rxjs'
|
||||||
|
import { catchError } from 'rxjs/operators'
|
||||||
|
import { Router } from '@angular/router'
|
||||||
|
import { Store } from '@ngrx/store'
|
||||||
|
import { logout } from '@/app/store/authentication/authentication.actions'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private store: Store
|
||||||
|
) {}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
// Obtener el token
|
||||||
|
const token = localStorage.getItem('access_token')
|
||||||
|
|
||||||
|
// Si hay token, agregarlo al header
|
||||||
|
if (token) {
|
||||||
|
request = request.clone({
|
||||||
|
setHeaders: {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return next.handle(request).pipe(
|
||||||
|
catchError((error: HttpErrorResponse) => {
|
||||||
|
if (error.status === 401) {
|
||||||
|
// Token expirado o inválido
|
||||||
|
this.store.dispatch(logout())
|
||||||
|
}
|
||||||
|
return throwError(() => error)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
62
apiferia/src/app/core/services/api/vehicles.service.ts
Normal file
62
apiferia/src/app/core/services/api/vehicles.service.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
export interface Vehicle {
|
||||||
|
id: string;
|
||||||
|
dealerId: string;
|
||||||
|
brand: string;
|
||||||
|
model: string;
|
||||||
|
year: number;
|
||||||
|
mileage: number;
|
||||||
|
condition: string;
|
||||||
|
price: number;
|
||||||
|
status: string;
|
||||||
|
qrCode?: string;
|
||||||
|
viewsCount: number;
|
||||||
|
scansCount: number;
|
||||||
|
createdAt?: Date;
|
||||||
|
updatedAt?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class VehiclesService {
|
||||||
|
private endpoint = '/api/vehicles';
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
getAll(): Observable<Vehicle[]> {
|
||||||
|
return this.http.get<Vehicle[]>(this.endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
getById(id: string): Observable<Vehicle> {
|
||||||
|
return this.http.get<Vehicle>(`${this.endpoint}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(vehicle: Partial<Vehicle>): Observable<Vehicle> {
|
||||||
|
return this.http.post<Vehicle>(this.endpoint, vehicle);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: string, vehicle: Partial<Vehicle>): Observable<Vehicle> {
|
||||||
|
return this.http.patch<Vehicle>(`${this.endpoint}/${id}`, vehicle);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: string): Observable<void> {
|
||||||
|
return this.http.delete<void>(`${this.endpoint}/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Métodos adicionales específicos de vehículos
|
||||||
|
getByDealer(dealerId: string): Observable<Vehicle[]> {
|
||||||
|
return this.http.get<Vehicle[]>(`${this.endpoint}/dealer/${dealerId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateQR(id: string): Observable<{qrCode: string}> {
|
||||||
|
return this.http.post<{qrCode: string}>(`${this.endpoint}/${id}/generate-qr`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus(id: string, status: string): Observable<Vehicle> {
|
||||||
|
return this.http.patch<Vehicle>(`${this.endpoint}/${id}/status`, { status });
|
||||||
|
}
|
||||||
|
}
|
||||||
61
apiferia/src/app/core/services/auth.service.ts
Normal file
61
apiferia/src/app/core/services/auth.service.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { Injectable, inject } from '@angular/core'
|
||||||
|
import { HttpClient } from '@angular/common/http'
|
||||||
|
import { map } from 'rxjs/operators'
|
||||||
|
import { CookieService } from 'ngx-cookie-service'
|
||||||
|
import { User } from '@/app/store/authentication/auth.model'
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AuthenticationService {
|
||||||
|
user: User | null = null
|
||||||
|
public readonly authSessionKey = '_REBACK_AUTH_SESSION_KEY_'
|
||||||
|
private cookieService = inject(CookieService)
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
// Recuperar usuario si existe
|
||||||
|
const savedUser = localStorage.getItem('currentUser');
|
||||||
|
if (savedUser) {
|
||||||
|
this.user = JSON.parse(savedUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
login(email: string, password: string) {
|
||||||
|
return this.http.post<User>(`/api/login`, { email, password }).pipe(
|
||||||
|
map((user) => {
|
||||||
|
if (user && user.token) {
|
||||||
|
this.user = user
|
||||||
|
this.saveSession(user.token)
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
logout(): void {
|
||||||
|
this.removeSession()
|
||||||
|
localStorage.removeItem('access_token')
|
||||||
|
localStorage.removeItem('currentUser')
|
||||||
|
this.user = null
|
||||||
|
}
|
||||||
|
|
||||||
|
get session(): string {
|
||||||
|
return this.cookieService.get(this.authSessionKey) || localStorage.getItem('access_token') || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSession(token: string): void {
|
||||||
|
this.cookieService.set(this.authSessionKey, token)
|
||||||
|
localStorage.setItem('access_token', token)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSession(): void {
|
||||||
|
this.cookieService.delete(this.authSessionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentUser(): User | null {
|
||||||
|
return this.user || JSON.parse(localStorage.getItem('currentUser') || '{}')
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRole(roles: string[]): boolean {
|
||||||
|
const user = this.getCurrentUser();
|
||||||
|
return user && user.role ? roles.includes(user.role) : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
apiferia/src/app/core/services/crud.service.ts
Normal file
35
apiferia/src/app/core/services/crud.service.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { Observable, of } from 'rxjs'
|
||||||
|
|
||||||
|
import { defaultEvents } from '@/app/store/calendar/data'
|
||||||
|
import type { EventInput } from '@fullcalendar/core'
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class CrudService {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Get
|
||||||
|
*/
|
||||||
|
fetchCalendarEvents(): Observable<EventInput[]> {
|
||||||
|
return of(defaultEvents)
|
||||||
|
}
|
||||||
|
|
||||||
|
addCalendarEvents(newData: EventInput): Observable<EventInput[]> {
|
||||||
|
let newEvents = [...defaultEvents, newData] // Create a new array by spreading defaultEvents and adding newData
|
||||||
|
return of(newEvents)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCalendarEvents(updatedData: EventInput): Observable<EventInput[]> {
|
||||||
|
const index = defaultEvents.findIndex((item) => item.id === updatedData.id)
|
||||||
|
let updatedEvents = defaultEvents.slice()
|
||||||
|
if (index !== -1) {
|
||||||
|
updatedEvents[index] = updatedData
|
||||||
|
}
|
||||||
|
return of(updatedEvents)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCalendarEvents(id: string): Observable<EventInput[]> {
|
||||||
|
return of(defaultEvents.filter((item) => item.id !== id))
|
||||||
|
}
|
||||||
|
}
|
||||||
35
apiferia/src/app/core/services/pagination.services.ts
Normal file
35
apiferia/src/app/core/services/pagination.services.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PaginationService {
|
||||||
|
page = 1
|
||||||
|
startIndex: number = 0
|
||||||
|
endIndex: number = 0
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
refreshData(displayList: any[], data: any[], pageSize: number) {
|
||||||
|
this.startIndex = (this.page - 1) * pageSize + 1
|
||||||
|
this.endIndex = (this.page - 1) * pageSize + pageSize
|
||||||
|
|
||||||
|
displayList = data
|
||||||
|
.map((item: any, i: number) => ({ id: i + 1, ...item }))
|
||||||
|
.slice(this.startIndex - 1, this.endIndex)
|
||||||
|
return displayList
|
||||||
|
}
|
||||||
|
|
||||||
|
searchTerm(displayList: any[], data: any[], searchQuery: string) {
|
||||||
|
if (searchQuery) {
|
||||||
|
displayList = data.filter((item) =>
|
||||||
|
Object.values(item).some((value: any) =>
|
||||||
|
value.toString().toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
displayList = data
|
||||||
|
}
|
||||||
|
return displayList
|
||||||
|
}
|
||||||
|
}
|
||||||
182
apiferia/src/app/core/services/table.service.ts
Normal file
182
apiferia/src/app/core/services/table.service.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import type { SortDirection } from '@/app/directive/sortable.directive'
|
||||||
|
import { DecimalPipe } from '@angular/common'
|
||||||
|
import { inject, Injectable, PipeTransform } from '@angular/core'
|
||||||
|
import { BehaviorSubject, Observable, of, Subject } from 'rxjs'
|
||||||
|
import { debounceTime, delay, switchMap, tap } from 'rxjs/operators'
|
||||||
|
|
||||||
|
interface SearchResult<T> {
|
||||||
|
items: T[]
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State<T> {
|
||||||
|
page: number
|
||||||
|
startIndex: number
|
||||||
|
endIndex: number
|
||||||
|
pageSize: number
|
||||||
|
searchTerm: string
|
||||||
|
sortColumn: keyof T | ''
|
||||||
|
sortDirection: SortDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
function matches<T>(items: any, term: string, searchFields: (keyof T)[]) {
|
||||||
|
if (!term) return true
|
||||||
|
term = term.toLowerCase()
|
||||||
|
|
||||||
|
for (const field of searchFields) {
|
||||||
|
const value = (items[field] as unknown as string)?.toString().toLowerCase()
|
||||||
|
if (value?.includes(term)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function compare<T>(v1: T, v2: T): number {
|
||||||
|
return v1 < v2 ? -1 : v1 > v2 ? 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class TableService<T extends {}> {
|
||||||
|
private _loading$ = new BehaviorSubject<boolean>(true)
|
||||||
|
private _search$ = new Subject<void>()
|
||||||
|
private _items$ = new BehaviorSubject<T[]>([])
|
||||||
|
private _total$ = new BehaviorSubject<number>(0)
|
||||||
|
|
||||||
|
items: T[] = []
|
||||||
|
private _state: State<T> = {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
searchTerm: '',
|
||||||
|
startIndex: 1,
|
||||||
|
endIndex: 10,
|
||||||
|
sortColumn: '',
|
||||||
|
sortDirection: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
public pipe = inject(DecimalPipe)
|
||||||
|
constructor() {
|
||||||
|
this._search$
|
||||||
|
.pipe(
|
||||||
|
tap(() => this._loading$.next(true)),
|
||||||
|
debounceTime(200),
|
||||||
|
switchMap(() => this._search()),
|
||||||
|
delay(0),
|
||||||
|
tap(() => this._loading$.next(false))
|
||||||
|
)
|
||||||
|
.subscribe((result) => {
|
||||||
|
this._items$.next(result.items)
|
||||||
|
this._total$.next(result.total)
|
||||||
|
})
|
||||||
|
|
||||||
|
this._search$.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
get items$(): Observable<T[]> {
|
||||||
|
return this._items$.asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
get total$(): Observable<number> {
|
||||||
|
return this._total$.asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
get loading$(): Observable<boolean> {
|
||||||
|
return this._loading$.asObservable()
|
||||||
|
}
|
||||||
|
|
||||||
|
get page(): number {
|
||||||
|
return this._state.page
|
||||||
|
}
|
||||||
|
|
||||||
|
get startIndex(): number {
|
||||||
|
return this._state.startIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
get endIndex(): number {
|
||||||
|
return this._state.endIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
get pageSize(): number {
|
||||||
|
return this._state.pageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
get searchTerm(): string {
|
||||||
|
return this._state.searchTerm
|
||||||
|
}
|
||||||
|
|
||||||
|
get sortColumn(): keyof T | '' {
|
||||||
|
return this._state.sortColumn
|
||||||
|
}
|
||||||
|
|
||||||
|
get sortDirection(): SortDirection {
|
||||||
|
return this._state.sortDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
set page(page: number) {
|
||||||
|
this._set({ page })
|
||||||
|
}
|
||||||
|
|
||||||
|
set startIndex(startIndex: number) {
|
||||||
|
this._set({ startIndex })
|
||||||
|
}
|
||||||
|
set endIndex(endIndex: number) {
|
||||||
|
this._set({ endIndex })
|
||||||
|
}
|
||||||
|
|
||||||
|
set pageSize(pageSize: number) {
|
||||||
|
this._set({ pageSize })
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchTerm(searchTerm: string) {
|
||||||
|
this._set({ searchTerm })
|
||||||
|
}
|
||||||
|
|
||||||
|
set sortColumn(sortColumn: keyof T | '') {
|
||||||
|
this._set({ sortColumn })
|
||||||
|
}
|
||||||
|
|
||||||
|
set sortDirection(sortDirection: SortDirection) {
|
||||||
|
this._set({ sortDirection })
|
||||||
|
}
|
||||||
|
|
||||||
|
setItems(items: T[], pageSize: number): void {
|
||||||
|
this.items = items
|
||||||
|
this._set({ pageSize })
|
||||||
|
this._set({ endIndex: pageSize })
|
||||||
|
}
|
||||||
|
|
||||||
|
private _set(patch: Partial<State<T>>): void {
|
||||||
|
Object.assign(this._state, patch)
|
||||||
|
this._search$.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
private _search(): Observable<SearchResult<T>> {
|
||||||
|
const { pageSize, page, searchTerm, sortColumn, sortDirection } =
|
||||||
|
this._state
|
||||||
|
const searchableFields = Object.keys(this.items[0]) as (keyof T)[]
|
||||||
|
|
||||||
|
// filter
|
||||||
|
let filteredItems = this.items.filter((item) =>
|
||||||
|
matches(item, searchTerm, searchableFields)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
if (sortColumn) {
|
||||||
|
filteredItems = [...filteredItems].sort((a, b) => {
|
||||||
|
const res = compare(a[sortColumn], b[sortColumn])
|
||||||
|
return sortDirection === 'asc' ? res : -res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = filteredItems.length
|
||||||
|
|
||||||
|
// Paginate the items
|
||||||
|
this.startIndex = (page - 1) * pageSize
|
||||||
|
this.endIndex = this.startIndex + pageSize
|
||||||
|
const paginatedItems = filteredItems.slice(this.startIndex, this.endIndex)
|
||||||
|
this._loading$.next(false)
|
||||||
|
return of({ items: paginatedItems, total })
|
||||||
|
}
|
||||||
|
}
|
||||||
38
apiferia/src/app/core/services/title.service.ts
Normal file
38
apiferia/src/app/core/services/title.service.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// title.service.ts
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import { Title } from '@angular/platform-browser'
|
||||||
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'
|
||||||
|
import { filter } from 'rxjs/operators'
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class TitleService {
|
||||||
|
constructor(
|
||||||
|
private titleService: Title,
|
||||||
|
private router: Router,
|
||||||
|
private activatedRoute: ActivatedRoute
|
||||||
|
) {}
|
||||||
|
|
||||||
|
init(): void {
|
||||||
|
this.router.events
|
||||||
|
.pipe(filter((event) => event instanceof NavigationEnd))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.updateTitle()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateTitle(): void {
|
||||||
|
let route = this.activatedRoute
|
||||||
|
while (route.firstChild) {
|
||||||
|
route = route.firstChild
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.snapshot.data['title']) {
|
||||||
|
this.titleService.setTitle(
|
||||||
|
route.snapshot.data['title'] +
|
||||||
|
' | Reback - Responsive Angular Admin Dashboard Template'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
apiferia/src/app/directive/flatpickr.directive.ts
Normal file
21
apiferia/src/app/directive/flatpickr.directive.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Directive, ElementRef, Input, OnInit } from '@angular/core'
|
||||||
|
import flatpickr from 'flatpickr'
|
||||||
|
import { Options } from 'flatpickr/dist/types/options'
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[mwlFlatpickr]',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class FlatpickrDirective implements OnInit {
|
||||||
|
@Input() flatpickrOptions: Options = {}
|
||||||
|
|
||||||
|
constructor(private el: ElementRef) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.initFlatpickr()
|
||||||
|
}
|
||||||
|
|
||||||
|
private initFlatpickr() {
|
||||||
|
flatpickr(this.el.nativeElement, this.flatpickrOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
37
apiferia/src/app/directive/iconify.component.ts
Normal file
37
apiferia/src/app/directive/iconify.component.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
CUSTOM_ELEMENTS_SCHEMA,
|
||||||
|
Input,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef,
|
||||||
|
type AfterViewInit,
|
||||||
|
} from '@angular/core'
|
||||||
|
import { getIcon, loadIcon, buildIcon } from 'iconify-icon'
|
||||||
|
import { getIconData, iconToSVG, iconToHTML, replaceIDs } from '@iconify/utils'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ng-iconify',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
template: `<template #iconTemplate></template>`,
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
styles: `
|
||||||
|
:host(ng-iconify) {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class IconifyComponent implements AfterViewInit {
|
||||||
|
@Input() icon: string = ''
|
||||||
|
@ViewChild('iconTemplate') iconTemplate!: ElementRef
|
||||||
|
|
||||||
|
svg = ''
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
const builtIcon = buildIcon(getIcon(this.icon))
|
||||||
|
this.svg = iconToHTML(builtIcon.body, builtIcon.attributes)
|
||||||
|
this.iconTemplate.nativeElement.innerHTML = this.svg
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
}
|
||||||
33
apiferia/src/app/directive/select-form-input.directive.ts
Normal file
33
apiferia/src/app/directive/select-form-input.directive.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Directive, ElementRef, Input, type OnInit } from '@angular/core'
|
||||||
|
import Choices, { Options as ChoiceOption } from 'choices.js'
|
||||||
|
|
||||||
|
export type SelectOptions = Partial<ChoiceOption>
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[selectFormInput]',
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class SelectFormInputDirective implements OnInit {
|
||||||
|
@Input() className?: string
|
||||||
|
@Input() onChange?: (text: string) => void
|
||||||
|
@Input() options?: SelectOptions
|
||||||
|
|
||||||
|
constructor(private eleRef: ElementRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
const choices = new Choices(this.eleRef.nativeElement, {
|
||||||
|
...this.options,
|
||||||
|
placeholder: true,
|
||||||
|
placeholderValue: 'Type and hit enter',
|
||||||
|
allowHTML: true,
|
||||||
|
shouldSort: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
choices.passedElement.element.addEventListener('change', (e: Event) => {
|
||||||
|
if (!(e.target instanceof HTMLSelectElement)) return
|
||||||
|
if (this.onChange) {
|
||||||
|
this.onChange(e.target.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
33
apiferia/src/app/directive/sortable.directive.ts
Normal file
33
apiferia/src/app/directive/sortable.directive.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { Directive, EventEmitter, Input, Output } from '@angular/core'
|
||||||
|
|
||||||
|
export type SortDirection = 'asc' | 'desc' | ''
|
||||||
|
const rotate: { [key: string]: SortDirection } = {
|
||||||
|
asc: 'desc',
|
||||||
|
desc: '',
|
||||||
|
'': 'asc',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SortEvent<T> {
|
||||||
|
column: keyof T | ''
|
||||||
|
direction: SortDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'th[sortable]',
|
||||||
|
standalone: true,
|
||||||
|
host: {
|
||||||
|
'[class.asc]': 'direction === "asc"',
|
||||||
|
'[class.desc]': 'direction === "desc"',
|
||||||
|
'(click)': 'rotate()',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export class NgbdSortableHeader<T> {
|
||||||
|
@Input() sortable: keyof T | '' = ''
|
||||||
|
@Input() direction: SortDirection = ''
|
||||||
|
@Output() sort = new EventEmitter<SortEvent<T>>()
|
||||||
|
|
||||||
|
rotate() {
|
||||||
|
this.direction = rotate[this.direction]
|
||||||
|
this.sort.emit({ column: this.sortable, direction: this.direction })
|
||||||
|
}
|
||||||
|
}
|
||||||
18
apiferia/src/app/helpers/countDown.ts
Normal file
18
apiferia/src/app/helpers/countDown.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const eventDate = new Date('Jan 17, 2026 12:00:01')
|
||||||
|
|
||||||
|
const calculateTimeToEvent = () => {
|
||||||
|
const currentDate = new Date()
|
||||||
|
|
||||||
|
const timeRemaining = eventDate.getTime() - currentDate.getTime()
|
||||||
|
|
||||||
|
const days = Math.floor(timeRemaining / (1000 * 60 * 60 * 24))
|
||||||
|
const hours = Math.floor(
|
||||||
|
(timeRemaining % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
|
||||||
|
)
|
||||||
|
const minutes = Math.floor((timeRemaining % (1000 * 60 * 60)) / (1000 * 60))
|
||||||
|
const seconds = Math.floor((timeRemaining % (1000 * 60)) / 1000)
|
||||||
|
|
||||||
|
return { days, hours, minutes, seconds }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default calculateTimeToEvent
|
||||||
138
apiferia/src/app/helpers/fake-backend.ts
Normal file
138
apiferia/src/app/helpers/fake-backend.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import { User } from "@/app/store/authentication/auth.model"
|
||||||
|
import { Injectable } from '@angular/core'
|
||||||
|
import {
|
||||||
|
HttpRequest,
|
||||||
|
HttpResponse,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpInterceptor,
|
||||||
|
HTTP_INTERCEPTORS,
|
||||||
|
HttpClient,
|
||||||
|
HttpHeaders,
|
||||||
|
} from '@angular/common/http'
|
||||||
|
import { Observable, throwError } from 'rxjs'
|
||||||
|
import { catchError, map } from 'rxjs/operators'
|
||||||
|
import { environment } from '../../environments/environment'
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FakeBackendInterceptor implements HttpInterceptor {
|
||||||
|
private apiUrl = environment.apiUrl;
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
intercept(
|
||||||
|
request: HttpRequest<any>,
|
||||||
|
next: HttpHandler
|
||||||
|
): Observable<HttpEvent<any>> {
|
||||||
|
|
||||||
|
// Solo interceptar llamadas a /api
|
||||||
|
if (!request.url.includes('/api/')) {
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener el token del localStorage
|
||||||
|
const token = localStorage.getItem('access_token');
|
||||||
|
|
||||||
|
// Construir la URL real
|
||||||
|
const apiEndpoint = request.url.replace('/api/', '/');
|
||||||
|
const fullUrl = `${this.apiUrl}${apiEndpoint}`;
|
||||||
|
|
||||||
|
// Clonar la petición con la nueva URL y headers
|
||||||
|
let headers = new HttpHeaders({
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (token && !request.url.includes('/auth/')) {
|
||||||
|
headers = headers.set('Authorization', `Bearer ${token}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiReq = request.clone({
|
||||||
|
url: fullUrl,
|
||||||
|
headers: headers
|
||||||
|
});
|
||||||
|
|
||||||
|
// Manejar login especialmente
|
||||||
|
if (request.url.endsWith('/api/login') && request.method === 'POST') {
|
||||||
|
return this.http.post<any>(`${this.apiUrl}/auth/login`, request.body).pipe(
|
||||||
|
map(response => {
|
||||||
|
if (response && response.access_token) {
|
||||||
|
// Guardar token y usuario
|
||||||
|
localStorage.setItem('access_token', response.access_token);
|
||||||
|
localStorage.setItem('currentUser', JSON.stringify(response.user));
|
||||||
|
|
||||||
|
// Retornar en el formato esperado por el template
|
||||||
|
return new HttpResponse({
|
||||||
|
status: 200,
|
||||||
|
body: {
|
||||||
|
...response.user,
|
||||||
|
token: response.access_token,
|
||||||
|
name: response.user.firstName + ' ' + response.user.lastName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new HttpResponse({ status: 200, body: response });
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('Login error:', error);
|
||||||
|
return throwError({
|
||||||
|
status: error.status || 400,
|
||||||
|
error: { message: error.error?.message || 'Error al iniciar sesión' }
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manejar registro
|
||||||
|
if (request.url.endsWith('/api/signup') && request.method === 'POST') {
|
||||||
|
const [firstName, lastName] = request.body?.name?.split(' ') || ['', ''];
|
||||||
|
const registerData = {
|
||||||
|
...request.body,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
role: 'cliente' // Rol por defecto
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.http.post<any>(`${this.apiUrl}/auth/register`, registerData).pipe(
|
||||||
|
map(response => {
|
||||||
|
return new HttpResponse({
|
||||||
|
status: 200,
|
||||||
|
body: response
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
return throwError({
|
||||||
|
status: error.status || 400,
|
||||||
|
error: { message: error.error?.message || 'Error al registrar usuario' }
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Para todas las demás peticiones, enviarlas a la API real
|
||||||
|
return this.http.request(apiReq.method, apiReq.url, {
|
||||||
|
body: apiReq.body,
|
||||||
|
headers: apiReq.headers,
|
||||||
|
observe: 'response',
|
||||||
|
responseType: 'json'
|
||||||
|
}).pipe(
|
||||||
|
map(response => {
|
||||||
|
return response;
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
// Si es error 401, limpiar sesión
|
||||||
|
if (error.status === 401) {
|
||||||
|
localStorage.removeItem('access_token');
|
||||||
|
localStorage.removeItem('currentUser');
|
||||||
|
}
|
||||||
|
return throwError(error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export let FakeBackendProvider = {
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: FakeBackendInterceptor,
|
||||||
|
multi: true,
|
||||||
|
}
|
||||||
35
apiferia/src/app/helpers/utils.ts
Normal file
35
apiferia/src/app/helpers/utils.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import type { MenuItem } from '../common/menu-meta'
|
||||||
|
|
||||||
|
export const findAllParent = (menuItems: MenuItem[], menuItem: any): any => {
|
||||||
|
let parents = []
|
||||||
|
const parent = findMenuItem(menuItems, menuItem['parentKey'])
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
parents.push(parent['key'])
|
||||||
|
if (parent['parentKey'])
|
||||||
|
parents = [...parents, ...findAllParent(menuItems, parent)]
|
||||||
|
}
|
||||||
|
return parents
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findMenuItem = (
|
||||||
|
menuItems: MenuItem[],
|
||||||
|
menuItemKey: string
|
||||||
|
): any => {
|
||||||
|
if (menuItems && menuItemKey) {
|
||||||
|
for (var i = 0; i < menuItems.length; i++) {
|
||||||
|
if (menuItems[i].key === menuItemKey) {
|
||||||
|
return menuItems[i]
|
||||||
|
}
|
||||||
|
var found = findMenuItem(menuItems[i].subMenu, menuItemKey)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addOrSubtractDaysFromDate(days: number): Date {
|
||||||
|
const result = new Date()
|
||||||
|
result.setDate(result.getDate() + days)
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { AuthLayoutComponent } from './auth-layout.component'
|
||||||
|
|
||||||
|
describe('AuthLayoutComponent', () => {
|
||||||
|
let component: AuthLayoutComponent
|
||||||
|
let fixture: ComponentFixture<AuthLayoutComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [AuthLayoutComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(AuthLayoutComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
inject,
|
||||||
|
Renderer2,
|
||||||
|
type OnDestroy,
|
||||||
|
type OnInit,
|
||||||
|
} from '@angular/core'
|
||||||
|
import { RouterModule } from '@angular/router'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-auth-layout',
|
||||||
|
standalone: true,
|
||||||
|
imports: [RouterModule],
|
||||||
|
template: ` <div class="account-pages pt-2 pt-sm-5 pb-4 pb-sm-5">
|
||||||
|
<div class="container">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>`,
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class AuthLayoutComponent implements OnInit, OnDestroy {
|
||||||
|
private renderer = inject(Renderer2)
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.renderer.addClass(document.body, 'authentication-bg')
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.renderer.removeClass(document.body, 'authentication-bg')
|
||||||
|
}
|
||||||
|
}
|
||||||
17
apiferia/src/app/layouts/footer/footer.component.html
Normal file
17
apiferia/src/app/layouts/footer/footer.component.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<footer class="footer">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
{{ year }}
|
||||||
|
© Reback. Crafted by
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:heart-duotone"
|
||||||
|
class="fs-18 align-middle text-danger"
|
||||||
|
></iconify-icon>
|
||||||
|
<a href="" class="fw-bold footer-text" target="_blank">{{
|
||||||
|
credits.name
|
||||||
|
}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
22
apiferia/src/app/layouts/footer/footer.component.spec.ts
Normal file
22
apiferia/src/app/layouts/footer/footer.component.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { FooterComponent } from './footer.component'
|
||||||
|
|
||||||
|
describe('FooterComponent', () => {
|
||||||
|
let component: FooterComponent
|
||||||
|
let fixture: ComponentFixture<FooterComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [FooterComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(FooterComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
15
apiferia/src/app/layouts/footer/footer.component.ts
Normal file
15
apiferia/src/app/layouts/footer/footer.component.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { credits, currentYear } from '@/app/common/constants'
|
||||||
|
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-footer',
|
||||||
|
standalone: true,
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './footer.component.html',
|
||||||
|
styles: ``,
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
})
|
||||||
|
export class FooterComponent {
|
||||||
|
year = currentYear
|
||||||
|
credits = credits
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { PrivateLayoutComponent } from './private-layout.component'
|
||||||
|
|
||||||
|
describe('PrivateLayoutComponent', () => {
|
||||||
|
let component: PrivateLayoutComponent
|
||||||
|
let fixture: ComponentFixture<PrivateLayoutComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [PrivateLayoutComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(PrivateLayoutComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Component, inject } from '@angular/core'
|
||||||
|
import { Store } from '@ngrx/store'
|
||||||
|
import { VerticalComponent } from '../vertical/vertical.component'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-private-layout',
|
||||||
|
standalone: true,
|
||||||
|
imports: [VerticalComponent],
|
||||||
|
template: ` <app-vertical></app-vertical> `,
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class PrivateLayoutComponent {
|
||||||
|
layoutType: any
|
||||||
|
|
||||||
|
private store = inject(Store)
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.store.select('layout').subscribe((data) => {
|
||||||
|
this.layoutType = data.LAYOUT
|
||||||
|
document.documentElement.setAttribute('data-bs-theme', data.LAYOUT_THEME)
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('data-menu-color', data.MENU_COLOR)
|
||||||
|
document.documentElement.setAttribute(
|
||||||
|
'data-topbar-color',
|
||||||
|
data.TOPBAR_COLOR
|
||||||
|
)
|
||||||
|
document.documentElement.setAttribute('data-menu-size', data.MENU_SIZE)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="offcanvas-end border-0"
|
||||||
|
tabindex="-1"
|
||||||
|
id="theme-settings-offcanvas"
|
||||||
|
>
|
||||||
|
<div class="d-flex align-items-center bg-primary p-3 offcanvas-header">
|
||||||
|
<h5 class="text-white m-0">Theme Settings</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-close btn-close-white ms-auto"
|
||||||
|
(click)="offcanvas.dismiss('Cross click')"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="offcanvas-body p-0">
|
||||||
|
<ngx-simplebar style="height: calc(100vh - 140px)">
|
||||||
|
<div class="p-3 settings-bar">
|
||||||
|
<div>
|
||||||
|
<h5 class="mb-3 font-16 fw-semibold">Color Scheme</h5>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-bs-theme"
|
||||||
|
id="layout-color-light"
|
||||||
|
value="light"
|
||||||
|
[checked]="color == 'light'"
|
||||||
|
(change)="changeLayoutColor('light')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="layout-color-light">
|
||||||
|
Light
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-bs-theme"
|
||||||
|
id="layout-color-dark"
|
||||||
|
value="dark"
|
||||||
|
[checked]="color == 'dark'"
|
||||||
|
(change)="changeLayoutColor('dark')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="layout-color-dark"
|
||||||
|
>Dark
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h5 class="my-3 font-16 fw-semibold">Topbar Color</h5>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-topbar-color"
|
||||||
|
id="topbar-color-light"
|
||||||
|
value="light"
|
||||||
|
[checked]="topbar == 'light'"
|
||||||
|
(change)="changeTopbar('light')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label"> Light </label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-topbar-color"
|
||||||
|
id="topbar-color-dark"
|
||||||
|
value="dark"
|
||||||
|
[checked]="topbar == 'dark'"
|
||||||
|
(change)="changeTopbar('dark')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="topbar-color-dark">
|
||||||
|
Dark
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h5 class="my-3 font-16 fw-semibold">Menu Color</h5>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-menu-color"
|
||||||
|
id="leftbar-color-light"
|
||||||
|
value="light"
|
||||||
|
[checked]="menucolor == 'light'"
|
||||||
|
(change)="changeMenu('light')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="leftbar-color-light">
|
||||||
|
Light
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-menu-color"
|
||||||
|
id="leftbar-color-dark"
|
||||||
|
value="dark"
|
||||||
|
[checked]="menucolor == 'dark'"
|
||||||
|
(change)="changeMenu('dark')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="leftbar-color-dark">
|
||||||
|
Dark
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h5 class="my-3 font-16 fw-semibold">Sidebar Size</h5>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-menu-size"
|
||||||
|
id="leftbar-size-default"
|
||||||
|
value="default"
|
||||||
|
[checked]="sidebarsize == 'default'"
|
||||||
|
(change)="changeSize('default')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="leftbar-size-default">
|
||||||
|
Default
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-menu-size"
|
||||||
|
id="leftbar-size-small"
|
||||||
|
value="condensed"
|
||||||
|
[checked]="sidebarsize == 'condensed'"
|
||||||
|
(change)="changeSize('condensed')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="leftbar-size-small">
|
||||||
|
Condensed
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-menu-size"
|
||||||
|
id="leftbar-hidden"
|
||||||
|
value="hidden"
|
||||||
|
[checked]="sidebarsize == 'hidden'"
|
||||||
|
(change)="changeSize('hidden')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="leftbar-hidden">
|
||||||
|
Hidden
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-menu-size"
|
||||||
|
id="leftbar-size-small-hover-active"
|
||||||
|
value="sm-hover-active"
|
||||||
|
[checked]="sidebarsize == 'sm-hover-active'"
|
||||||
|
(change)="changeSize('sm-hover-active')"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
class="form-check-label"
|
||||||
|
for="leftbar-size-small-hover-active"
|
||||||
|
>
|
||||||
|
Small Hover Active
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="radio"
|
||||||
|
name="data-menu-size"
|
||||||
|
id="leftbar-size-small-hover"
|
||||||
|
value="sm-hover"
|
||||||
|
[checked]="sidebarsize == 'sm-hover'"
|
||||||
|
(change)="changeSize('sm-hover')"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="leftbar-size-full">
|
||||||
|
Small Hover
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ngx-simplebar>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-footer border-top p-3 text-center">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-danger w-100"
|
||||||
|
id="reset-layout"
|
||||||
|
(click)="reset()"
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { RightSidebarComponent } from './right-sidebar.component'
|
||||||
|
|
||||||
|
describe('RightSidebarComponent', () => {
|
||||||
|
let component: RightSidebarComponent
|
||||||
|
let fixture: ComponentFixture<RightSidebarComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [RightSidebarComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RightSidebarComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { Component, inject } from '@angular/core'
|
||||||
|
import { NgbActiveOffcanvas } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { Store } from '@ngrx/store'
|
||||||
|
import { SimplebarAngularModule } from 'simplebar-angular'
|
||||||
|
import {
|
||||||
|
changemenucolor,
|
||||||
|
changesidebarsize,
|
||||||
|
changetheme,
|
||||||
|
changetopbarcolor,
|
||||||
|
resetState,
|
||||||
|
} from '../../store/layout/layout-action'
|
||||||
|
import {
|
||||||
|
getLayoutColor,
|
||||||
|
getMenucolor,
|
||||||
|
getSidebarsize,
|
||||||
|
getTopbarcolor,
|
||||||
|
} from '../../store/layout/layout-selector'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-right-sidebar',
|
||||||
|
standalone: true,
|
||||||
|
imports: [SimplebarAngularModule],
|
||||||
|
templateUrl: './right-sidebar.component.html',
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class RightSidebarComponent {
|
||||||
|
public isRightSidebarOpen: boolean = false
|
||||||
|
|
||||||
|
offcanvas = inject(NgbActiveOffcanvas)
|
||||||
|
store = inject(Store)
|
||||||
|
|
||||||
|
color: any
|
||||||
|
topbar: any
|
||||||
|
menucolor: any
|
||||||
|
sidebarsize: any
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.store.select('layout').subscribe((data: any) => {
|
||||||
|
this.color = data.LAYOUT_THEME
|
||||||
|
this.topbar = data.TOPBAR_COLOR
|
||||||
|
this.menucolor = data.MENU_COLOR
|
||||||
|
this.sidebarsize = data.MENU_SIZE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change Layout Color
|
||||||
|
changeLayoutColor(color: any) {
|
||||||
|
this.store.dispatch(changetheme({ color }))
|
||||||
|
this.store.select(getLayoutColor).subscribe((color) => {
|
||||||
|
document.documentElement.setAttribute('data-bs-theme', color)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change Topbar Color
|
||||||
|
changeTopbar(topbar: any) {
|
||||||
|
this.store.dispatch(changetopbarcolor({ topbar }))
|
||||||
|
this.store.select(getTopbarcolor).subscribe((topbar) => {
|
||||||
|
document.documentElement.setAttribute('data-topbar-color', topbar)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change Menu Color
|
||||||
|
changeMenu(menu: any) {
|
||||||
|
this.store.dispatch(changemenucolor({ menu }))
|
||||||
|
this.store.select(getMenucolor).subscribe((menucolor) => {
|
||||||
|
document.documentElement.setAttribute('data-menu-color', menucolor)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change Sidebar Size
|
||||||
|
changeSize(size: any) {
|
||||||
|
this.store.dispatch(changesidebarsize({ size }))
|
||||||
|
this.store.select(getSidebarsize).subscribe((size) => {
|
||||||
|
document.documentElement.setAttribute('data-menu-size', size)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset Option
|
||||||
|
reset() {
|
||||||
|
this.store.dispatch(resetState())
|
||||||
|
}
|
||||||
|
}
|
||||||
159
apiferia/src/app/layouts/sidebar/sidebar.component.html
Normal file
159
apiferia/src/app/layouts/sidebar/sidebar.component.html
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<div class="main-nav">
|
||||||
|
<app-logo-box className="logo-box" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button-sm-hover"
|
||||||
|
aria-label="Show Full Sidebar"
|
||||||
|
(click)="changeSidebarSize()"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:arrow-left-4-square-duotone"
|
||||||
|
class="button-sm-hover-icon"
|
||||||
|
></iconify-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ngx-simplebar class="scrollbar" id="leftside-menu-container" data-simplebar>
|
||||||
|
<ul class="navbar-nav" id="navbar-nav">
|
||||||
|
@for (item of menuItems; track item.label) {
|
||||||
|
@if (item.isTitle) {
|
||||||
|
<li class="menu-title">{{ item.label }}</li>
|
||||||
|
} @else {
|
||||||
|
@if (hasSubmenu(item)) {
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="
|
||||||
|
MenuItemWithChildren;
|
||||||
|
context: {
|
||||||
|
menu: item,
|
||||||
|
linkClassName: 'nav-link menu-arrow',
|
||||||
|
subMenuClassNames: 'nav sub-navbar-nav',
|
||||||
|
itemClassName: 'nav-item',
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
} @else {
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="
|
||||||
|
MenuItem;
|
||||||
|
context: {
|
||||||
|
menu: item,
|
||||||
|
linkClassName: 'nav-link nav-link-ref',
|
||||||
|
itemClassName: 'nav-item',
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</ngx-simplebar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
#MenuItemWithChildren
|
||||||
|
let-menu="menu"
|
||||||
|
let-itemClassName="itemClassName"
|
||||||
|
let-linkClassName="linkClassName"
|
||||||
|
let-subMenuClassNames="subMenuClassNames"
|
||||||
|
>
|
||||||
|
<li [class]="itemClassName">
|
||||||
|
<a
|
||||||
|
[class]="linkClassName"
|
||||||
|
[ngClass]="{ active: activeMenuItems.includes(menu.key) }"
|
||||||
|
(click)="toggleMenuItem(menu, collapse)"
|
||||||
|
role="button"
|
||||||
|
[attr.aria-expanded]="!menu.collapsed"
|
||||||
|
aria-controls="sidebarDashboards"
|
||||||
|
[attr.aria-controls]="menu.key"
|
||||||
|
>
|
||||||
|
@if (menu.icon) {
|
||||||
|
<span class="nav-icon">
|
||||||
|
<iconify-icon [icon]="menu.icon"></iconify-icon>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
<span class="nav-text"> {{ menu.label }} </span>
|
||||||
|
</a>
|
||||||
|
<div
|
||||||
|
#collapse="ngbCollapse"
|
||||||
|
[(ngbCollapse)]="menu.collapsed"
|
||||||
|
class="collapse"
|
||||||
|
id="sidebarDashboards"
|
||||||
|
>
|
||||||
|
<ul [class]="subMenuClassNames">
|
||||||
|
@for (child of menu.subMenu; track child.label) {
|
||||||
|
@if (hasSubmenu(child)) {
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="
|
||||||
|
MenuItemWithChildren;
|
||||||
|
context: {
|
||||||
|
menu: child,
|
||||||
|
linkClassName: 'sub-nav-link menu-arrow',
|
||||||
|
itemClassName: 'sub-nav-item',
|
||||||
|
subMenuClassNames: 'nav sub-navbar-nav',
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
} @else {
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="
|
||||||
|
MenuItem;
|
||||||
|
context: {
|
||||||
|
menu: child,
|
||||||
|
linkClassName: 'sub-nav-link nav-link-ref',
|
||||||
|
itemClassName: 'sub-nav-item',
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template
|
||||||
|
#MenuItem
|
||||||
|
let-menu="menu"
|
||||||
|
let-linkClassName="linkClassName"
|
||||||
|
let-itemClassName="itemClassName"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
[class]="itemClassName"
|
||||||
|
[ngClass]="{ active: activeMenuItems.includes(menu.key) }"
|
||||||
|
>
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="
|
||||||
|
MenuItemLink;
|
||||||
|
context: { menu: menu, className: linkClassName }
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
</li>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #MenuItemLink let-menu="menu" let-className="className">
|
||||||
|
<a
|
||||||
|
[routerLink]="menu.link"
|
||||||
|
[class]="className"
|
||||||
|
[ngClass]="{ active: activeMenuItems.includes(menu.key) }"
|
||||||
|
[attr.aria-controls]="menu.key"
|
||||||
|
>
|
||||||
|
@if (menu.icon) {
|
||||||
|
<span class="nav-icon">
|
||||||
|
<iconify-icon [icon]="menu.icon"></iconify-icon>
|
||||||
|
</span>
|
||||||
|
<span class="nav-text">{{ menu.label }}</span>
|
||||||
|
} @else {
|
||||||
|
{{ menu.label }}
|
||||||
|
}
|
||||||
|
@if (menu.badge) {
|
||||||
|
<span class="badge badge-pill text-end bg-{{ menu.badge.variant }}">{{
|
||||||
|
menu.badge.text
|
||||||
|
}}</span>
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
</ng-template>
|
||||||
22
apiferia/src/app/layouts/sidebar/sidebar.component.spec.ts
Normal file
22
apiferia/src/app/layouts/sidebar/sidebar.component.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { SidebarComponent } from './sidebar.component'
|
||||||
|
|
||||||
|
describe('SidebarComponent', () => {
|
||||||
|
let component: SidebarComponent
|
||||||
|
let fixture: ComponentFixture<SidebarComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SidebarComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SidebarComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
201
apiferia/src/app/layouts/sidebar/sidebar.component.ts
Normal file
201
apiferia/src/app/layouts/sidebar/sidebar.component.ts
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, Component, inject } from '@angular/core'
|
||||||
|
import { SimplebarAngularModule } from 'simplebar-angular'
|
||||||
|
import { NavigationEnd, Router, RouterModule } from '@angular/router'
|
||||||
|
import {
|
||||||
|
NgbCollapse,
|
||||||
|
NgbCollapseModule,
|
||||||
|
NgbTooltipModule,
|
||||||
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { CommonModule } from '@angular/common'
|
||||||
|
import { findAllParent, findMenuItem } from '../../helpers/utils'
|
||||||
|
import { LogoBoxComponent } from '@/app/components/logo-box.component'
|
||||||
|
import { MENU, type MenuItem } from '@/app/common/menu-meta'
|
||||||
|
import { changesidebarsize } from '@/app/store/layout/layout-action'
|
||||||
|
import { Store } from '@ngrx/store'
|
||||||
|
import { getSidebarsize } from '@/app/store/layout/layout-selector'
|
||||||
|
import { basePath } from '@/app/common/constants'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sidebar',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
SimplebarAngularModule,
|
||||||
|
RouterModule,
|
||||||
|
NgbCollapseModule,
|
||||||
|
CommonModule,
|
||||||
|
NgbTooltipModule,
|
||||||
|
LogoBoxComponent,
|
||||||
|
],
|
||||||
|
templateUrl: './sidebar.component.html',
|
||||||
|
styles: ``,
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
})
|
||||||
|
export class SidebarComponent {
|
||||||
|
menuItems: MenuItem[] = []
|
||||||
|
activeMenuItems: string[] = []
|
||||||
|
|
||||||
|
store = inject(Store)
|
||||||
|
router = inject(Router)
|
||||||
|
trimmedURL = this.router.url?.replaceAll(
|
||||||
|
basePath !== '' ? basePath + '/' : '',
|
||||||
|
'/'
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.router.events.forEach((event) => {
|
||||||
|
if (event instanceof NavigationEnd) {
|
||||||
|
this.trimmedURL = this.router.url?.replaceAll(
|
||||||
|
basePath !== '' ? basePath + '/' : '',
|
||||||
|
'/'
|
||||||
|
)
|
||||||
|
this._activateMenu()
|
||||||
|
setTimeout(() => {
|
||||||
|
this.scrollToActive()
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.initMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
initMenu(): void {
|
||||||
|
this.menuItems = MENU
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this._activateMenu()
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
this.scrollToActive()
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToActive(): void {
|
||||||
|
const activatedItem = document.querySelector('.nav-item li a.active')
|
||||||
|
if (activatedItem) {
|
||||||
|
const simplebarContent = document.querySelector(
|
||||||
|
'.main-nav .simplebar-content-wrapper'
|
||||||
|
)
|
||||||
|
if (simplebarContent) {
|
||||||
|
const activatedItemRect = activatedItem.getBoundingClientRect()
|
||||||
|
const simplebarContentRect = simplebarContent.getBoundingClientRect()
|
||||||
|
const activatedItemOffsetTop =
|
||||||
|
activatedItemRect.top + simplebarContent.scrollTop
|
||||||
|
const centerOffset =
|
||||||
|
activatedItemOffsetTop -
|
||||||
|
simplebarContentRect.top -
|
||||||
|
simplebarContent.clientHeight / 2 +
|
||||||
|
activatedItemRect.height / 2
|
||||||
|
this.scrollTo(simplebarContent, centerOffset, 600)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
easeInOutQuad(t: number, b: number, c: number, d: number): number {
|
||||||
|
t /= d / 2
|
||||||
|
if (t < 1) return (c / 2) * t * t + b
|
||||||
|
t--
|
||||||
|
return (-c / 2) * (t * (t - 2) - 1) + b
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTo(element: Element, to: number, duration: number): void {
|
||||||
|
const start = element.scrollTop
|
||||||
|
const change = to - start
|
||||||
|
const increment = 20
|
||||||
|
let currentTime = 0
|
||||||
|
|
||||||
|
const animateScroll = () => {
|
||||||
|
currentTime += increment
|
||||||
|
const val = this.easeInOutQuad(currentTime, start, change, duration)
|
||||||
|
element.scrollTop = val
|
||||||
|
if (currentTime < duration) {
|
||||||
|
setTimeout(animateScroll, increment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animateScroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
_activateMenu(): void {
|
||||||
|
const div = document.querySelector('.navbar-nav')
|
||||||
|
|
||||||
|
let matchingMenuItem = null
|
||||||
|
|
||||||
|
if (div) {
|
||||||
|
let items: any = div.getElementsByClassName('nav-link-ref')
|
||||||
|
for (let i = 0; i < items.length; ++i) {
|
||||||
|
if (
|
||||||
|
this.trimmedURL === items[i].pathname ||
|
||||||
|
(this.trimmedURL.startsWith('/invoice/') &&
|
||||||
|
items[i].pathname === '/invoice/RB6985') ||
|
||||||
|
(this.trimmedURL.startsWith('/ecommerce/product/') &&
|
||||||
|
items[i].pathname === '/ecommerce/product/1')
|
||||||
|
) {
|
||||||
|
matchingMenuItem = items[i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingMenuItem) {
|
||||||
|
const mid = matchingMenuItem.getAttribute('aria-controls')
|
||||||
|
const activeMt = findMenuItem(this.menuItems, mid)
|
||||||
|
|
||||||
|
if (activeMt) {
|
||||||
|
const matchingObjs = [
|
||||||
|
activeMt['key'],
|
||||||
|
...findAllParent(this.menuItems, activeMt),
|
||||||
|
]
|
||||||
|
|
||||||
|
this.activeMenuItems = matchingObjs
|
||||||
|
this.menuItems.forEach((menu: MenuItem) => {
|
||||||
|
menu.collapsed = !matchingObjs.includes(menu.key!)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true or false if given menu item has child or not
|
||||||
|
* @param item menuItem
|
||||||
|
*/
|
||||||
|
hasSubmenu(menu: MenuItem): boolean {
|
||||||
|
return menu.subMenu ? true : false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toggles open menu
|
||||||
|
* @param menuItem clicked menuitem
|
||||||
|
* @param collapse collpase instance
|
||||||
|
*/
|
||||||
|
toggleMenuItem(menuItem: MenuItem, collapse: NgbCollapse): void {
|
||||||
|
collapse.toggle()
|
||||||
|
let openMenuItems: string[]
|
||||||
|
if (!menuItem.collapsed) {
|
||||||
|
openMenuItems = [
|
||||||
|
menuItem['key'],
|
||||||
|
...findAllParent(this.menuItems, menuItem),
|
||||||
|
]
|
||||||
|
this.menuItems.forEach((menu: MenuItem) => {
|
||||||
|
if (!openMenuItems.includes(menu.key!)) {
|
||||||
|
menu.collapsed = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeSidebarSize() {
|
||||||
|
let size = document.documentElement.getAttribute('data-menu-size')
|
||||||
|
if (size == 'sm-hover') {
|
||||||
|
size = 'sm-hover-active'
|
||||||
|
} else {
|
||||||
|
size = 'sm-hover'
|
||||||
|
}
|
||||||
|
this.store.dispatch(changesidebarsize({ size }))
|
||||||
|
this.store.select(getSidebarsize).subscribe((size) => {
|
||||||
|
document.documentElement.setAttribute('data-menu-size', size)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
164
apiferia/src/app/layouts/topbar/data.ts
Normal file
164
apiferia/src/app/layouts/topbar/data.ts
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
import { addOrSubtractDaysFromDate } from '@/app/helpers/utils'
|
||||||
|
|
||||||
|
const bitbucketImg = 'assets/images/brands/bitbucket.svg'
|
||||||
|
const dribbleImg = 'assets/images/brands/dribbble.svg'
|
||||||
|
const dropboxImg = 'assets/images/brands/dropbox.svg'
|
||||||
|
const githubImg = 'assets/images/brands/github.svg'
|
||||||
|
const slackImg = 'assets/images/brands/slack.svg'
|
||||||
|
const smImg3 = 'assets/images/small/img-3.jpg'
|
||||||
|
const smImg4 = 'assets/images/small/img-4.jpg'
|
||||||
|
const smImg6 = 'assets/images/small/img-6.jpg'
|
||||||
|
const avatar1 = 'assets/images/users/avatar-1.jpg'
|
||||||
|
const avatar3 = 'assets/images/users/avatar-3.jpg'
|
||||||
|
const avatar5 = 'assets/images/users/avatar-5.jpg'
|
||||||
|
const avatar6 = 'assets/images/users/avatar-6.jpg'
|
||||||
|
const avatar7 = 'assets/images/users/avatar-7.jpg'
|
||||||
|
|
||||||
|
export type BootstrapVariantType =
|
||||||
|
| 'primary'
|
||||||
|
| 'secondary'
|
||||||
|
| 'success'
|
||||||
|
| 'danger'
|
||||||
|
| 'warning'
|
||||||
|
| 'info'
|
||||||
|
| 'dark'
|
||||||
|
| 'light'
|
||||||
|
|
||||||
|
export type FileType = Partial<File> & {
|
||||||
|
preview?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ActivityType = {
|
||||||
|
title: string
|
||||||
|
icon?: string
|
||||||
|
variant?: BootstrapVariantType
|
||||||
|
status?: 'completed' | 'latest'
|
||||||
|
files?: FileType[]
|
||||||
|
time: Date
|
||||||
|
type?: 'task' | 'design' | 'achievement'
|
||||||
|
content?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppType = {
|
||||||
|
image: string
|
||||||
|
name: string
|
||||||
|
handle: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NotificationType = {
|
||||||
|
from: string
|
||||||
|
content: string
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const appsData: AppType[] = [
|
||||||
|
{
|
||||||
|
image: githubImg,
|
||||||
|
name: 'Github',
|
||||||
|
handle: '@reback',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: bitbucketImg,
|
||||||
|
name: 'Bitbucket',
|
||||||
|
handle: '@reback',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: dribbleImg,
|
||||||
|
name: 'Dribble',
|
||||||
|
handle: '@username',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: dropboxImg,
|
||||||
|
name: 'Dropbox',
|
||||||
|
handle: '@username',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: slackImg,
|
||||||
|
name: 'Slack',
|
||||||
|
handle: '@reback',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const notificationsData: NotificationType[] = [
|
||||||
|
{
|
||||||
|
from: 'Josephine Thompson',
|
||||||
|
content:
|
||||||
|
'commented on admin panel "Wow 😍! this admin looks good and awesome design"',
|
||||||
|
icon: avatar1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: 'Donoghue Susan',
|
||||||
|
content: 'Hi, How are you? What about our next meeting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: 'Jacob Gines',
|
||||||
|
content: "Answered to your comment on the cash flow forecast's graph 🔔.",
|
||||||
|
icon: avatar3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: 'Shawn Bunch',
|
||||||
|
content: 'Commented on Admin',
|
||||||
|
icon: avatar5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: 'Vanessa R. Davis',
|
||||||
|
content: 'Delivery processing your order is being shipped',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const activityStreamData: ActivityType[] = [
|
||||||
|
{
|
||||||
|
title: 'Report-Fix / Update',
|
||||||
|
variant: 'danger',
|
||||||
|
type: 'task',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
name: 'Concept.fig',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reback.docs',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
time: addOrSubtractDaysFromDate(0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Project Status',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
name: 'UI/UX Figma Design.fig',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variant: 'success',
|
||||||
|
type: 'design',
|
||||||
|
status: 'completed',
|
||||||
|
time: addOrSubtractDaysFromDate(1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Reback Application UI v2.0.0',
|
||||||
|
variant: 'primary',
|
||||||
|
content:
|
||||||
|
'Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order E-commerce & Marketing pages.',
|
||||||
|
files: [{ name: 'Backup.zip' }],
|
||||||
|
status: 'latest',
|
||||||
|
time: addOrSubtractDaysFromDate(3),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Alex Smith Attached Photos',
|
||||||
|
icon: avatar7,
|
||||||
|
time: addOrSubtractDaysFromDate(4),
|
||||||
|
files: [{ preview: smImg6 }, { preview: smImg3 }, { preview: smImg4 }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Rebecca J. added a new team member',
|
||||||
|
icon: avatar6,
|
||||||
|
time: addOrSubtractDaysFromDate(4),
|
||||||
|
content: 'Added a new member to Front Dashboard',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Achievements',
|
||||||
|
variant: 'warning',
|
||||||
|
type: 'achievement',
|
||||||
|
time: addOrSubtractDaysFromDate(5),
|
||||||
|
content: 'Earned a "Best Product Award"',
|
||||||
|
},
|
||||||
|
]
|
||||||
380
apiferia/src/app/layouts/topbar/topbar.component.html
Normal file
380
apiferia/src/app/layouts/topbar/topbar.component.html
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
<header class="topbar">
|
||||||
|
<div class="container-xxl">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<div class="topbar-item">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button-toggle-menu"
|
||||||
|
(click)="toggleMobileMenu()"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:menu-burger-horizontal"
|
||||||
|
class="fs-22"
|
||||||
|
></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- App Search-->
|
||||||
|
<form class="app-search d-none d-md-block me-auto">
|
||||||
|
<div class="position-relative">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Search..."
|
||||||
|
autocomplete="off"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:search-duotone"
|
||||||
|
class="search-widget-icon"
|
||||||
|
></iconify-icon>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center gap-1">
|
||||||
|
<div class="topbar-item">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="topbar-button"
|
||||||
|
id="light-dark-mode"
|
||||||
|
(click)="changeTheme()"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:mode-dark-duotone"
|
||||||
|
class="fs-24 align-middle"
|
||||||
|
></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ngbDropdown class="dropdown topbar-item d-none d-lg-flex">
|
||||||
|
<button
|
||||||
|
ngbDropdownToggle
|
||||||
|
type="button"
|
||||||
|
class="topbar-button arrow-none"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:apps"
|
||||||
|
class="fs-24 align-middle"
|
||||||
|
></iconify-icon>
|
||||||
|
</button>
|
||||||
|
<div ngbDropdownMenu class="dropdown-menu dropdown-menu-end p-0">
|
||||||
|
<div class="p-1">
|
||||||
|
@for (item of appsList; track $index) {
|
||||||
|
<a class="dropdown-item py-2" href="javascript:void(0);">
|
||||||
|
<img [src]="item.image" class="avatar-xs" alt="Github" />
|
||||||
|
<span class="ms-2"
|
||||||
|
>{{ item.name }}:
|
||||||
|
<span class="fw-medium">{{ item.handle }}</span></span
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ngbDropdown class="dropdown topbar-item">
|
||||||
|
<button
|
||||||
|
ngbDropdownToggle
|
||||||
|
type="button"
|
||||||
|
class="topbar-button position-relative arrow-none"
|
||||||
|
id="page-header-notifications-dropdown"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:notification-duotone"
|
||||||
|
class="fs-24 align-middle"
|
||||||
|
></iconify-icon>
|
||||||
|
<span
|
||||||
|
class="position-absolute topbar-badge fs-10 translate-middle badge bg-danger rounded-pill"
|
||||||
|
>{{ notificationList.length
|
||||||
|
}}<span class="visually-hidden">unread messages</span></span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
ngbDropdownMenu
|
||||||
|
class="dropdown-menu py-0 dropdown-lg dropdown-menu-end"
|
||||||
|
aria-labelledby="page-header-notifications-dropdown"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="p-3 border-top-0 border-start-0 border-end-0 border-dashed border"
|
||||||
|
>
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col">
|
||||||
|
<h6 class="m-0 fs-16 fw-semibold">Notifications</h6>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<a
|
||||||
|
href="javascript: void(0);"
|
||||||
|
class="text-dark text-decoration-underline"
|
||||||
|
>
|
||||||
|
<small>Clear All</small>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ngx-simplebar style="max-height: 280px">
|
||||||
|
@for (notify of notificationList; track $index) {
|
||||||
|
<a
|
||||||
|
href="javascript:void(0);"
|
||||||
|
class="dropdown-item py-3 border-bottom text-wrap"
|
||||||
|
>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
@if (notify.icon) {
|
||||||
|
<img
|
||||||
|
[src]="notify.icon"
|
||||||
|
class="img-fluid me-2 avatar-sm rounded-circle"
|
||||||
|
alt="avatar-1"
|
||||||
|
/>
|
||||||
|
} @else {
|
||||||
|
<div class="avatar-sm me-2">
|
||||||
|
<span
|
||||||
|
class="avatar-title bg-soft-info text-info fs-20 rounded-circle"
|
||||||
|
>
|
||||||
|
{{ notify.from.charAt(0) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<p class="mb-0">
|
||||||
|
<span class="fw-medium">{{ notify.from }} </span>
|
||||||
|
</p>
|
||||||
|
<p class="mb-0 text-wrap">{{ notify.content }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</ngx-simplebar>
|
||||||
|
<div class="text-center py-3">
|
||||||
|
<a href="javascript:void(0);" class="btn btn-primary btn-sm"
|
||||||
|
>View All Notification <i class="bx bx-right-arrow-alt ms-1"></i
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="topbar-item">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="settingMenu()"
|
||||||
|
class="topbar-button"
|
||||||
|
id="theme-settings-btn"
|
||||||
|
data-bs-target="#theme-settings-offcanvas"
|
||||||
|
aria-controls="theme-settings-offcanvas"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:settings-duotone"
|
||||||
|
class="fs-24 align-middle"
|
||||||
|
></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="topbar-item d-none d-md-flex">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="topbar-button"
|
||||||
|
id="theme-settings-btn"
|
||||||
|
(click)="open(content)"
|
||||||
|
aria-controls="theme-settings-offcanvas"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:history-duotone"
|
||||||
|
class="fs-24 align-middle"
|
||||||
|
></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ngbDropdown class="dropdown topbar-item">
|
||||||
|
<a
|
||||||
|
type="button"
|
||||||
|
ngbDropdownToggle
|
||||||
|
class="topbar-button arrow-none"
|
||||||
|
id="page-header-user-dropdown"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<span class="d-flex align-items-center">
|
||||||
|
<img
|
||||||
|
class="rounded-circle"
|
||||||
|
width="32"
|
||||||
|
src="assets/images/users/avatar-1.jpg"
|
||||||
|
alt="avatar-3"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div ngbDropdownMenu class="dropdown-menu dropdown-menu-end">
|
||||||
|
<!-- item-->
|
||||||
|
<h6 class="dropdown-header">Welcome Gaston!</h6>
|
||||||
|
<a class="dropdown-item" routerLink="/pages/profile">
|
||||||
|
<i
|
||||||
|
class="bx bx-user-circle text-muted fs-18 align-middle me-1"
|
||||||
|
></i
|
||||||
|
><span class="align-middle">Profile</span>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" routerLink="/apps/chat">
|
||||||
|
<i
|
||||||
|
class="bx bx-message-dots text-muted fs-18 align-middle me-1"
|
||||||
|
></i
|
||||||
|
><span class="align-middle">Messages</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="dropdown-item" routerLink="/pages/pricing">
|
||||||
|
<i class="bx bx-wallet text-muted fs-18 align-middle me-1"></i
|
||||||
|
><span class="align-middle">Pricing</span>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" routerLink="/pages/faqs">
|
||||||
|
<i
|
||||||
|
class="bx bx-help-circle text-muted fs-18 align-middle me-1"
|
||||||
|
></i
|
||||||
|
><span class="align-middle">Help</span>
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" routerLink="/auth/lock-screen">
|
||||||
|
<i class="bx bx-lock text-muted fs-18 align-middle me-1"></i
|
||||||
|
><span class="align-middle">Lock screen</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="dropdown-divider my-1"></div>
|
||||||
|
|
||||||
|
<a class="dropdown-item text-danger" (click)="logout()">
|
||||||
|
<i class="bx bx-log-out fs-18 align-middle me-1"></i
|
||||||
|
><span class="align-middle">Logout</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<ng-template #content let-offcanvas>
|
||||||
|
<div class="d-flex align-items-center bg-primary p-3 offcanvas-header">
|
||||||
|
<h5 class="text-white m-0 fw-semibold">Activity Stream</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-close btn-close-white ms-auto"
|
||||||
|
(click)="offcanvas.close()"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="offcanvas-body p-0">
|
||||||
|
<ngx-simplebar data-simplebar class="h-100 p-4">
|
||||||
|
<div class="position-relative ms-2">
|
||||||
|
<span
|
||||||
|
class="position-absolute start-0 top-0 border border-dashed h-100"
|
||||||
|
></span>
|
||||||
|
@for (data of activityList; track $index) {
|
||||||
|
<div class="position-relative ps-4">
|
||||||
|
<div class="mb-4">
|
||||||
|
<span
|
||||||
|
class="position-absolute start-0 translate-middle-x d-inline-flex align-items-center justify-content-center rounded-circle text-light fs-20 bg-{{
|
||||||
|
data.variant
|
||||||
|
}} "
|
||||||
|
[ngClass]="{ 'avatar-sm': !data.icon }"
|
||||||
|
>
|
||||||
|
@if (data.icon) {
|
||||||
|
<img
|
||||||
|
[src]="data.icon"
|
||||||
|
alt="avatar-5"
|
||||||
|
class="avatar-sm rounded-circle"
|
||||||
|
/>
|
||||||
|
} @else if (data.type) {
|
||||||
|
<iconify-icon
|
||||||
|
[icon]="getActivityIcon(data.type)"
|
||||||
|
></iconify-icon>
|
||||||
|
} @else {
|
||||||
|
{{ data.title.charAt(0).toUpperCase() }}
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
<div class="ms-2">
|
||||||
|
<h5 class="mb-1 text-dark fw-semibold fs-15 lh-base">
|
||||||
|
{{ data.title }}
|
||||||
|
@if (data.status) {
|
||||||
|
<span
|
||||||
|
class="badge bg-{{ data.variant }}-subtle text-{{
|
||||||
|
data.variant
|
||||||
|
}} px-2 py-1 ms-1"
|
||||||
|
>{{ data.status }}</span
|
||||||
|
>
|
||||||
|
}
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
@if (data.files && data.type) {
|
||||||
|
<p class="d-flex align-items-center">
|
||||||
|
Add {{ data.files.length }} files to
|
||||||
|
<span class="d-flex align-items-center text-primary ms-1">
|
||||||
|
<iconify-icon icon="iconamoon:file-light"></iconify-icon>
|
||||||
|
{{ data.type }}</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
@if (data.content) {
|
||||||
|
<p>{{ data.content }}</p>
|
||||||
|
}
|
||||||
|
@if (data.files) {
|
||||||
|
<div class="bg-light bg-opacity-50 rounded-2 p-2">
|
||||||
|
<div class="row">
|
||||||
|
@for (file of data.files; track $index) {
|
||||||
|
@if (file.preview) {
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<a href="javascript:void(0);">
|
||||||
|
<img
|
||||||
|
[src]="file.preview"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/></a>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<div
|
||||||
|
class="border-end border-light"
|
||||||
|
[ngClass]="{ 'col-lg-6': data.files.length > 1 }"
|
||||||
|
>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<i
|
||||||
|
class="bx fs-20"
|
||||||
|
[class]="getFileExtensionIcon(file.name)"
|
||||||
|
></i>
|
||||||
|
<a
|
||||||
|
href="javascript:void(0);"
|
||||||
|
class="text-dark fw-medium"
|
||||||
|
>{{ file.name }}</a
|
||||||
|
>
|
||||||
|
<div class="ms-auto">
|
||||||
|
<a
|
||||||
|
href="javascript:void(0);"
|
||||||
|
class="fw-medium text-primary fs-18"
|
||||||
|
ngbTooltip="Download"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<iconify-icon
|
||||||
|
icon="iconamoon:cloud-download-duotone"
|
||||||
|
></iconify-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<h6 class="mt-2 text-muted">{{ data.time.toDateString() }}</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<a href="javascript:void(0);" class="btn btn-outline-dark w-100"
|
||||||
|
>View All</a
|
||||||
|
>
|
||||||
|
</ngx-simplebar>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
0
apiferia/src/app/layouts/topbar/topbar.component.scss
vendored
Normal file
0
apiferia/src/app/layouts/topbar/topbar.component.scss
vendored
Normal file
22
apiferia/src/app/layouts/topbar/topbar.component.spec.ts
Normal file
22
apiferia/src/app/layouts/topbar/topbar.component.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { TopbarComponent } from './topbar.component'
|
||||||
|
|
||||||
|
describe('TopbarComponent', () => {
|
||||||
|
let component: TopbarComponent
|
||||||
|
let fixture: ComponentFixture<TopbarComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [TopbarComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(TopbarComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
116
apiferia/src/app/layouts/topbar/topbar.component.ts
Normal file
116
apiferia/src/app/layouts/topbar/topbar.component.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { changetheme } from '@/app/store/layout/layout-action'
|
||||||
|
import { CommonModule, DOCUMENT } from '@angular/common'
|
||||||
|
import {
|
||||||
|
CUSTOM_ELEMENTS_SCHEMA,
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Inject,
|
||||||
|
Output,
|
||||||
|
inject,
|
||||||
|
type TemplateRef,
|
||||||
|
} from '@angular/core'
|
||||||
|
import {
|
||||||
|
NgbDropdownModule,
|
||||||
|
NgbOffcanvas,
|
||||||
|
NgbOffcanvasModule,
|
||||||
|
NgbTooltipModule,
|
||||||
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { Store } from '@ngrx/store'
|
||||||
|
import { SimplebarAngularModule } from 'simplebar-angular'
|
||||||
|
import { getLayoutColor } from '../../store/layout/layout-selector'
|
||||||
|
import { logout } from '@/app/store/authentication/authentication.actions'
|
||||||
|
import { Router, RouterLink } from '@angular/router'
|
||||||
|
import { activityStreamData, appsData, notificationsData } from './data'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-topbar',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
NgbDropdownModule,
|
||||||
|
SimplebarAngularModule,
|
||||||
|
NgbOffcanvasModule,
|
||||||
|
RouterLink,
|
||||||
|
CommonModule,
|
||||||
|
NgbTooltipModule,
|
||||||
|
],
|
||||||
|
templateUrl: './topbar.component.html',
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
})
|
||||||
|
export class TopbarComponent {
|
||||||
|
element: any
|
||||||
|
|
||||||
|
router = inject(Router)
|
||||||
|
store = inject(Store)
|
||||||
|
offcanvasService = inject(NgbOffcanvas)
|
||||||
|
|
||||||
|
notificationList = notificationsData
|
||||||
|
appsList = appsData
|
||||||
|
activityList = activityStreamData
|
||||||
|
|
||||||
|
constructor(@Inject(DOCUMENT) private document: any) {}
|
||||||
|
@Output() settingsButtonClicked = new EventEmitter()
|
||||||
|
@Output() mobileMenuButtonClicked = new EventEmitter()
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.element = document.documentElement
|
||||||
|
}
|
||||||
|
|
||||||
|
settingMenu() {
|
||||||
|
this.settingsButtonClicked.emit()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the menu bar when having mobile screen
|
||||||
|
*/
|
||||||
|
toggleMobileMenu() {
|
||||||
|
// document.getElementById('topnav-hamburger-icon')?.classList.toggle('open');
|
||||||
|
this.mobileMenuButtonClicked.emit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change Theme
|
||||||
|
changeTheme() {
|
||||||
|
const color = document.documentElement.getAttribute('data-bs-theme')
|
||||||
|
console.log(color)
|
||||||
|
if (color == 'light') {
|
||||||
|
this.store.dispatch(changetheme({ color: 'dark' }))
|
||||||
|
} else {
|
||||||
|
this.store.dispatch(changetheme({ color: 'light' }))
|
||||||
|
}
|
||||||
|
this.store.select(getLayoutColor).subscribe((color) => {
|
||||||
|
document.documentElement.setAttribute('data-bs-theme', color)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
open(content: TemplateRef<any>) {
|
||||||
|
this.offcanvasService.open(content, {
|
||||||
|
position: 'end',
|
||||||
|
panelClass: 'border-0 width-auto',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.store.dispatch(logout())
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileExtensionIcon(file: any) {
|
||||||
|
const dotIndex = file.lastIndexOf('.')
|
||||||
|
const extension = file.slice(dotIndex + 1)
|
||||||
|
if (extension == 'docs') {
|
||||||
|
return 'bxs-file-doc'
|
||||||
|
} else if (extension == 'zip') {
|
||||||
|
return 'bxs-file-archive'
|
||||||
|
} else {
|
||||||
|
return 'bxl-figma '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getActivityIcon(type: string) {
|
||||||
|
if (type == 'task') {
|
||||||
|
return 'iconamoon:folder-check-duotone'
|
||||||
|
} else if (type == 'design') {
|
||||||
|
return 'iconamoon:check-circle-1-duotone'
|
||||||
|
} else {
|
||||||
|
return 'iconamoon:certificate-badge-duotone'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
apiferia/src/app/layouts/vertical/vertical.component.spec.ts
Normal file
22
apiferia/src/app/layouts/vertical/vertical.component.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { VerticalComponent } from './vertical.component'
|
||||||
|
|
||||||
|
describe('VerticalComponent', () => {
|
||||||
|
let component: VerticalComponent
|
||||||
|
let fixture: ComponentFixture<VerticalComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [VerticalComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(VerticalComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
107
apiferia/src/app/layouts/vertical/vertical.component.ts
Normal file
107
apiferia/src/app/layouts/vertical/vertical.component.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { Component, HostListener, inject, Renderer2 } from '@angular/core'
|
||||||
|
import { SidebarComponent } from '../sidebar/sidebar.component'
|
||||||
|
import { TopbarComponent } from '../topbar/topbar.component'
|
||||||
|
import { FooterComponent } from '../footer/footer.component'
|
||||||
|
import { RouterModule } from '@angular/router'
|
||||||
|
import { RightSidebarComponent } from '../right-sidebar/right-sidebar.component'
|
||||||
|
import { NgbActiveOffcanvas, NgbOffcanvas } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { Store } from '@ngrx/store'
|
||||||
|
import { changesidebarsize } from '@store/layout/layout-action'
|
||||||
|
import { getSidebarsize } from '@store/layout/layout-selector'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-vertical',
|
||||||
|
standalone: true,
|
||||||
|
imports: [SidebarComponent, TopbarComponent, FooterComponent, RouterModule],
|
||||||
|
template: `
|
||||||
|
<div class="wrapper">
|
||||||
|
<app-topbar
|
||||||
|
(settingsButtonClicked)="onSettingsButtonClicked()"
|
||||||
|
(mobileMenuButtonClicked)="onToggleMobileMenu()"
|
||||||
|
></app-topbar>
|
||||||
|
<app-sidebar></app-sidebar>
|
||||||
|
|
||||||
|
<div class="page-content">
|
||||||
|
<!-- Start Content-->
|
||||||
|
<div class="container-xxl">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<app-footer></app-footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styles: ``,
|
||||||
|
providers: [NgbActiveOffcanvas],
|
||||||
|
})
|
||||||
|
export class VerticalComponent {
|
||||||
|
private offcanvasService = inject(NgbOffcanvas)
|
||||||
|
private store = inject(Store)
|
||||||
|
private renderer = inject(Renderer2)
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.onResize()
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('window:resize', ['$event'])
|
||||||
|
onResize() {
|
||||||
|
if (document.documentElement.clientWidth <= 1140) {
|
||||||
|
this.store.dispatch(changesidebarsize({ size: 'hidden' }))
|
||||||
|
} else {
|
||||||
|
this.store.dispatch(changesidebarsize({ size: 'default' }))
|
||||||
|
document.documentElement.classList.remove('sidebar-enable')
|
||||||
|
const backdrop = document.querySelector('.offcanvas-backdrop')
|
||||||
|
if (backdrop) this.renderer.removeChild(document.body, backdrop)
|
||||||
|
}
|
||||||
|
this.store.select(getSidebarsize).subscribe((size: string) => {
|
||||||
|
this.renderer.setAttribute(
|
||||||
|
document.documentElement,
|
||||||
|
'data-sidenav-size',
|
||||||
|
size
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onSettingsButtonClicked() {
|
||||||
|
this.offcanvasService.open(RightSidebarComponent, { position: 'end' })
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggleMobileMenu() {
|
||||||
|
this.store.select(getSidebarsize).subscribe((size: any) => {
|
||||||
|
document.documentElement.setAttribute('data-menu-size', size)
|
||||||
|
})
|
||||||
|
|
||||||
|
const size = document.documentElement.getAttribute('data-menu-size')
|
||||||
|
|
||||||
|
document.documentElement.classList.toggle('sidebar-enable')
|
||||||
|
if (size != 'hidden') {
|
||||||
|
if (document.documentElement.classList.contains('sidebar-enable')) {
|
||||||
|
this.store.dispatch(changesidebarsize({ size: 'condensed' }))
|
||||||
|
} else {
|
||||||
|
this.store.dispatch(changesidebarsize({ size: 'default' }))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.showBackdrop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showBackdrop() {
|
||||||
|
const backdrop = this.renderer.createElement('div')
|
||||||
|
this.renderer.addClass(backdrop, 'offcanvas-backdrop')
|
||||||
|
this.renderer.addClass(backdrop, 'fade')
|
||||||
|
this.renderer.addClass(backdrop, 'show')
|
||||||
|
this.renderer.appendChild(document.body, backdrop)
|
||||||
|
this.renderer.setStyle(document.body, 'overflow', 'hidden')
|
||||||
|
|
||||||
|
if (window.innerWidth > 1040) {
|
||||||
|
this.renderer.setStyle(document.body, 'paddingRight', '15px')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderer.listen(backdrop, 'click', () => {
|
||||||
|
document.documentElement.classList.remove('sidebar-enable')
|
||||||
|
this.renderer.removeChild(document.body, backdrop)
|
||||||
|
this.renderer.setStyle(document.body, 'overflow', null)
|
||||||
|
this.renderer.setStyle(document.body, 'paddingRight', null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
17
apiferia/src/app/store/authentication/auth.model.ts
Normal file
17
apiferia/src/app/store/authentication/auth.model.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export class User {
|
||||||
|
id?: string
|
||||||
|
email?: string
|
||||||
|
role?: 'admin' | 'dealer' | 'cliente' | 'aseguradora' | 'financiera' | 'tasador' | 'gps'
|
||||||
|
firstName?: string
|
||||||
|
lastName?: string
|
||||||
|
phone?: string
|
||||||
|
isActive?: boolean
|
||||||
|
createdAt?: Date
|
||||||
|
updatedAt?: Date
|
||||||
|
fullName?: string
|
||||||
|
token?: string
|
||||||
|
// Compatibilidad con el template original
|
||||||
|
username?: string
|
||||||
|
password?: string
|
||||||
|
name?: string
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { createAction, props } from '@ngrx/store'
|
||||||
|
import type { User } from './auth.model'
|
||||||
|
|
||||||
|
// login action
|
||||||
|
export const login = createAction(
|
||||||
|
'[Authentication] Login',
|
||||||
|
props<{ email: string; password: string }>()
|
||||||
|
)
|
||||||
|
export const loginSuccess = createAction(
|
||||||
|
'[Authentication] Login Success',
|
||||||
|
props<{ user: User }>()
|
||||||
|
)
|
||||||
|
export const loginFailure = createAction(
|
||||||
|
'[Authentication] Login Failure',
|
||||||
|
props<{ error: string }>()
|
||||||
|
)
|
||||||
|
|
||||||
|
// logout action
|
||||||
|
export const logout = createAction('[Authentication] Logout')
|
||||||
|
|
||||||
|
export const logoutSuccess = createAction('[Auth] Logout Success')
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { Inject, Injectable } from '@angular/core'
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
|
import { Actions, createEffect, ofType } from '@ngrx/effects'
|
||||||
|
import { of } from 'rxjs'
|
||||||
|
import { catchError, exhaustMap, map } from 'rxjs/operators'
|
||||||
|
import {
|
||||||
|
login,
|
||||||
|
loginFailure,
|
||||||
|
loginSuccess,
|
||||||
|
logout,
|
||||||
|
logoutSuccess,
|
||||||
|
} from './authentication.actions'
|
||||||
|
import { AuthenticationService } from '@/app/core/services/auth.service'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthenticationEffects {
|
||||||
|
login$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(login),
|
||||||
|
exhaustMap(({ email, password }) => {
|
||||||
|
return this.AuthenticationService.login(email, password).pipe(
|
||||||
|
map((user) => {
|
||||||
|
if (user) {
|
||||||
|
const returnUrl =
|
||||||
|
this.route.snapshot.queryParams['returnUrl'] || '/'
|
||||||
|
this.router.navigateByUrl(returnUrl)
|
||||||
|
}
|
||||||
|
return loginSuccess({ user })
|
||||||
|
}),
|
||||||
|
catchError((error) => of(loginFailure({ error })))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
logout$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(logout),
|
||||||
|
exhaustMap(() => {
|
||||||
|
this.AuthenticationService.logout()
|
||||||
|
this.router.navigate(['/auth/sign-in'])
|
||||||
|
return of(logoutSuccess())
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(Actions) private actions$: Actions,
|
||||||
|
private AuthenticationService: AuthenticationService,
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { createReducer, on } from '@ngrx/store'
|
||||||
|
import {
|
||||||
|
login,
|
||||||
|
loginFailure,
|
||||||
|
loginSuccess,
|
||||||
|
logout,
|
||||||
|
} from './authentication.actions'
|
||||||
|
import type { User } from './auth.model'
|
||||||
|
|
||||||
|
export type AuthenticationState = {
|
||||||
|
isLoggedIn: boolean
|
||||||
|
user: User | null
|
||||||
|
error: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: AuthenticationState = {
|
||||||
|
isLoggedIn: false,
|
||||||
|
user: null,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authenticationReducer = createReducer(
|
||||||
|
initialState,
|
||||||
|
on(login, (state) => ({ ...state, error: null })),
|
||||||
|
on(loginSuccess, (state, { user }) => ({
|
||||||
|
...state,
|
||||||
|
isLoggedIn: true,
|
||||||
|
user,
|
||||||
|
error: null,
|
||||||
|
})),
|
||||||
|
on(loginFailure, (state, { error }) => ({ ...state, error })),
|
||||||
|
|
||||||
|
on(logout, (state) => ({ ...state, user: null }))
|
||||||
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFeatureSelector, createSelector } from '@ngrx/store'
|
||||||
|
import { AuthenticationState } from './authentication.reducer'
|
||||||
|
|
||||||
|
export const getUserState =
|
||||||
|
createFeatureSelector<AuthenticationState>('authentication')
|
||||||
|
|
||||||
|
export const getUser = createSelector(
|
||||||
|
getUserState,
|
||||||
|
(state: AuthenticationState) => state.user
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getToken = createSelector(
|
||||||
|
getUserState,
|
||||||
|
(state: AuthenticationState) => state.user?.token
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getisLoggedIn = createSelector(
|
||||||
|
getUserState,
|
||||||
|
(state: AuthenticationState) => state.isLoggedIn
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getError = createSelector(
|
||||||
|
getUserState,
|
||||||
|
(state: AuthenticationState) => state.error
|
||||||
|
)
|
||||||
67
apiferia/src/app/store/calendar/calendar.actions.ts
Normal file
67
apiferia/src/app/store/calendar/calendar.actions.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { createAction, props } from '@ngrx/store'
|
||||||
|
import { EventInput } from '@fullcalendar/core'
|
||||||
|
|
||||||
|
export const fetchCalendar = createAction('[Calendar] Fetch Calendar')
|
||||||
|
export const fetchCalendarSuccess = createAction(
|
||||||
|
'[Calendar] Fetch Calendar Success',
|
||||||
|
props<{ events: EventInput[] }>()
|
||||||
|
)
|
||||||
|
export const fetchCalendarFailure = createAction(
|
||||||
|
'[Data] Fetch Calendar Failure',
|
||||||
|
props<{ error: string }>()
|
||||||
|
)
|
||||||
|
|
||||||
|
export const addEvent = createAction(
|
||||||
|
'[Calendar] Add Event',
|
||||||
|
props<{ event: EventInput }>()
|
||||||
|
)
|
||||||
|
export const addEventSuccess = createAction(
|
||||||
|
'[Calendar] Add Event Success',
|
||||||
|
props<{ events: EventInput }>()
|
||||||
|
)
|
||||||
|
export const addEventFailure = createAction(
|
||||||
|
'[Calendar] Add Event Failure',
|
||||||
|
props<{ error: string }>()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add Data
|
||||||
|
export const addCalendar = createAction(
|
||||||
|
'[Data] Add Calendar',
|
||||||
|
props<{ events: EventInput }>()
|
||||||
|
)
|
||||||
|
export const addCalendarSuccess = createAction(
|
||||||
|
'[Data] Add Calendar Success',
|
||||||
|
props<{ events: EventInput }>()
|
||||||
|
)
|
||||||
|
export const addCalendarFailure = createAction(
|
||||||
|
'[Data] Add Calendar Failure',
|
||||||
|
props<{ error: string }>()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Update Data
|
||||||
|
export const updateCalendar = createAction(
|
||||||
|
'[Data] Update calendar event',
|
||||||
|
props<{ events: EventInput }>()
|
||||||
|
)
|
||||||
|
export const updateCalendarSuccess = createAction(
|
||||||
|
'[Data] Update calendar event Success',
|
||||||
|
props<{ events: EventInput }>()
|
||||||
|
)
|
||||||
|
export const updateCalendarFailure = createAction(
|
||||||
|
'[Data] Update calendar event Failure',
|
||||||
|
props<{ error: string }>()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Delete Data
|
||||||
|
export const deleteCalendar = createAction(
|
||||||
|
'[Data] Delete Calendar',
|
||||||
|
props<{ id: string }>()
|
||||||
|
)
|
||||||
|
export const deleteCalendarSuccess = createAction(
|
||||||
|
'[Data] Delete Calendar Success',
|
||||||
|
props<{ id: string }>()
|
||||||
|
)
|
||||||
|
export const deleteCalendarFailure = createAction(
|
||||||
|
'[Data] Delete Calendar Failure',
|
||||||
|
props<{ error: string }>()
|
||||||
|
)
|
||||||
75
apiferia/src/app/store/calendar/calendar.effects.ts
Normal file
75
apiferia/src/app/store/calendar/calendar.effects.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { inject, Injectable } from '@angular/core'
|
||||||
|
import { of } from 'rxjs'
|
||||||
|
import { Actions, createEffect, ofType } from '@ngrx/effects'
|
||||||
|
import { catchError, map, mergeMap, tap } from 'rxjs/operators'
|
||||||
|
|
||||||
|
// Action
|
||||||
|
import {
|
||||||
|
addCalendar,
|
||||||
|
addCalendarFailure,
|
||||||
|
addCalendarSuccess,
|
||||||
|
deleteCalendar,
|
||||||
|
deleteCalendarFailure,
|
||||||
|
deleteCalendarSuccess,
|
||||||
|
fetchCalendar,
|
||||||
|
fetchCalendarFailure,
|
||||||
|
fetchCalendarSuccess,
|
||||||
|
updateCalendar,
|
||||||
|
updateCalendarFailure,
|
||||||
|
updateCalendarSuccess,
|
||||||
|
} from './calendar.actions'
|
||||||
|
import { CrudService } from '@/app/core/services/crud.service'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CalendarEffects {
|
||||||
|
private CrudService = inject(CrudService)
|
||||||
|
private actions$ = inject(Actions)
|
||||||
|
|
||||||
|
fetchEvents$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(fetchCalendar),
|
||||||
|
mergeMap(() =>
|
||||||
|
this.CrudService.fetchCalendarEvents().pipe(
|
||||||
|
map((events) => fetchCalendarSuccess({ events })),
|
||||||
|
catchError((error) => of(fetchCalendarFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
addEvents$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(addCalendar),
|
||||||
|
mergeMap(({ events }) =>
|
||||||
|
this.CrudService.addCalendarEvents(events).pipe(
|
||||||
|
map(() => addCalendarSuccess({ events })),
|
||||||
|
catchError((error) => of(addCalendarFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
updateEvents$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(updateCalendar),
|
||||||
|
mergeMap(({ events }) =>
|
||||||
|
this.CrudService.updateCalendarEvents(events).pipe(
|
||||||
|
map(() => updateCalendarSuccess({ events })),
|
||||||
|
catchError((error) => of(updateCalendarFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
deleteEvents$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(deleteCalendar),
|
||||||
|
mergeMap(({ id }) =>
|
||||||
|
this.CrudService.deleteCalendarEvents(id).pipe(
|
||||||
|
map(() => deleteCalendarSuccess({ id })),
|
||||||
|
catchError((error) => of(deleteCalendarFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
57
apiferia/src/app/store/calendar/calendar.reducer.ts
Normal file
57
apiferia/src/app/store/calendar/calendar.reducer.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { createReducer, on, Action } from '@ngrx/store'
|
||||||
|
import { EventInput } from '@fullcalendar/core'
|
||||||
|
import {
|
||||||
|
addEvent,
|
||||||
|
fetchCalendar,
|
||||||
|
fetchCalendarSuccess,
|
||||||
|
updateCalendarSuccess,
|
||||||
|
deleteCalendar,
|
||||||
|
addEventSuccess,
|
||||||
|
deleteCalendarSuccess,
|
||||||
|
addCalendarSuccess,
|
||||||
|
} from './calendar.actions'
|
||||||
|
|
||||||
|
export type CalendarState = {
|
||||||
|
events: EventInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialState: CalendarState = {
|
||||||
|
events: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calendarReducer = createReducer(
|
||||||
|
initialState,
|
||||||
|
on(fetchCalendar, (state) => {
|
||||||
|
return { ...state }
|
||||||
|
}),
|
||||||
|
on(fetchCalendarSuccess, (state, { events }) => {
|
||||||
|
return { ...state, events }
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(addCalendarSuccess, (state, { events }) => {
|
||||||
|
return { ...state, events: [...state.events, events], error: null }
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(updateCalendarSuccess, (state, { events }) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
events: state.events.map((event) =>
|
||||||
|
event.id == events.id ? events : event
|
||||||
|
),
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(deleteCalendarSuccess, (state, { id }) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
events: state.events.filter((event) => event.id !== id),
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Selector
|
||||||
|
export function reducer(state: CalendarState | undefined, action: Action) {
|
||||||
|
return calendarReducer(state, action)
|
||||||
|
}
|
||||||
9
apiferia/src/app/store/calendar/calendar.selectors.ts
Normal file
9
apiferia/src/app/store/calendar/calendar.selectors.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { createFeatureSelector, createSelector } from '@ngrx/store'
|
||||||
|
import { CalendarState } from './calendar.reducer'
|
||||||
|
|
||||||
|
export const selectDataState = createFeatureSelector<CalendarState>('Calendar')
|
||||||
|
|
||||||
|
export const getEvents = createSelector(
|
||||||
|
selectDataState,
|
||||||
|
(state: CalendarState) => state.events
|
||||||
|
)
|
||||||
100
apiferia/src/app/store/calendar/data.ts
Normal file
100
apiferia/src/app/store/calendar/data.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { EventInput } from '@fullcalendar/core'
|
||||||
|
|
||||||
|
export type externalModel = {
|
||||||
|
id: number
|
||||||
|
textClass: string
|
||||||
|
className: string
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultEvents: EventInput[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
title: 'Interview - Backend Engineer',
|
||||||
|
start: new Date(),
|
||||||
|
className: 'bg-primary',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
title: 'Meeting with CT Team',
|
||||||
|
start: new Date(Date.now() + 13000000),
|
||||||
|
className: 'bg-warning',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
title: 'Meeting with Mr. Reback',
|
||||||
|
start: new Date(Date.now() + 308000000),
|
||||||
|
end: new Date(Date.now() + 338000000),
|
||||||
|
className: 'bg-info',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
title: 'Interview - Frontend Engineer',
|
||||||
|
start: new Date(Date.now() + 60570000),
|
||||||
|
end: new Date(Date.now() + 153000000),
|
||||||
|
className: 'bg-secondary',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
title: 'Phone Screen - Frontend Engineer',
|
||||||
|
start: new Date(Date.now() + 168000000),
|
||||||
|
className: 'bg-success',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
title: 'Buy Design Assets',
|
||||||
|
start: new Date(Date.now() + 330000000),
|
||||||
|
end: new Date(Date.now() + 330800000),
|
||||||
|
className: 'bg-primary',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
title: 'Setup Github Repository',
|
||||||
|
start: new Date(Date.now() + 1008000000),
|
||||||
|
end: new Date(Date.now() + 1108000000),
|
||||||
|
className: 'bg-danger',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
title: 'Meeting with Mr. Shreyu',
|
||||||
|
start: new Date(Date.now() + 2508000000),
|
||||||
|
end: new Date(Date.now() + 2508000000),
|
||||||
|
className: 'bg-dark',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// external events
|
||||||
|
const externalEvents: externalModel[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
textClass: 'text-primary',
|
||||||
|
className: 'primary',
|
||||||
|
title: 'Team Building Retreat Meeting ',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
textClass: 'text-info',
|
||||||
|
className: 'info',
|
||||||
|
title: 'Product Launch Strategy Meeting',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
textClass: 'text-success',
|
||||||
|
className: 'success',
|
||||||
|
title: 'Monthly Sales Review',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
textClass: 'text-danger',
|
||||||
|
className: 'danger',
|
||||||
|
title: 'Team Lunch Celebration',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
textClass: 'text-warning',
|
||||||
|
className: 'warning',
|
||||||
|
title: 'Marketing Campaign Kickoff',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export { defaultEvents, externalEvents }
|
||||||
22
apiferia/src/app/store/index.ts
Normal file
22
apiferia/src/app/store/index.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ActionReducerMap } from '@ngrx/store'
|
||||||
|
import { LayoutState, layoutReducer } from './layout/layout-reducers'
|
||||||
|
import {
|
||||||
|
calendarReducer,
|
||||||
|
type CalendarState,
|
||||||
|
} from './calendar/calendar.reducer'
|
||||||
|
import {
|
||||||
|
authenticationReducer,
|
||||||
|
type AuthenticationState,
|
||||||
|
} from './authentication/authentication.reducer'
|
||||||
|
|
||||||
|
export interface RootReducerState {
|
||||||
|
authentication: AuthenticationState
|
||||||
|
layout: LayoutState
|
||||||
|
Calendar: CalendarState
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rootReducer: ActionReducerMap<RootReducerState> = {
|
||||||
|
authentication: authenticationReducer,
|
||||||
|
layout: layoutReducer,
|
||||||
|
Calendar: calendarReducer,
|
||||||
|
}
|
||||||
19
apiferia/src/app/store/layout/layout-action.ts
Normal file
19
apiferia/src/app/store/layout/layout-action.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { createAction, props } from '@ngrx/store'
|
||||||
|
|
||||||
|
export const changetheme = createAction(
|
||||||
|
'[Layout] Set Color',
|
||||||
|
props<{ color: string }>()
|
||||||
|
)
|
||||||
|
export const changetopbarcolor = createAction(
|
||||||
|
'[Layout] Set Topbar',
|
||||||
|
props<{ topbar: string }>()
|
||||||
|
)
|
||||||
|
export const changemenucolor = createAction(
|
||||||
|
'[Layout] Set Menu',
|
||||||
|
props<{ menu: string }>()
|
||||||
|
)
|
||||||
|
export const changesidebarsize = createAction(
|
||||||
|
'[Layout] Set size',
|
||||||
|
props<{ size: string }>()
|
||||||
|
)
|
||||||
|
export const resetState = createAction('[App] Reset State')
|
||||||
65
apiferia/src/app/store/layout/layout-reducers.ts
Normal file
65
apiferia/src/app/store/layout/layout-reducers.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { Action, createReducer, on } from '@ngrx/store'
|
||||||
|
import { localStorageSync } from 'ngrx-store-localstorage'
|
||||||
|
import {
|
||||||
|
LAYOUT_COLOR_TYPES,
|
||||||
|
SIDEBAR_SIZE_TYPES,
|
||||||
|
TOPBAR_COLOR_TYPES,
|
||||||
|
} from './layout'
|
||||||
|
import * as appActions from './layout-action'
|
||||||
|
import {
|
||||||
|
changemenucolor,
|
||||||
|
changesidebarsize,
|
||||||
|
changetheme,
|
||||||
|
changetopbarcolor,
|
||||||
|
} from './layout-action'
|
||||||
|
|
||||||
|
export interface LayoutState {
|
||||||
|
LAYOUT_THEME: string
|
||||||
|
TOPBAR_COLOR: string
|
||||||
|
MENU_COLOR: string
|
||||||
|
MENU_SIZE: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntialState
|
||||||
|
export const initialState: LayoutState = {
|
||||||
|
LAYOUT_THEME: LAYOUT_COLOR_TYPES.LIGHTMODE,
|
||||||
|
TOPBAR_COLOR: TOPBAR_COLOR_TYPES.LIGHT,
|
||||||
|
MENU_COLOR: TOPBAR_COLOR_TYPES.LIGHT,
|
||||||
|
MENU_SIZE: SIDEBAR_SIZE_TYPES.DEFAULT,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reducer
|
||||||
|
export const layoutReducer = createReducer(
|
||||||
|
initialState,
|
||||||
|
on(changetheme, (state, action) => ({
|
||||||
|
...state,
|
||||||
|
LAYOUT_THEME: action.color,
|
||||||
|
})),
|
||||||
|
on(changetopbarcolor, (state, action) => ({
|
||||||
|
...state,
|
||||||
|
TOPBAR_COLOR: action.topbar,
|
||||||
|
})),
|
||||||
|
on(changemenucolor, (state, action) => ({
|
||||||
|
...state,
|
||||||
|
MENU_COLOR: action.menu,
|
||||||
|
})),
|
||||||
|
on(changesidebarsize, (state, action) => ({
|
||||||
|
...state,
|
||||||
|
MENU_SIZE: action.size,
|
||||||
|
})),
|
||||||
|
on(appActions.resetState, () => initialState)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configuration for localStorageSync
|
||||||
|
export function localStorageSyncReducer(reducer: any) {
|
||||||
|
return localStorageSync({ keys: ['app'], rehydrate: true })(reducer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selector
|
||||||
|
export function reducer(state: LayoutState | undefined, action: Action) {
|
||||||
|
return layoutReducer(state, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rootReducer = localStorageSyncReducer(layoutReducer)
|
||||||
|
|
||||||
|
const metaReducers = [rootReducer]
|
||||||
24
apiferia/src/app/store/layout/layout-selector.ts
Normal file
24
apiferia/src/app/store/layout/layout-selector.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { createFeatureSelector, createSelector } from '@ngrx/store'
|
||||||
|
import { LayoutState } from './layout-reducers'
|
||||||
|
|
||||||
|
export const getLayoutState = createFeatureSelector<LayoutState>('layout')
|
||||||
|
|
||||||
|
export const getLayoutColor = createSelector(
|
||||||
|
getLayoutState,
|
||||||
|
(state: LayoutState) => state.LAYOUT_THEME
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getTopbarcolor = createSelector(
|
||||||
|
getLayoutState,
|
||||||
|
(state: LayoutState) => state.TOPBAR_COLOR
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getMenucolor = createSelector(
|
||||||
|
getLayoutState,
|
||||||
|
(state: LayoutState) => state.MENU_COLOR
|
||||||
|
)
|
||||||
|
|
||||||
|
export const getSidebarsize = createSelector(
|
||||||
|
getLayoutState,
|
||||||
|
(state: LayoutState) => state.MENU_SIZE
|
||||||
|
)
|
||||||
41
apiferia/src/app/store/layout/layout.ts
Normal file
41
apiferia/src/app/store/layout/layout.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export enum LAYOUT_TYPES {
|
||||||
|
VERTICAL = 'vertical',
|
||||||
|
HORIZONTAL = 'horizontal',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LAYOUT_COLOR_TYPES {
|
||||||
|
LIGHTMODE = 'light',
|
||||||
|
DARKMODE = 'dark',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LAYOUT_MODE_TYPES {
|
||||||
|
FLUID = 'fluid',
|
||||||
|
BOXED = 'boxed',
|
||||||
|
DETACHED = 'detached',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TOPBAR_COLOR_TYPES {
|
||||||
|
LIGHT = 'light',
|
||||||
|
DARK = 'dark',
|
||||||
|
BRAND = 'brand',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MENU_COLOR_TYPES {
|
||||||
|
LIGHT = 'light',
|
||||||
|
DARK = 'dark',
|
||||||
|
BRAND = 'brand',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SIDEBAR_SIZE_TYPES {
|
||||||
|
DEFAULT = 'default',
|
||||||
|
COMPACT = 'compact',
|
||||||
|
CONDENSED = 'condensed',
|
||||||
|
HOVER = 'sm-hover',
|
||||||
|
FULL = 'full',
|
||||||
|
FULLSCREEN = 'fullscreen',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LAYOUT_POSITION_TYPES {
|
||||||
|
FIXED = 'fixed',
|
||||||
|
SCROLLABLE = 'scrollable',
|
||||||
|
}
|
||||||
34
apiferia/src/app/views/advanced-ui/advanced.route.ts
Normal file
34
apiferia/src/app/views/advanced-ui/advanced.route.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Route } from '@angular/router'
|
||||||
|
import { RatingsComponent } from './ratings/ratings.component'
|
||||||
|
import { SweetalertComponent } from './sweetalert/sweetalert.component'
|
||||||
|
import { SwiperComponent } from './swiper/swiper.component'
|
||||||
|
import { ScrollbarComponent } from './scrollbar/scrollbar.component'
|
||||||
|
import { ToastifyComponent } from './toastify/toastify.component'
|
||||||
|
|
||||||
|
export const ADVANCED_ROUTES: Route[] = [
|
||||||
|
{
|
||||||
|
path: 'ratings',
|
||||||
|
component: RatingsComponent,
|
||||||
|
data: { title: 'Ratings' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'alert',
|
||||||
|
component: SweetalertComponent,
|
||||||
|
data: { title: 'Sweet Alert' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'swiper',
|
||||||
|
component: SwiperComponent,
|
||||||
|
data: { title: 'Swiper Slider' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'scrollbar',
|
||||||
|
component: ScrollbarComponent,
|
||||||
|
data: { title: 'Scrollbar' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'toastify',
|
||||||
|
component: ToastifyComponent,
|
||||||
|
data: { title: 'Toastify' },
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
<app-page-title [title]="'Advanced UI'" [subtitle]="'Ratings'" />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-9">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="overview">
|
||||||
|
Overview
|
||||||
|
<a
|
||||||
|
class="btn btn-sm btn-outline-success rounded-2 float-end"
|
||||||
|
href="https://ng-bootstrap.github.io/#/components/rating/api"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Official Website
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted mb-3">
|
||||||
|
Rater js is the best star rater for the browser. No dependencies.
|
||||||
|
Unlimited number of stars
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h5 class="mt-2">Usage</h5>
|
||||||
|
<p class="mb-0">
|
||||||
|
To use Rating, follow the instructions from its documentation over
|
||||||
|
<a
|
||||||
|
href="https://ng-bootstrap.github.io/#/components/rating/api"
|
||||||
|
class="text-primary"
|
||||||
|
>
|
||||||
|
here</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="basic">
|
||||||
|
Basic Rater Example
|
||||||
|
<a class="anchor-link" href="#basic">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div id="basic-rater" dir="ltr"></div>
|
||||||
|
<ngb-rating
|
||||||
|
[(rate)]="basicRating"
|
||||||
|
[starTemplate]="t"
|
||||||
|
[readonly]="true"
|
||||||
|
[max]="5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="step">
|
||||||
|
Rater with Step Example
|
||||||
|
<a class="anchor-link" href="#step">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div id="rater-step" dir="ltr"></div>
|
||||||
|
<ng-template #t let-fill="fill">
|
||||||
|
@if (fill > 0) {
|
||||||
|
<i
|
||||||
|
class="bx bxs-star text-warning fs-3"
|
||||||
|
[style.width.%]="fill"
|
||||||
|
></i>
|
||||||
|
} @else {
|
||||||
|
<i class="bx bxs-star text-muted opacity-25 fs-3"> </i>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
<ngb-rating [(rate)]="rating" [starTemplate]="t" [max]="5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="custom-message">
|
||||||
|
Custom Messages Example
|
||||||
|
<a class="anchor-link" href="#custom-message">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div id="rater-message" dir="ltr"></div>
|
||||||
|
<ng-template #t let-fill="fill">
|
||||||
|
@if (fill > 0) {
|
||||||
|
<i
|
||||||
|
class="bx bxs-star text-warning fs-3"
|
||||||
|
[style.width.%]="fill"
|
||||||
|
></i>
|
||||||
|
} @else {
|
||||||
|
<i class="bx bxs-star text-muted opacity-25 fs-3"> </i>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
<ngb-rating [(rate)]="stepRating" [starTemplate]="t" [max]="5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="readOnly">
|
||||||
|
ReadOnly Example
|
||||||
|
<a class="anchor-link" href="#readOnly">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div id="rater-unlimitedstar" dir="ltr"></div>
|
||||||
|
<ng-template #step let-fill="fill">
|
||||||
|
@if (fill > 0) {
|
||||||
|
<i
|
||||||
|
class="bx bxs-star text-warning fs-3"
|
||||||
|
[style.width.%]="fill"
|
||||||
|
></i>
|
||||||
|
} @else {
|
||||||
|
<i class="bx bxs-star text-muted opacity-25 fs-3"> </i>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
<ngb-rating
|
||||||
|
[(rate)]="readonly"
|
||||||
|
[readonly]="true"
|
||||||
|
[starTemplate]="step"
|
||||||
|
[max]="5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="onhover">
|
||||||
|
On Hover Event Example
|
||||||
|
<a class="anchor-link" href="#onhover">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div dir="ltr d-flex align-items-center">
|
||||||
|
<div id="rater-onhover" class="align-middle"></div>
|
||||||
|
<ng-template #tem let-fill="fill">
|
||||||
|
@if (fill > 0) {
|
||||||
|
<i
|
||||||
|
class="bx bxs-star text-warning fs-3"
|
||||||
|
[style.width.%]="fill"
|
||||||
|
></i>
|
||||||
|
} @else {
|
||||||
|
<i class="bx bxs-star text-muted opacity-25 fs-3"> </i>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
<ngb-rating
|
||||||
|
[(rate)]="hoverSelected"
|
||||||
|
[starTemplate]="tem"
|
||||||
|
(hover)="hovered = $event"
|
||||||
|
(leave)="hovered = 0"
|
||||||
|
[max]="5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="ratingnum badge bg-info align-middle mt-n2 ms-2">{{
|
||||||
|
hoverSelected
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="reset">
|
||||||
|
Clear/Reset Rater Example
|
||||||
|
<a class="anchor-link link-offset-2" href="#reset">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div dir="ltr">
|
||||||
|
<div id="raterreset" class="align-middle"></div>
|
||||||
|
<span class="clear-rating"></span>
|
||||||
|
<ng-template #tem let-fill="fill">
|
||||||
|
@if (fill > 0) {
|
||||||
|
<i
|
||||||
|
class="bx bxs-star text-warning fs-3"
|
||||||
|
[style.width.%]="fill"
|
||||||
|
></i>
|
||||||
|
} @else {
|
||||||
|
<i class="bx bxs-star text-muted opacity-25 fs-3"> </i>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
<ngb-rating [formControl]="ctrl" [starTemplate]="tem" [max]="5" />
|
||||||
|
<button
|
||||||
|
(click)="ctrl.setValue(null)"
|
||||||
|
id="raterreset-button"
|
||||||
|
class="btn btn-light btn-sm ms-2 mt-n2"
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-3">
|
||||||
|
<ui-examples-list
|
||||||
|
[linkList]="[
|
||||||
|
{ label: 'Overview', link: '#overview' },
|
||||||
|
{ label: 'Basic Example', link: '#basic' },
|
||||||
|
{ label: 'Rater with Step Example', link: '#step' },
|
||||||
|
{ label: 'Custom Messages Example', link: '#custom-message' },
|
||||||
|
{ label: 'ReadOnly Example', link: '#readOnly' },
|
||||||
|
{ label: 'On Hover Event Example', link: '#onhover' },
|
||||||
|
{ label: 'Clear/Reset Rater Example', link: '#reset' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { RatingsComponent } from './ratings.component'
|
||||||
|
|
||||||
|
describe('RatingsComponent', () => {
|
||||||
|
let component: RatingsComponent
|
||||||
|
let fixture: ComponentFixture<RatingsComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [RatingsComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RatingsComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { PageTitleComponent } from '@/app/components/page-title.component'
|
||||||
|
import { UIExamplesListComponent } from '@/app/components/ui-examples-list/ui-examples-list.component'
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
Validators,
|
||||||
|
} from '@angular/forms'
|
||||||
|
import { NgbRatingModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-ratings',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
PageTitleComponent,
|
||||||
|
NgbRatingModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
UIExamplesListComponent,
|
||||||
|
],
|
||||||
|
templateUrl: './ratings.component.html',
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class RatingsComponent {
|
||||||
|
basicRating = 5
|
||||||
|
rating = 3
|
||||||
|
stepRating = 0
|
||||||
|
currentRate = 2
|
||||||
|
selected = 0
|
||||||
|
hovered = 0
|
||||||
|
hoverSelected = 1
|
||||||
|
readonly = 3.5
|
||||||
|
ariaValueText(current: number, max: number) {
|
||||||
|
return `${current} out of ${max} hearts`
|
||||||
|
}
|
||||||
|
ctrl = new FormControl<number | null>(null, Validators.required)
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
<app-page-title [title]="'Advanced UI'" [subtitle]="'Scrollbar'" />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-9">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="default">
|
||||||
|
Default Scroll Example
|
||||||
|
<a class="anchor-link" href="#defalut">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Just use tag
|
||||||
|
<code>ngx-simplebar </code> and add <code>max-height: **px</code> oh
|
||||||
|
fix height
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="border rounded py-3 mb-3">
|
||||||
|
<ngx-simplebar class="px-3" style="max-height: 250px">
|
||||||
|
SimpleBar does only one thing: replace the browser's default
|
||||||
|
scrollbar with a custom CSS-styled one without losing performances.
|
||||||
|
Unlike some popular plugins, SimpleBar doesn't mimic scroll with
|
||||||
|
Javascript, causing janks and strange scrolling behaviours... You
|
||||||
|
keep the awesomeness of native scrolling...with a custom scrollbar!
|
||||||
|
<p>
|
||||||
|
SimpleBar
|
||||||
|
<strong>does NOT implement a custom scroll behaviour</strong>. It
|
||||||
|
keeps the
|
||||||
|
<strong>native</strong>
|
||||||
|
<code>overflow: auto</code>
|
||||||
|
scroll and
|
||||||
|
<strong>only</strong> replace the scrollbar visual appearance.
|
||||||
|
</p>
|
||||||
|
<h5>Design it as you want</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar uses pure CSS to style the scrollbar. You can easily
|
||||||
|
customize it as you want! Or even have multiple style on the same
|
||||||
|
page...or just keep the default style ("Mac OS" scrollbar style).
|
||||||
|
</p>
|
||||||
|
<h5>Lightweight and performant</h5>
|
||||||
|
<p>
|
||||||
|
Only 6kb minified. SimpleBar doesn't use Javascript to handle
|
||||||
|
scrolling. You keep the performances/behaviours of the native
|
||||||
|
scroll.
|
||||||
|
</p>
|
||||||
|
<h5>Supported everywhere</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar has been tested on the following browsers: Chrome,
|
||||||
|
Firefox, Safari, Edge, IE11.
|
||||||
|
</p>
|
||||||
|
</ngx-simplebar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="rtl">
|
||||||
|
RTL Position
|
||||||
|
<a class="anchor-link" href="#rtl">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Just add tag <code>ngx-simplebar</code> and use data attribute
|
||||||
|
<code> data-simplebar-direction='rtl'</code>
|
||||||
|
and add <code>max-height: **px</code> oh fix height
|
||||||
|
</p>
|
||||||
|
<div class="border rounded py-3 mb-3">
|
||||||
|
<ngx-simplebar
|
||||||
|
class="px-3"
|
||||||
|
data-simplebar-direction="rtl"
|
||||||
|
style="max-height: 250px"
|
||||||
|
>
|
||||||
|
SimpleBar does only one thing: replace the browser's default
|
||||||
|
scrollbar with a custom CSS-styled one without losing performances.
|
||||||
|
Unlike some popular plugins, SimpleBar doesn't mimic scroll with
|
||||||
|
Javascript, causing janks and strange scrolling behaviours... You
|
||||||
|
keep the awesomeness of native scrolling...with a custom scrollbar!
|
||||||
|
<p>
|
||||||
|
SimpleBar
|
||||||
|
<strong>does NOT implement a custom scroll behaviour</strong>. It
|
||||||
|
keeps the
|
||||||
|
<strong>native</strong>
|
||||||
|
<code>overflow: auto</code>
|
||||||
|
scroll and
|
||||||
|
<strong>only</strong> replace the scrollbar visual appearance.
|
||||||
|
</p>
|
||||||
|
<h5>Design it as you want</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar uses pure CSS to style the scrollbar. You can easily
|
||||||
|
customize it as you want! Or even have multiple style on the same
|
||||||
|
page...or just keep the default style ("Mac OS" scrollbar style).
|
||||||
|
</p>
|
||||||
|
<h5>Lightweight and performant</h5>
|
||||||
|
<p>
|
||||||
|
Only 6kb minified. SimpleBar doesn't use Javascript to handle
|
||||||
|
scrolling. You keep the performances/behaviours of the native
|
||||||
|
scroll.
|
||||||
|
</p>
|
||||||
|
<h5>Supported everywhere</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar has been tested on the following browsers: Chrome,
|
||||||
|
Firefox, Safari, Edge, IE11.
|
||||||
|
</p>
|
||||||
|
</ngx-simplebar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="size">
|
||||||
|
Scroll Size
|
||||||
|
<a class="anchor-link" href="#size">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Just add a tag <code>ngx-simplebar</code> and use data attribute use
|
||||||
|
data attribute <code>data-simplebar-lg</code> and add
|
||||||
|
<code>max-height: **px</code> oh fix height
|
||||||
|
</p>
|
||||||
|
<div class="border rounded py-3 mb-3">
|
||||||
|
<ngx-simplebar
|
||||||
|
class="px-3"
|
||||||
|
data-simplebar-lg
|
||||||
|
style="max-height: 250px"
|
||||||
|
>
|
||||||
|
SimpleBar does only one thing: replace the browser's default
|
||||||
|
scrollbar with a custom CSS-styled one without losing performances.
|
||||||
|
Unlike some popular plugins, SimpleBar doesn't mimic scroll with
|
||||||
|
Javascript, causing janks and strange scrolling behaviours... You
|
||||||
|
keep the awesomeness of native scrolling...with a custom scrollbar!
|
||||||
|
<p>
|
||||||
|
SimpleBar
|
||||||
|
<strong>does NOT implement a custom scroll behaviour</strong>. It
|
||||||
|
keeps the
|
||||||
|
<strong>native</strong>
|
||||||
|
<code>overflow: auto</code>
|
||||||
|
scroll and
|
||||||
|
<strong>only</strong> replace the scrollbar visual appearance.
|
||||||
|
</p>
|
||||||
|
<h5>Design it as you want</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar uses pure CSS to style the scrollbar. You can easily
|
||||||
|
customize it as you want! Or even have multiple style on the same
|
||||||
|
page...or just keep the default style ("Mac OS" scrollbar style).
|
||||||
|
</p>
|
||||||
|
<h5>Lightweight and performant</h5>
|
||||||
|
<p>
|
||||||
|
Only 6kb minified. SimpleBar doesn't use Javascript to handle
|
||||||
|
scrolling. You keep the performances/behaviours of the native
|
||||||
|
scroll.
|
||||||
|
</p>
|
||||||
|
<h5>Supported everywhere</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar has been tested on the following browsers: Chrome,
|
||||||
|
Firefox, Safari, Edge, IE11.
|
||||||
|
</p>
|
||||||
|
</ngx-simplebar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="color">
|
||||||
|
Scroll Color
|
||||||
|
<a class="anchor-link" href="#color">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Just add a tag <code>ngx-simplebar</code> and use data attribute
|
||||||
|
<code>data-simplebar data-simplebar-*</code>
|
||||||
|
and add <code>max-height: **px</code> oh fix height
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="border rounded py-3 mb-3">
|
||||||
|
<ngx-simplebar
|
||||||
|
class="px-3"
|
||||||
|
data-simplebar-primary
|
||||||
|
style="max-height: 250px"
|
||||||
|
>
|
||||||
|
SimpleBar does only one thing: replace the browser's default
|
||||||
|
scrollbar with a custom CSS-styled one without losing performances.
|
||||||
|
Unlike some popular plugins, SimpleBar doesn't mimic scroll with
|
||||||
|
Javascript, causing janks and strange scrolling behaviours... You
|
||||||
|
keep the awesomeness of native scrolling...with a custom scrollbar!
|
||||||
|
<p>
|
||||||
|
SimpleBar
|
||||||
|
<strong>does NOT implement a custom scroll behaviour</strong>. It
|
||||||
|
keeps the
|
||||||
|
<strong>native</strong>
|
||||||
|
<code>overflow: auto</code>
|
||||||
|
scroll and
|
||||||
|
<strong>only</strong> replace the scrollbar visual appearance.
|
||||||
|
</p>
|
||||||
|
<h5>Design it as you want</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar uses pure CSS to style the scrollbar. You can easily
|
||||||
|
customize it as you want! Or even have multiple style on the same
|
||||||
|
page...or just keep the default style ("Mac OS" scrollbar style).
|
||||||
|
</p>
|
||||||
|
<h5>Lightweight and performant</h5>
|
||||||
|
<p>
|
||||||
|
Only 6kb minified. SimpleBar doesn't use Javascript to handle
|
||||||
|
scrolling. You keep the performances/behaviours of the native
|
||||||
|
scroll.
|
||||||
|
</p>
|
||||||
|
<h5>Supported everywhere</h5>
|
||||||
|
<p>
|
||||||
|
SimpleBar has been tested on the following browsers: Chrome,
|
||||||
|
Firefox, Safari, Edge, IE11.
|
||||||
|
</p>
|
||||||
|
</ngx-simplebar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-3">
|
||||||
|
<ui-examples-list
|
||||||
|
[linkList]="[
|
||||||
|
{ label: 'Default Scroll Example', link: '#default' },
|
||||||
|
{ label: 'RTL Position', link: '#rtl' },
|
||||||
|
{ label: 'Scroll Size', link: '#size' },
|
||||||
|
{ label: 'Scroll Color', link: '#color' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { ScrollbarComponent } from './scrollbar.component'
|
||||||
|
|
||||||
|
describe('ScrollbarComponent', () => {
|
||||||
|
let component: ScrollbarComponent
|
||||||
|
let fixture: ComponentFixture<ScrollbarComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ScrollbarComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ScrollbarComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { PageTitleComponent } from '@/app/components/page-title.component'
|
||||||
|
import { UIExamplesListComponent } from '@/app/components/ui-examples-list/ui-examples-list.component'
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { SimplebarAngularModule } from 'simplebar-angular'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-scrollbar',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
PageTitleComponent,
|
||||||
|
SimplebarAngularModule,
|
||||||
|
UIExamplesListComponent,
|
||||||
|
],
|
||||||
|
templateUrl: './scrollbar.component.html',
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class ScrollbarComponent {}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
<app-page-title [title]="'Advanced UI'" [subtitle]="'Sweet Alert'" />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-9">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="overview">
|
||||||
|
Overview
|
||||||
|
<a
|
||||||
|
class="btn btn-sm btn-outline-success rounded-2 float-end"
|
||||||
|
href="https://sweetalert2.github.io/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Official Website
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted mb-3">
|
||||||
|
A beautiful, responsive, customizable, accessible (WAI-ARIA)
|
||||||
|
replacement for JavaScript's popup boxes
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h5 class="mt-2">Usage</h5>
|
||||||
|
<p class="mb-0">
|
||||||
|
To use SweetAlert, follow the instructions from its documentation
|
||||||
|
over<a href="https://sweetalert2.github.io/" class="text-primary">
|
||||||
|
here</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="basic">
|
||||||
|
Basic<a class="anchor-link" href="#basic">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<button
|
||||||
|
type=" "
|
||||||
|
class="btn btn-primary"
|
||||||
|
(click)="basicMessage()"
|
||||||
|
id="sweetalert-basic"
|
||||||
|
>
|
||||||
|
Click me
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="title">
|
||||||
|
A Title with a Text Under<a class="anchor-link" href="#title">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="titleText()"
|
||||||
|
class="btn btn-primary"
|
||||||
|
id="sweetalert-title"
|
||||||
|
>
|
||||||
|
Click me
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="message">
|
||||||
|
Message<a class="anchor-link" href="#message">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="hstack gap-2 mb-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-success"
|
||||||
|
(click)="success()"
|
||||||
|
id="sweetalert-success"
|
||||||
|
>
|
||||||
|
Success
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-warning"
|
||||||
|
(click)="warning()"
|
||||||
|
id="sweetalert-warning"
|
||||||
|
>
|
||||||
|
Warning
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-info"
|
||||||
|
(click)="info()"
|
||||||
|
id="sweetalert-info"
|
||||||
|
>
|
||||||
|
Info
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-danger"
|
||||||
|
(click)="danger()"
|
||||||
|
id="sweetalert-error"
|
||||||
|
>
|
||||||
|
Error
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="longcontent">
|
||||||
|
Long content Images Message<a class="anchor-link" href="#longcontent"
|
||||||
|
>#</a
|
||||||
|
>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="longContent()"
|
||||||
|
class="btn btn-primary"
|
||||||
|
id="sweetalert-longcontent"
|
||||||
|
>
|
||||||
|
Click me
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="parameter">
|
||||||
|
Parameter<a class="anchor-link" href="#parameter">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="parameter()"
|
||||||
|
class="btn btn-primary"
|
||||||
|
id="sweetalert-params"
|
||||||
|
>
|
||||||
|
Click me
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-3">
|
||||||
|
<ui-examples-list
|
||||||
|
[linkList]="[
|
||||||
|
{ label: 'Overview', link: '#overview' },
|
||||||
|
{ label: 'Basic', link: '#basic' },
|
||||||
|
{ label: 'A Title with a Text Under', link: '#title' },
|
||||||
|
{ label: 'Message', link: '#message' },
|
||||||
|
{ label: 'long content Images Message', link: '#longcontent' },
|
||||||
|
{ label: 'Parameter', link: '#parameter' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { SweetalertComponent } from './sweetalert.component'
|
||||||
|
|
||||||
|
describe('SweetalertComponent', () => {
|
||||||
|
let component: SweetalertComponent
|
||||||
|
let fixture: ComponentFixture<SweetalertComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SweetalertComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SweetalertComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import { PageTitleComponent } from '@/app/components/page-title.component'
|
||||||
|
import { UIExamplesListComponent } from '@/app/components/ui-examples-list/ui-examples-list.component'
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
import Swal from 'sweetalert2'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sweetalert',
|
||||||
|
standalone: true,
|
||||||
|
imports: [PageTitleComponent, UIExamplesListComponent],
|
||||||
|
templateUrl: './sweetalert.component.html',
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class SweetalertComponent {
|
||||||
|
basicMessage() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Any fool can use a computer',
|
||||||
|
confirmButtonColor: '#1c84ee',
|
||||||
|
showCloseButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
titleText() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'The Internet?',
|
||||||
|
text: 'That thing is still around?',
|
||||||
|
icon: 'question',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
showCloseButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
success() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Good job!',
|
||||||
|
text: 'You clicked the button!',
|
||||||
|
icon: 'success',
|
||||||
|
showCancelButton: true,
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs me-2 mt-2',
|
||||||
|
cancelButton: 'btn btn-danger w-xs mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
showCloseButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
danger() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Oops...',
|
||||||
|
text: 'Something went wrong!',
|
||||||
|
icon: 'error',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
footer: '<a href="">Why do I have this issue?</a>',
|
||||||
|
showCloseButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Oops...',
|
||||||
|
text: 'Something went wrong!',
|
||||||
|
icon: 'info',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
footer: '<a href="">Why do I have this issue?</a>',
|
||||||
|
showCloseButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
warning() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Oops...',
|
||||||
|
text: 'Something went wrong!',
|
||||||
|
icon: 'warning',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs mt-2',
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonsStyling: false,
|
||||||
|
footer: '<a href="">Why do I have this issue?</a>',
|
||||||
|
showCloseButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
longContent() {
|
||||||
|
Swal.fire({
|
||||||
|
imageUrl: 'https://placeholder.pics/svg/300x1500',
|
||||||
|
imageHeight: 1500,
|
||||||
|
imageAlt: 'A tall image',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
showCloseButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter() {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Are you sure?',
|
||||||
|
text: "You won't be able to revert this!",
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Yes, delete it!',
|
||||||
|
cancelButtonText: 'No, cancel!',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs me-2 mt-2',
|
||||||
|
cancelButton: 'btn btn-danger w-xs mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
showCloseButton: false,
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.value) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Deleted!',
|
||||||
|
text: 'Your file has been deleted.',
|
||||||
|
icon: 'success',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary w-xs mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
})
|
||||||
|
} else if (
|
||||||
|
// Read more about handling dismissals
|
||||||
|
result.dismiss === Swal.DismissReason.cancel
|
||||||
|
) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Cancelled',
|
||||||
|
text: 'Your imaginary file is safe :)',
|
||||||
|
icon: 'error',
|
||||||
|
customClass: {
|
||||||
|
confirmButton: 'btn btn-primary mt-2',
|
||||||
|
},
|
||||||
|
buttonsStyling: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
567
apiferia/src/app/views/advanced-ui/swiper/swiper.component.html
Normal file
567
apiferia/src/app/views/advanced-ui/swiper/swiper.component.html
Normal file
@@ -0,0 +1,567 @@
|
|||||||
|
<app-page-title [title]="'Advanced UI'" [subtitle]="'Swiper Slider'" />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-9">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="overview">
|
||||||
|
Overview
|
||||||
|
<a
|
||||||
|
class="btn btn-sm btn-outline-success rounded-2 float-end"
|
||||||
|
href="https://swiperjs.com/get-started"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Official Website
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted mb-3">
|
||||||
|
Swiper is the most modern slider with hardware accelerated transitions
|
||||||
|
and amazing native behavior.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h5 class="mt-2">Usage</h5>
|
||||||
|
<p>
|
||||||
|
To use Swiper, follow the instructions from its documentation over
|
||||||
|
<a href="https://swiperjs.com/get-started" class="text-primary">
|
||||||
|
here</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="default">
|
||||||
|
Default Swiper
|
||||||
|
<a class="anchor-link" href="#default">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code>[config]="swiperConfig" init="false"</code>
|
||||||
|
attribute to set a default swiper.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper rounded" data-swiper="default">
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
[config]="swiperConfig"
|
||||||
|
init="false"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-1.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-2.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="navigation">
|
||||||
|
Navigation & Pagination Swiper
|
||||||
|
<a class="anchor-link" href="#navigation">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code>[config]="swiperNavigation" init="false"</code>
|
||||||
|
attribute to set a swiper with navigation and pagination.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper rounded">
|
||||||
|
<swiper-container
|
||||||
|
[config]="swiperNavigation"
|
||||||
|
class="swiper-wrapper"
|
||||||
|
init="false"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-6.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div class="swiper-button-next basic-next"></div>
|
||||||
|
<div class="swiper-button-prev basic-prev"></div>
|
||||||
|
<div class="swiper-pagination basic-pagination"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="pagination-dynamic">
|
||||||
|
Pagination Dynamic Swiper
|
||||||
|
<a class="anchor-link" href="#pagination-dynamic">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code> init="false" [config]="swiperPagination"</code>
|
||||||
|
attribute to set a swiper with dynamic and pagination.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper rounded" data-swiper="dynamic">
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
init="false"
|
||||||
|
[config]="swiperPagination"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-6.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div
|
||||||
|
id="swiper-pagination"
|
||||||
|
class="swiper-pagination dynamic-pagination"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="effect-fade">
|
||||||
|
Effect Fade Swiper
|
||||||
|
<a class="anchor-link" href="#effect-fade">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code>[config]="swiperfadeEffect" init="false"</code>
|
||||||
|
attribute to set a swiper with fade effect.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper rounded" data-swiper="effect-fade">
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
[config]="swiperfadeEffect"
|
||||||
|
init="false"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div
|
||||||
|
id="swiper-pagination"
|
||||||
|
class="swiper-pagination effect-pagination"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="effect-creative">
|
||||||
|
Effect Creative Swiper
|
||||||
|
<a class="anchor-link" href="#effect-creative">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code> [config]="swiperCreativeEffect" init="false"</code>
|
||||||
|
attribute to set a swiper with creative custom effect.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper" data-swiper="creative">
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
[config]="swiperCreativeEffect"
|
||||||
|
init="false"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div class="swiper-pagination creative-pagination"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="effect-flip">
|
||||||
|
Effect Flip Swiper
|
||||||
|
<a class="anchor-link" href="#effect-flip">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code>[config]="swiperFlip" init="false"</code>
|
||||||
|
attribute to set a swiper with flip custom effect.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper" data-swiper="flip">
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
[config]="swiperFlip"
|
||||||
|
init="false"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div class="swiper-pagination swiper-flip"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="scrollbar">
|
||||||
|
Scrollbar Swiper
|
||||||
|
<a class="anchor-link" href="#scrollbar">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code>[config]="swiperScroll" init="false"</code>
|
||||||
|
attribute to set a swiper with scrollbar pagination.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper" data-swiper="scrollbar">
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
[config]="swiperScroll"
|
||||||
|
init="false"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div class="swiper-button-next"></div>
|
||||||
|
<div class="swiper-button-prev"></div>
|
||||||
|
<div class="swiper-scrollbar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="vertical">
|
||||||
|
Vertical Swiper
|
||||||
|
<a class="anchor-link" href="#vertical">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code>[config]="verticalConfig" init="false"</code>
|
||||||
|
attribute to set a swiper with vertical pagination.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div class="swiper" data-swiper="vertical" style="height: 320px">
|
||||||
|
<swiper-container
|
||||||
|
[config]="verticalConfig"
|
||||||
|
init="false"
|
||||||
|
class="swiper-wrapper"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div class="swiper-pagination vertical-pagination"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="mousewheel">
|
||||||
|
Mousewheel Control Swiper
|
||||||
|
<a class="anchor-link" href="#mousewheel">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code>init="false" [config]="swiperMouseWheel"</code>
|
||||||
|
attribute to set a swiper with mousewheel scroll.
|
||||||
|
</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 mb-3">
|
||||||
|
<div
|
||||||
|
class="swiper rounded"
|
||||||
|
data-swiper="mousewheel"
|
||||||
|
style="height: 324px"
|
||||||
|
>
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
init="false"
|
||||||
|
[config]="swiperMouseWheel"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div class="swiper-pagination mouse-wheel-pagination"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="responsive">
|
||||||
|
Responsive Breakpoints Swiper
|
||||||
|
<a class="anchor-link" href="#overview">#</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted">
|
||||||
|
Use
|
||||||
|
<code> init="false" [config]="resposiveConfig"</code>
|
||||||
|
attribute to set a responsive swiper.
|
||||||
|
</p>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div
|
||||||
|
class="swiper rounded gallery-light pb-4"
|
||||||
|
data-swiper="responsive"
|
||||||
|
>
|
||||||
|
<swiper-container
|
||||||
|
class="swiper-wrapper"
|
||||||
|
init="false"
|
||||||
|
[config]="resposiveConfig"
|
||||||
|
>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-1.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-2.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-3.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-4.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-5.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
<swiper-slide class="swiper-slide">
|
||||||
|
<img
|
||||||
|
src="assets/images/small/img-6.jpg"
|
||||||
|
alt=""
|
||||||
|
class="img-fluid rounded"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper-container>
|
||||||
|
<div
|
||||||
|
class="swiper-pagination swiper-pagination-dark responsive-pagination"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-3">
|
||||||
|
<ui-examples-list
|
||||||
|
[linkList]="[
|
||||||
|
{ label: 'Overview', link: '#overview' },
|
||||||
|
{ label: 'Default Swiper', link: '#default' },
|
||||||
|
{ label: 'Navigation & Pagination Swiper', link: '#navigation' },
|
||||||
|
{ label: 'Pagination Dynamic Swiper', link: '#pagination-dynamic' },
|
||||||
|
{ label: 'Effect Fade Swiper', link: '#effect-fade' },
|
||||||
|
{ label: 'Effect Creative Swiper', link: '#effect-creative' },
|
||||||
|
{ label: 'Effect Flip Swiper', link: '#effect-flip' },
|
||||||
|
{ label: 'Scrollbar Swiper', link: '#scrollbar' },
|
||||||
|
{ label: 'Vertical Swiper', link: '#vertical' },
|
||||||
|
{ label: 'Mousewheel Control Swiper', link: '#mousewheel' },
|
||||||
|
{ label: 'Responsive Breakpoints Swiper', link: '#responsive' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { SwiperComponent } from './swiper.component'
|
||||||
|
|
||||||
|
describe('SwiperComponent', () => {
|
||||||
|
let component: SwiperComponent
|
||||||
|
let fixture: ComponentFixture<SwiperComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SwiperComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SwiperComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
187
apiferia/src/app/views/advanced-ui/swiper/swiper.component.ts
Normal file
187
apiferia/src/app/views/advanced-ui/swiper/swiper.component.ts
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import { PageTitleComponent } from '@/app/components/page-title.component'
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core'
|
||||||
|
import { SwiperOptions } from 'swiper/types'
|
||||||
|
import {
|
||||||
|
Autoplay,
|
||||||
|
EffectCreative,
|
||||||
|
EffectFade,
|
||||||
|
EffectFlip,
|
||||||
|
Mousewheel,
|
||||||
|
Navigation,
|
||||||
|
Pagination,
|
||||||
|
Scrollbar,
|
||||||
|
} from 'swiper/modules'
|
||||||
|
import { UIExamplesListComponent } from '@/app/components/ui-examples-list/ui-examples-list.component'
|
||||||
|
import { register } from 'swiper/element'
|
||||||
|
import { SwiperDirective } from '@/app/components/swiper-directive.component'
|
||||||
|
|
||||||
|
register()
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-swiper',
|
||||||
|
standalone: true,
|
||||||
|
imports: [PageTitleComponent, UIExamplesListComponent, SwiperDirective],
|
||||||
|
templateUrl: './swiper.component.html',
|
||||||
|
styles: ``,
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
})
|
||||||
|
export class SwiperComponent {
|
||||||
|
swiperConfig: SwiperOptions = {
|
||||||
|
loop: true,
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
swiperNavigation: SwiperOptions = {
|
||||||
|
modules: [Autoplay, Pagination, Navigation],
|
||||||
|
loop: true,
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
clickable: true,
|
||||||
|
el: '.basic-pagination',
|
||||||
|
},
|
||||||
|
navigation: {
|
||||||
|
nextEl: '.basic-next',
|
||||||
|
prevEl: '.basic-prev',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
swiperPagination: SwiperOptions = {
|
||||||
|
modules: [Autoplay, Pagination],
|
||||||
|
loop: true,
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
clickable: true,
|
||||||
|
el: '.dynamic-pagination',
|
||||||
|
dynamicBullets: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
swiperfadeEffect: SwiperOptions = {
|
||||||
|
modules: [Pagination, Autoplay, EffectFade],
|
||||||
|
loop: true,
|
||||||
|
effect: 'fade',
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
clickable: true,
|
||||||
|
el: '.effect-pagination',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
swiperCreativeEffect: SwiperOptions = {
|
||||||
|
modules: [Autoplay, Pagination, EffectCreative],
|
||||||
|
loop: true,
|
||||||
|
grabCursor: true,
|
||||||
|
effect: 'creative',
|
||||||
|
// EffectCreative: {
|
||||||
|
// prev: {
|
||||||
|
// shadow: true,
|
||||||
|
// translate: [0, 0, -400],
|
||||||
|
// },
|
||||||
|
// next: {
|
||||||
|
// translate: ["100%", 0, 0],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
el: '.creative-pagination',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
swiperFlip: SwiperOptions = {
|
||||||
|
modules: [EffectFlip, Pagination],
|
||||||
|
loop: true,
|
||||||
|
effect: 'flip',
|
||||||
|
grabCursor: true,
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
el: '.swiper-flip',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
swiperScroll: SwiperOptions = {
|
||||||
|
modules: [Autoplay, Scrollbar, Navigation],
|
||||||
|
loop: true,
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
scrollbar: {
|
||||||
|
el: '.swiper-scrollbar',
|
||||||
|
hide: true,
|
||||||
|
},
|
||||||
|
navigation: {
|
||||||
|
nextEl: '.swiper-button-next',
|
||||||
|
prevEl: '.swiper-button-prev',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
verticalConfig: SwiperOptions = {
|
||||||
|
modules: [Autoplay, Pagination],
|
||||||
|
loop: true,
|
||||||
|
direction: 'vertical',
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
el: '.vertical-pagination',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
swiperMouseWheel: SwiperOptions = {
|
||||||
|
modules: [Autoplay, Pagination, Mousewheel],
|
||||||
|
loop: true,
|
||||||
|
direction: 'vertical',
|
||||||
|
mousewheel: true,
|
||||||
|
autoplay: {
|
||||||
|
delay: 2500,
|
||||||
|
disableOnInteraction: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
el: '.mouse-wheel-pagination',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resposiveConfig: SwiperOptions = {
|
||||||
|
modules: [Pagination],
|
||||||
|
loop: true,
|
||||||
|
slidesPerView: 1,
|
||||||
|
spaceBetween: 10,
|
||||||
|
pagination: {
|
||||||
|
el: '.responsive-pagination',
|
||||||
|
clickable: true,
|
||||||
|
},
|
||||||
|
breakpoints: {
|
||||||
|
768: {
|
||||||
|
slidesPerView: 2,
|
||||||
|
spaceBetween: 40,
|
||||||
|
},
|
||||||
|
1200: {
|
||||||
|
slidesPerView: 3,
|
||||||
|
spaceBetween: 50,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
<app-page-title [title]="'Advanced UI'" [subtitle]="'Toastify'" />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-9">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-1 anchor" id="overview">
|
||||||
|
Overview
|
||||||
|
<a
|
||||||
|
class="btn btn-sm btn-outline-success rounded-2 float-end"
|
||||||
|
href="https://www.npmjs.com/package/ngx-toastr"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Official Website
|
||||||
|
</a>
|
||||||
|
</h5>
|
||||||
|
<p class="text-muted mb-3">Better notification messages</p>
|
||||||
|
|
||||||
|
<h5 class="mt-2">Usage</h5>
|
||||||
|
<p class="mb-0">
|
||||||
|
To use Toastify, follow the instructions from its documentation over
|
||||||
|
<a
|
||||||
|
href="https://www.npmjs.com/package/ngx-toastr#setup"
|
||||||
|
class="text-primary"
|
||||||
|
>
|
||||||
|
here</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title anchor mb-3" id="basic">
|
||||||
|
Basic Toastify JS Example
|
||||||
|
<a class="anchor-link" href="#basic">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="hstack flex-wrap gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-toast
|
||||||
|
data-toast-text="Welcome Back! This is a Toast Notification"
|
||||||
|
(click)="default()"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Default
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="success('toast-top-center')"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Success
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="warning()"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Warning
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="error()"
|
||||||
|
data-toast
|
||||||
|
data-toast-text="Error ! An error occurred."
|
||||||
|
data-toast-gravity="top"
|
||||||
|
data-toast-position="center"
|
||||||
|
data-toast-className="danger"
|
||||||
|
data-toast-duration="3000"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Error
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="display_position">
|
||||||
|
Display Position Example
|
||||||
|
<a class="anchor-link" href="#display_position">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="hstack flex-wrap gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-top-left')"
|
||||||
|
data-toast
|
||||||
|
data-toast-text="Welcome Back ! This is a Toast Notification"
|
||||||
|
data-toast-gravity="top"
|
||||||
|
data-toast-position="left"
|
||||||
|
data-toast-duration="3000"
|
||||||
|
data-toast-close="close"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Top Left
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-top-center')"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Top Center
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-top-right')"
|
||||||
|
data-toast
|
||||||
|
data-toast-text="Welcome Back ! This is a Toast Notification"
|
||||||
|
data-toast-gravity="top"
|
||||||
|
data-toast-position="right"
|
||||||
|
data-toast-duration="3000"
|
||||||
|
data-toast-close="close"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Top Right
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-bottom-left')"
|
||||||
|
data-toast
|
||||||
|
data-toast-text="Welcome Back ! This is a Toast Notification"
|
||||||
|
data-toast-gravity="bottom"
|
||||||
|
data-toast-position="left"
|
||||||
|
data-toast-duration="3000"
|
||||||
|
data-toast-close="close"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Bottom Left
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-bottom-center')"
|
||||||
|
data-toast
|
||||||
|
data-toast-text="Welcome Back ! This is a Toast Notification"
|
||||||
|
data-toast-gravity="bottom"
|
||||||
|
data-toast-position="center"
|
||||||
|
data-toast-duration="3000"
|
||||||
|
data-toast-close="close"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Bottom Center
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-bottom-right')"
|
||||||
|
data-toast
|
||||||
|
data-toast-text="Welcome Back ! This is a Toast Notification"
|
||||||
|
data-toast-gravity="bottom"
|
||||||
|
data-toast-position="right"
|
||||||
|
data-toast-duration="3000"
|
||||||
|
data-toast-close="close"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Bottom Right
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title mb-3 anchor" id="rater">
|
||||||
|
Offset, Close Button & Duration Example
|
||||||
|
<a class="anchor-link" href="#rater">#</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="d-flex align-items-center flex-wrap gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-top-right')"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Offset Position
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="positionToast('toast-top-right')"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Close icon Display
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="durationToast('toast-top-right')"
|
||||||
|
class="btn btn-light w-xs"
|
||||||
|
>
|
||||||
|
Duration
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-3">
|
||||||
|
<ui-examples-list
|
||||||
|
[linkList]="[
|
||||||
|
{ label: 'Overview', link: '#overview' },
|
||||||
|
{ label: 'Basic', link: '#basic' },
|
||||||
|
{ label: 'Display Position Example', link: '#display_position' },
|
||||||
|
{ label: 'Offset, Close Button & Duration Example', link: '#rater' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
|
import { ToastifyComponent } from './toastify.component'
|
||||||
|
|
||||||
|
describe('ToastifyComponent', () => {
|
||||||
|
let component: ToastifyComponent
|
||||||
|
let fixture: ComponentFixture<ToastifyComponent>
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ToastifyComponent],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ToastifyComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { PageTitleComponent } from '@/app/components/page-title.component'
|
||||||
|
import { UIExamplesListComponent } from '@/app/components/ui-examples-list/ui-examples-list.component'
|
||||||
|
import { Component } from '@angular/core'
|
||||||
|
import { ToastrService } from 'ngx-toastr'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-toastify',
|
||||||
|
standalone: true,
|
||||||
|
imports: [PageTitleComponent, UIExamplesListComponent],
|
||||||
|
templateUrl: './toastify.component.html',
|
||||||
|
styles: ``,
|
||||||
|
})
|
||||||
|
export class ToastifyComponent {
|
||||||
|
constructor(public toastService: ToastrService) {}
|
||||||
|
|
||||||
|
default() {
|
||||||
|
this.toastService.show('Welcome Back! This is a Toast Notification', '', {
|
||||||
|
toastClass: 'btn-light text-bg-primary px-3 py-2 mt-2 rounded-1',
|
||||||
|
positionClass: 'toast-top-right',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
success(position: any) {
|
||||||
|
this.toastService.show('Your application was successfully sent', '', {
|
||||||
|
toastClass: 'btn-light text-bg-success px-3 py-2 mt-2 rounded-1',
|
||||||
|
positionClass: position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
warning() {
|
||||||
|
this.toastService.show('Warning ! Something went wrong try again', '', {
|
||||||
|
toastClass: 'btn-light text-bg-warning px-3 py-2 mt-2 rounded-1',
|
||||||
|
positionClass: 'toast-top-center',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
this.toastService.show('Error ! An error occurred.', '', {
|
||||||
|
toastClass: 'btn-light text-bg-danger px-3 py-2 mt-2 rounded-1',
|
||||||
|
positionClass: 'toast-top-center',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
positionToast(position: any) {
|
||||||
|
this.toastService.success(
|
||||||
|
'Welcome Back ! This is a Toast Notification',
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
timeOut: 3000,
|
||||||
|
closeButton: true,
|
||||||
|
toastClass: 'btn-light text-bg-success px-3 py-2 mt-2 rounded-1',
|
||||||
|
positionClass: position,
|
||||||
|
tapToDismiss: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
durationToast(position: any) {
|
||||||
|
this.toastService.show('Toast Duration 5s', '', {
|
||||||
|
timeOut: 5000,
|
||||||
|
positionClass: position,
|
||||||
|
toastClass: 'btn-light text-bg-success px-3 py-2 mt-2 rounded-1',
|
||||||
|
closeButton: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
34
apiferia/src/app/views/apps/apps.route.ts
Normal file
34
apiferia/src/app/views/apps/apps.route.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Route } from '@angular/router'
|
||||||
|
import { ChatComponent } from './chat/chat.component'
|
||||||
|
import { ContactsComponent } from './contacts/contacts.component'
|
||||||
|
import { EmailComponent } from './email/email.component'
|
||||||
|
import { SocialComponent } from './social/social.component'
|
||||||
|
import { TodoComponent } from './todo/todo.component'
|
||||||
|
|
||||||
|
export const APPS_ROUTES: Route[] = [
|
||||||
|
{
|
||||||
|
path: 'chat',
|
||||||
|
component: ChatComponent,
|
||||||
|
data: { title: 'Chat' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'email',
|
||||||
|
component: EmailComponent,
|
||||||
|
data: { title: 'Email' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'todo',
|
||||||
|
component: TodoComponent,
|
||||||
|
data: { title: 'To do' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'social',
|
||||||
|
component: SocialComponent,
|
||||||
|
data: { title: 'Social' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'contacts',
|
||||||
|
component: ContactsComponent,
|
||||||
|
data: { title: 'Contacts' },
|
||||||
|
},
|
||||||
|
]
|
||||||
17
apiferia/src/app/views/apps/chat/chat.component.html
Normal file
17
apiferia/src/app/views/apps/chat/chat.component.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<div class="row g-1">
|
||||||
|
<div class="col-xxl-3">
|
||||||
|
<div
|
||||||
|
class="offcanvas-xxl offcanvas-start h-100"
|
||||||
|
container="body"
|
||||||
|
tabindex="-1"
|
||||||
|
id="Contactoffcanvas"
|
||||||
|
aria-labelledby="ContactoffcanvasLabel"
|
||||||
|
>
|
||||||
|
<chat-contacts (profileDetail)="receiveDataFromChild($event)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xxl-9">
|
||||||
|
<chat-list [profileDetail]="profileDetail" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user