Mise à jour articles et produits

This commit is contained in:
Rampeur
2025-08-19 05:12:16 +02:00
parent 977104aac2
commit ed6104ed21
137 changed files with 1498 additions and 681 deletions
+3
View File
@@ -53,3 +53,6 @@ sonar.properties
**/*.copy.ts **/*.copy.ts
**/*.copy.scss **/*.copy.scss
**/*.copy.html **/*.copy.html
.next/
next-env.d.ts
+1 -1
View File
@@ -1,4 +1,4 @@
# HeadupAngular # Ad Astra
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.2.2. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.2.2.
+1 -1
View File
@@ -1,6 +1,6 @@
<div class="container auth-page"> <div class="container auth-page">
<div class="row"> <div class="row">
<div class="col-md-6 offset-md-3 col-xs-12"> <div class="col-lg-8 offset-lg-2 col-xl-6 offset-xl-3 col-xs-12">
<h1 class="text-xs-center">{{ title }}</h1> <h1 class="text-xs-center">{{ title }}</h1>
<p class="text-xs-center"> <p class="text-xs-center">
@if (authType === 'register') { @if (authType === 'register') {
+6 -17
View File
@@ -1,14 +1,13 @@
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { Title } from '@angular/platform-browser';
import { trigger, state, style, animate, transition } from '@angular/animations'; import { trigger, state, style, animate, transition } from '@angular/animations';
import { Component, DestroyRef, inject, OnInit } from '@angular/core'; import { Component, DestroyRef, inject, OnInit } from '@angular/core';
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { Observable, Subscription } from 'rxjs';
import { ListErrorsComponent, } from 'src/app/components/shared'; import { ListErrorsComponent, } from 'src/app/components/shared';
import { Errors, Jump } from 'src/app/core/models'; import { Errors } from 'src/app/core/models';
import { JumpsService } from 'src/app/core/services'; //import { UserService } from 'src/app/core/services';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
@@ -35,28 +34,18 @@ import { JumpsService } from 'src/app/core/services';
] ]
}) })
export class HomeComponent implements OnInit { export class HomeComponent implements OnInit {
private _lastjump: Subscription = new Subscription();
private _lastjump$: Observable<Jump> = new Observable();
public title = 'Shop bientôt disponible!'; public title = 'Shop bientôt disponible!';
public description = 'Encore un peu de patience, notre shop sera mis en ligne d\'ici peu.'; public description = 'Encore un peu de patience, notre shop sera mis en ligne d\'ici peu.';
public errors: Errors = { errors: {} }; public errors: Errors = { errors: {} };
public lastJump: Jump = {} as Jump;
public destroyRef = inject(DestroyRef); public destroyRef = inject(DestroyRef);
public now = new Date(); public now = new Date();
constructor( constructor(
private _jumpsService: JumpsService private titleService: Title
//private _userService: UserService
) { } ) { }
ngOnInit() { ngOnInit() {
this._lastjump$ = this._jumpsService.getLastJump().pipe(takeUntilDestroyed(this.destroyRef)); this.titleService.setTitle(this.title);
this._lastjump = this._lastjump$.subscribe((jump) => {
this.lastJump = jump;
});
}
private _isToday(someDate: Date) {
return someDate.getDate() == this.now.getDate() &&
someDate.getMonth() == this.now.getMonth() &&
someDate.getFullYear() == this.now.getFullYear()
} }
} }
+4
View File
@@ -8,6 +8,10 @@ export * from './home/home.component';
export * from './jump/jump.component'; export * from './jump/jump.component';
export * from './jumps/jumps.component'; export * from './jumps/jumps.component';
export * from './logbook/logbook.component'; export * from './logbook/logbook.component';
export * from './page/page.component';
export * from './pages/pages.component';
export * from './product/product.component';
export * from './products/products.component';
export * from './profile/profile.component'; export * from './profile/profile.component';
export * from './qcm/qcm.component'; export * from './qcm/qcm.component';
export * from './settings'; export * from './settings';
+38 -38
View File
@@ -23,44 +23,6 @@
</div> </div>
<mat-divider class="my-3"></mat-divider> <mat-divider class="my-3"></mat-divider>
<mat-card> <mat-card>
<mat-card-header>
<mat-card-title>Types de sauts</mat-card-title>
</mat-card-header>
<mat-card-content class="mt-2">
@if(displayCharts) {
<div class="row">
<div class="col-xs-12">
<app-line-chart [headers]="seriesTypesHeader" [names]="seriesTypesName" [values]="seriesTypesRowCumulated" [colors]="seriesTypesColor" [legend]="true" [hideZero]="true"></app-line-chart>
</div>
</div>
<mat-divider class="mt-2 mb-3"></mat-divider>
<div class="row">
@for (item of getCategories(); track $index) {
<div class="col-lg-2 col-sm-4 col-xs-6">
<app-circle-chart [headers]="seriesHeader" [names]="seriesName" [values]="[item.count, (lastJump.numero-item.count)]" [color]="$index" [label]="item.categorie"></app-circle-chart>
</div>
}
@for (item of getModules(); track $index) {
<div class="col-lg-2 col-sm-4 col-xs-6">
<app-circle-chart [headers]="seriesHeader" [names]="seriesName" [values]="[item.count, (lastJump.numero-item.count)]" [color]="getModuleColor($index)" [label]="item.module"></app-circle-chart>
</div>
}
</div>
<mat-divider class="my-3"></mat-divider>
<!--
<div class="row">
<div class="col-xxl-6 col-xs-12">
<app-line-chart [headers]="seriesTypesHeader" [names]="seriesTypesName" [values]="seriesTypesRow" [colors]="seriesTypesColor" [legend]="true"></app-line-chart>
</div>
<div class="col-xxl-6 col-xs-12">
<app-line-chart [headers]="seriesTypesHeader" [names]="seriesTypesName" [values]="seriesTypesRowCumulated" [colors]="seriesTypesColor" [legend]="true"></app-line-chart>
</div>
</div>
-->
}
</mat-card-content>
</mat-card>
<mat-card class="mt-3">
<mat-card-header> <mat-card-header>
<mat-card-title>{{ subtitle }}</mat-card-title> <mat-card-title>{{ subtitle }}</mat-card-title>
</mat-card-header> </mat-card-header>
@@ -127,4 +89,42 @@
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<mat-card class="banner">
<mat-card-header>
<mat-card-title>Types de sauts</mat-card-title>
</mat-card-header>
<mat-card-content class="mt-2">
@if(displayCharts) {
<div class="row">
<div class="col-xs-12">
<app-line-chart [headers]="seriesTypesHeader" [names]="seriesTypesName" [values]="seriesTypesRowCumulated" [colors]="seriesTypesColor" [legend]="true" [hideZero]="true"></app-line-chart>
</div>
</div>
<mat-divider class="mt-2 mb-3"></mat-divider>
<div class="row">
@for (item of getCategories(); track $index) {
<div class="col-lg-2 col-sm-4 col-xs-6">
<app-circle-chart [headers]="seriesHeader" [names]="seriesName" [values]="[item.count, (lastJump.numero-item.count)]" [color]="$index" [label]="item.categorie"></app-circle-chart>
</div>
}
@for (item of getModules(); track $index) {
<div class="col-lg-2 col-sm-4 col-xs-6">
<app-circle-chart [headers]="seriesHeader" [names]="seriesName" [values]="[item.count, (lastJump.numero-item.count)]" [color]="getModuleColor($index)" [label]="item.module"></app-circle-chart>
</div>
}
</div>
<mat-divider class="my-3"></mat-divider>
<!--
<div class="row">
<div class="col-xxl-6 col-xs-12">
<app-line-chart [headers]="seriesTypesHeader" [names]="seriesTypesName" [values]="seriesTypesRow" [colors]="seriesTypesColor" [legend]="true"></app-line-chart>
</div>
<div class="col-xxl-6 col-xs-12">
<app-line-chart [headers]="seriesTypesHeader" [names]="seriesTypesName" [values]="seriesTypesRowCumulated" [colors]="seriesTypesColor" [legend]="true"></app-line-chart>
</div>
</div>
-->
}
</mat-card-content>
</mat-card>
</div> </div>
@@ -0,0 +1,12 @@
element.style {
margin: 1.5rem -1.5rem -1.5rem -1.5rem;
padding: 0.9rem 1.1rem;
border-radius: 0;
}
.banner {
background-color: #202020;
color: #ffffff;
margin: 1.5rem -1.5rem -1.5rem -1.5rem;
padding: 0.9rem 1.1rem;
border-radius: 0;
}
@@ -1,10 +1,10 @@
<h2 mat-dialog-title class="bg-navy-light border-bottom border-light-subtle d-flex mb-3 pt-3 pb-2"> <h2 mat-dialog-title class="bg-navy-light text-white border-bottom border-light-subtle d-flex mb-3 pt-3 pb-2">
Saut n° {{jump.numero}}<span class="flex-spacer"></span> Saut n° {{jump.numero}}<span class="flex-spacer"></span>
<mat-icon fontIcon="close" class="pointer" (click)="closeDialog()"></mat-icon> <mat-icon fontIcon="close" class="pointer" (click)="closeDialog()"></mat-icon>
</h2> </h2>
<mat-dialog-content> <mat-dialog-content>
<div class="container"> <div class="container">
<div class="d-flex border-bottom border-light-subtle text-light-info pb-2 mb-3"> <div class="d-flex border-bottom border-light-subtle pb-2 mb-3">
<div class="flex-fill"> <div class="flex-fill">
<h5>Le {{ jump.date | date: 'dd/MM/yyyy' }} à {{ jump.lieu }} {{ jump.zone ? jump.zone : jump.oaci }} :</h5> <h5>Le {{ jump.date | date: 'dd/MM/yyyy' }} à {{ jump.lieu }} {{ jump.zone ? jump.zone : jump.oaci }} :</h5>
</div> </div>
@@ -20,12 +20,12 @@
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12"> <div class="col-lg-6 col-md-12">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt class="text-light-info fw-normal">Aeronef:</dt> <dt>Aeronef:</dt>
<dd>{{ jump.aeronef }} {{ jump.imat }}</dd> <dd>{{ jump.aeronef }} {{ jump.imat }}</dd>
<dt class="text-light-info fw-normal">Voile:</dt> <dt>Voile:</dt>
<dd>{{ jump.voile }} {{ jump.taille }} ft<sup>2</sup></dd> <dd>{{ jump.voile }} {{ jump.taille }} ft<sup>2</sup></dd>
@if (jump.programme) { @if (jump.programme) {
<dt class="text-light-info fw-normal">Programme:</dt> <dt>Programme:</dt>
<dd>{{ jump.programme }}</dd> <dd>{{ jump.programme }}</dd>
} }
</dl> </dl>
@@ -33,13 +33,13 @@
<div class="col-lg-6 col-md-12"> <div class="col-lg-6 col-md-12">
<dl class="dl-horizontal"> <dl class="dl-horizontal">
@if (!jump.x2data) { @if (!jump.x2data) {
<dt class="text-light-info fw-normal">Hauteur:</dt> <dt>Hauteur:</dt>
<dd>{{ jump.hauteur }} m</dd> <dd>{{ jump.hauteur }} m</dd>
<dt class="text-light-info fw-normal">Déploiement:</dt> <dt>Déploiement:</dt>
<dd>{{ jump.deploiement }} m</dd> <dd>{{ jump.deploiement }} m</dd>
} }
@for (file of jump.files; track file) { @for (file of jump.files; track file) {
<dt class="text-light-info fw-normal">Fichier {{ file.type }}</dt> <dt>Fichier {{ file.type }}</dt>
<dd>{{ file.name }}</dd> <dd>{{ file.name }}</dd>
} }
</dl> </dl>
@@ -44,7 +44,7 @@
<mat-card> <mat-card>
<mat-card-header class="p-0"> <mat-card-header class="p-0">
<mat-expansion-panel class="w-100"> <mat-expansion-panel class="w-100">
<mat-expansion-panel-header class="bg-navy-light"> <mat-expansion-panel-header class="bg-accent">
<mat-panel-title>Filtrer les sauts</mat-panel-title> <mat-panel-title>Filtrer les sauts</mat-panel-title>
@if (jumpsCount < lastJump.numero) { @if (jumpsCount < lastJump.numero) {
<mat-panel-description> <mat-panel-description>
@@ -0,0 +1,30 @@
<div @flyInOut>
<div class="d-flex">
<div class="flex-fill">
<h1 class="mb-1">{{ article.title }}</h1>
</div>
<span class="flex-spacer"></span>
<div class="align-self-center me-2 text-end">
<span class="fs-5 text-primary">{{ article.createdAt | date: 'dd/MM/yyyy' }}</span>
</div>
</div>
<mat-divider class="my-3"></mat-divider>
<app-list-errors [errors]="errors"></app-list-errors>
<div class="d-flex">
{{ article.description }}
</div>
<div class="d-flex">
{{ article.body }}
</div>
@if (article.tagList) {
<mat-divider class="my-3"></mat-divider>
<div class="d-flex">
@for (tag of getArticleTags(); track tag) {
<span class="badge rounded-pill bg-accent mb-2 me-1 pe-3 py-1 fw-normal text-dark">
<mat-icon class="align-middle me-1" fontIcon="tag"></mat-icon>
{{ tag.name }}
</span>
}
</div>
}
</div>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageComponent } from './page.component';
describe('PageComponent', () => {
let component: PageComponent;
let fixture: ComponentFixture<PageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PageComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
+69
View File
@@ -0,0 +1,69 @@
import { Title } from '@angular/platform-browser';
import { trigger, state, style, animate, transition } from '@angular/animations';
import { Component, DestroyRef, inject, OnInit, OnDestroy } from '@angular/core';
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { DatePipe } from '@angular/common';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { Observable, Subscription } from 'rxjs';
import { ListErrorsComponent, } from 'src/app/components/shared';
import { Errors, Article, ArticlePageData } from 'src/app/core/models';
@Component({
selector: 'app-page',
standalone: true,
imports: [
DatePipe, RouterLink,
MatCardModule, MatDividerModule, MatIconModule,
ListErrorsComponent
],
templateUrl: './page.component.html',
styleUrl: './page.component.scss',
animations: [
trigger('flyInOut', [
state('in', style({ transform: 'translateY(0)' })),
transition('void => *', [
style({ transform: 'translateY(-100%)' }),
animate(200)
]),
transition('* => void', [
style({ transform: 'translateY(100%)' }),
animate(200)
])
])
]
})
export class PageComponent implements OnInit, OnDestroy {
private _data: Subscription = new Subscription();
public title = 'Shop bientôt disponible!';
public description = 'Encore un peu de patience, notre shop sera mis en ligne d\'ici peu.';
public errors: Errors = { errors: {} };
public destroyRef = inject(DestroyRef);
public article: Article = {} as Article;
constructor(
private route: ActivatedRoute,
private titleService: Title
) { }
ngOnInit() {
const data$: Observable<{ pageData: ArticlePageData }> = this.route.data as Observable<{ pageData: ArticlePageData }>;
this._data = data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data: { pageData: ArticlePageData }) => {
this.article = data.pageData.article;
this.title = this.article.title;
this.titleService.setTitle(this.article.title);
console.log(this.article);
});
}
ngOnDestroy() {
this._data.unsubscribe();
}
getArticleTags() {
return this.article.tagList;
}
}
@@ -0,0 +1 @@
<p>pages works!</p>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesComponent } from './pages.component';
describe('PagesComponent', () => {
let component: PagesComponent;
let fixture: ComponentFixture<PagesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PagesComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PagesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-pages',
standalone: true,
imports: [],
templateUrl: './pages.component.html',
styleUrl: './pages.component.scss'
})
export class PagesComponent {
}
@@ -0,0 +1 @@
<p>product works!</p>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProductComponent } from './product.component';
describe('ProductComponent', () => {
let component: ProductComponent;
let fixture: ComponentFixture<ProductComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ProductComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ProductComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-product',
standalone: true,
imports: [],
templateUrl: './product.component.html',
styleUrl: './product.component.scss'
})
export class ProductComponent {
}
@@ -0,0 +1 @@
<p>products works!</p>
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProductsComponent } from './products.component';
describe('ProductsComponent', () => {
let component: ProductsComponent;
let fixture: ComponentFixture<ProductsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ProductsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ProductsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-products',
standalone: true,
imports: [],
templateUrl: './products.component.html',
styleUrl: './products.component.scss'
})
export class ProductsComponent {
}
+104 -418
View File
@@ -1,14 +1,14 @@
<div class="banner light text-center mat-elevation-z2"> <div class="banner bg-accent mat-elevation-z2">
<div class="d-flex justify-content-center text-start"> <div class="d-flex mx-2 mx-md-3 mx-lg-4">
<img [src]="user.image" class="user-img" [alt]="user.username" /> <img [src]="user.image" class="user-img me-3" [alt]="user.username" />
<div class="ms-3"> <div class="flex-spacer">
<h4>{{ bannerTitle }}</h4> <h3 class="mb-0">{{bannerTitle}}</h3>
<button mat-raised-button color="warn" routerLink="/settings" class="ms-2"> <span class="fs-6">{{user.firstname}} {{user.lastname}}</span>
</div>
<div class="align-self-center text-end">
<button mat-raised-button color="warn" routerLink="/settings">
<mat-icon aria-label="Modifier le compte" fontIcon="settings"></mat-icon> {{ btnSettingsTitle }} <mat-icon aria-label="Modifier le compte" fontIcon="settings"></mat-icon> {{ btnSettingsTitle }}
</button> </button>
<button mat-raised-button color="warn" [routerLink]="['/settings', 'credentials']" class="ms-2">
<mat-icon aria-label="Modifier le compte" fontIcon="password"></mat-icon> {{ btnPasswordTitle }}
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -22,63 +22,60 @@
</div> </div>
<mat-divider class="my-3"></mat-divider> <mat-divider class="my-3"></mat-divider>
<app-list-errors [errors]="errors"></app-list-errors> <app-list-errors [errors]="errors"></app-list-errors>
<mat-card class="mt-4 bg-light text-black scaled"> <mat-card class="mt-4">
<mat-card-content> <mat-card-content>
<div class="text-center"> <div class="row mt-2">
<a href="/" class="d-block p-4"><img height="72" width="90" src="assets/images/vector/upt_logo.png" alt="Logo UPT"></a> <div class="col-lg-6 col-md-12">
<mat-divider class="border-primary w-50 mx-auto"></mat-divider> <dl class="dl-horizontal mb-0">
<h1 class="mt-3 mb-4"><span class="pt-1 pb-2 px-2">Vector 3 - <small class="text-muted">V306 Micron</small></span></h1> <dt>Nom:</dt>
<mat-divider class="border-primary w-50 mx-auto"></mat-divider> <dd>{{ user.lastname }}</dd>
</div> <dt>Prénom:</dt>
<div class="d-none d-lg-flex justify-content-center mt-5 mb-0"> <dd>{{ user.firstname }}</dd>
<img class="img-fluid canopy" id="canopy_NWG_01" src="assets/images/vector/v306_NWG_01.svg" alt="v306 Navy White Gold" style="width: 1260px; height: 433.125px;"> </dl>
</div> </div>
<div class="d-flex d-lg-none justify-content-center mt-5 mb-0"> <div class="col-lg-6 col-md-12">
<img class="img-fluid" src="assets/images/vector/v306_NWG_01.png" alt="v306 Navy White Gold preview"> <dl class="dl-horizontal mb-0">
</div> <dt>Email:</dt>
<div class="d-flex text-center mt-4 mb-0 py-4"> <dd>{{ user.email }}</dd>
<figure class="flex-fill mx-5"> <dt>Téléphone:</dt>
<img class="figure-img img-fluid img-thumbnail" src="assets/images/vector/ContainerA.png" alt="" title="ContainerA" height="138" width="120"> <dd>{{ user.phone }}</dd>
<figcaption class="figure-caption text-center fs-6">10</figcaption> </dl>
</figure> </div>
<figure class="flex-fill mx-5">
<img class="figure-img img-fluid img-thumbnail" src="assets/images/vector/ContainerB.png" alt="" title="ContainerB" height="138" width="120">
<figcaption class="figure-caption text-center fs-6">10</figcaption>
</figure>
<figure class="flex-fill mx-5">
<img class="figure-img img-fluid img-thumbnail" src="assets/images/vector/ContainerLength.png" alt="" title="ContainerLength" height="138" width="120">
<figcaption class="figure-caption text-center fs-6">15.5</figcaption>
</figure>
<figure class="flex-fill mx-5">
<img class="figure-img img-fluid img-thumbnail" src="assets/images/vector/Thickness.png" alt="" title="Thickness" height="138" width="120">
<figcaption class="figure-caption text-center fs-6">5</figcaption>
</figure>
</div> </div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<mat-card> <mat-card>
<mat-card-header class="bg-primary text-bg-primary rounded-top py-2 px-4">
<div class="flex-fill">
<mat-card-title>
Mes commandes
</mat-card-title>
</div>
<span class="flex-spacer"></span>
<div class="flex-fill"></div>
</mat-card-header>
<mat-card-content> <mat-card-content>
<table class="table table-dark table-striped table-condensed mat-elevation-z2"> <table class="table table-dark table-striped table-condensed mat-elevation-z2">
<thead> <thead>
<tr role="row" class="table-dark"> <tr role="row" class="table-dark">
<th class="align-middle w-25 ps-4"> <th class="align-middle w-25 ps-4">
<div class="text-left"> <div class="text-left">
Main Std Fit Date
</div> </div>
</th> </th>
<th class="align-middle w-25"> <th class="align-middle w-25">
<div class="text-left"> <div class="text-left">
Main Full Fit Numéro
</div> </div>
</th> </th>
<th class="align-middle w-25"> <th class="align-middle w-25">
<div class="text-left"> <div class="text-left">
Reserve Std Fit Statut
</div> </div>
</th> </th>
<th class="align-middle w-25"> <th class="align-middle w-25">
<div class="text-left"> <div class="text-left">
Reserve Full Fit Total
</div> </div>
</th> </th>
</tr> </tr>
@@ -86,391 +83,80 @@
<tbody> <tbody>
<tr role="row" class="align-middle"> <tr role="row" class="align-middle">
<td class="cell ps-4"> <td class="cell ps-4">
<span class="text-orange fw-bold">Crossfire 3 119</span><br> 20/04/2025 13:37
Epicene 130<br>
Extreme-JFX 94<br>
Fusion 120<br>
Horizon 135<br>
JFX2 90<br>
Mamba 104<br>
Neos 94<br>
Nitron 108<br>
Omega 119<br>
Omni 119<br>
Pilot 104<br>
<span class="text-red fw-bold">Sabre 2 107</span><br>
<span class="text-orange fw-bold">Safire 3 119</span><br>
Synergy 108<br>
Velocity 96<br>
Spectre 107<br>
Stiletto 107<br>
Storm 107<br>
Katana 107<br>
</td> </td>
<td class="cell"> <td class="cell">
<span class="text-teal fw-bold text-decoration-underline">Crossfire 3 129</span><br> #aa2025042013371
Epicene 150<br>
Extreme-VX 84<br>
Horizon 150<br>
Katana 120<br>
Neos 104<br>
Pulse 120<br>
<span class="text-orange fw-bold">Sabre 2 120</span><br>
<span class="text-cyan fw-bold">Safire 3 129</span><br>
Spectre 120<br>
Stiletto 120<br>
Valkyrie 96<br>
Valkyrie Hybrid 96<br>
Xaos-21 98<br>
Xaos-27 88<br>
</td> </td>
<td class="cell"> <td class="cell">
Icarus Reserve 109<br> <span class="text-green">Terminé</span>
Micro Raven 109<br>
Optimum 113<br>
PD 106<br>
R-Max 108<br>
Smart 99<br>
<span class="text-cyan fw-bold">Techno 115</span><br>
</td> </td>
<td class="cell"> <td class="cell">
<span class="text-teal fw-bold text-decoration-underline">Icarus Reserve 119</span><br> 56.00 €
Micro Raven 120<br> </td>
Nano 113<br> </tr>
PD 113<br> <tr role="row" class="align-middle">
R-Max 118<br> <td class="cell ps-4">
Smart 110<br> 20/04/2025 13:42
Speed 120<br> </td>
Tempo 120<br> <td class="cell">
#aa2025042013421
</td>
<td class="cell">
<span class="text-teal">Expédié</span>
</td>
<td class="cell">
224.70 €
</td>
</tr>
<tr role="row" class="align-middle">
<td class="cell ps-4">
20/04/2025 13:54
</td>
<td class="cell">
#aa2025042013541
</td>
<td class="cell">
<span class="text-red">Annulé</span>
</td>
<td class="cell">
128.80 €
</td>
</tr>
<tr role="row" class="align-middle">
<td class="cell ps-4">
14/06/2025 13:37
</td>
<td class="cell">
#aa2025061413371
</td>
<td class="cell">
<span class="text-cyan">En cours de préparation</span>
</td>
<td class="cell">
195.30 €
</td>
</tr>
<tr role="row" class="align-middle">
<td class="cell ps-4">
07/07/2025 14:39
</td>
<td class="cell">
#aa2025070714391
</td>
<td class="cell">
<span class="text-orange">En attente de paiement</span>
</td>
<td class="cell">
64.00 €
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>
<mat-card>
<mat-card-content>
<div class="row mx-0 mt-4 mb-0">
<div class="col-sm-6 col-xs-12 ps-5 py-2">
<div class="card mb-3">
<div class="card-body">
<h5 class="mb-3">RISERS</h5>
<div class="mb-1 row">
<label for="rislength" class="col-sm-4 col-form-label">Length:</label>
<div class="col-sm-8">
<select name="rislength" class="form-select" id="rislength">
<option value="">Select...</option>
<option value="19 inch">19 inch</option>
<option value="21 inch" selected="selected">21 inch</option>
<option value="23 inch">23 inch</option>
<option value="25 inch">25 inch</option>
</select>
</div>
</div>
<div class="mb-1 row">
<label for="riscolor" class="col-sm-4 col-form-label">Color:</label>
<div class="col-sm-8">
<input class="form-control" id="riscolor" type="text" placeholder="Default input" value="SMK">
</div>
</div>
<div class="mb-1 row">
<label for="webtype" class="col-sm-4 col-form-label">Web type / ring:</label>
<div class="col-sm-8">
<select name="webtype" class="form-select" id="webtype" onchange="javascript:checkLowDrag()">
<option value="Mini ring w/ type 17 webbing" selected="selected">Mini ring w/ type 17 webbing</option>
<option value="Large ring w/ type 8 webbing">Large ring w/ type 8 webbing</option>
</select>
</div>
</div>
<div class="mb-1 row">
<span class="col-sm-4 col-form-label">&nbsp;</span>
<div class="col-sm-8">
<input class="form-check-input" type="checkbox" name="blkHardware" id="blkHardware" value="Y" checked="checked">
<label class="form-check-label" for="blkHardware">Black Hardware</label>
</div>
</div>
<div class="mb-1 row">
<label for="toggles" class="col-sm-4 col-form-label">Toggles:</label>
<div class="col-sm-8">
<select name="toggles" class="form-select" id="toggles">
<option value="TRULOK" selected="selected">TRULOK</option>
<option value="VELCRO">VELCRO</option>
</select>
</div>
</div>
<div class="mb-1 row">
<label for="louie" class="col-sm-4 col-form-label">LouieLoops:</label>
<div class="col-sm-8">
<select name="louie" class="form-select" id="louie" onchange="javascript:checkLowDrag()">
<option value="No">No</option>
<option value="Yes" selected="selected">Yes</option>
</select>
</div>
</div>
<div class="mb-1 row">
<label for="lowdrag" class="col-sm-4 col-form-label">Low drag:</label>
<div class="col-sm-8">
<select name="lowdrag" class="form-select" id="lowdrag">
<option value="No" selected="selected">No</option>
<option value="Yes">Yes</option>
</select>
</div>
</div>
<div class="mb-1 row">
<label for="rsl" class="col-sm-4 col-form-label">Skyhook / RSL:</label>
<div class="col-sm-8">
<select name="rsl" class="form-select" id="rsl" onchange="javascript:checkSpecRip()">
<option value="">Select...</option>
<option value="Skyhook RSL">Skyhook RSL</option>
<option value="RSL" selected="selected">Reserve static line lanyard (RSL)</option>
<option value="none">None</option>
</select>
</div>
</div>
<div class="mb-1 row">
<span class="col-sm-4 col-form-label">&nbsp;</span>
<div class="col-sm-8">
<input class="form-check-input" type="checkbox" name="magnetic_chx" id="magnetic_chx" value="Y" checked="checked">
<label class="form-check-label" for="magnetic_chx">Magnetic riser covers</label>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h5 class="mb-3">RESERVE HANDLE</h5>
<div class="mb-1 row">
<label for="reshand" class="col-sm-4 col-form-label">Handle type:</label>
<div class="col-sm-8">
<select name="reshand" class="form-select" id="reshand">
<option value="">Select...</option>
<option value="Metal D handle">Metal D handle</option>
<option value="Low profile D handle">Low profile D handle</option>
<option value="Mini-D handle">Mini-D handle</option>
<option value="Soft handle" selected="selected">Soft handle - GOL (extra cost)</option>
</select>
</div>
</div>
<div class="mb-1 row">
<span class="col-sm-4 col-form-label">&nbsp;</span>
<div class="col-sm-8">
<input class="form-check-input" type="checkbox" name="spectraRip_chx" id="spectraRip_chx" value="Y" checked="checked">
<label class="form-check-label" for="spectraRip_chx">Spectra ripcord</label>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h5 class="mb-3">PILOT CHUTE</h5>
<div class="mb-1 row">
<label for="selpc" class="col-sm-4 col-form-label">Material:</label>
<div class="col-sm-8">
<select name="selpc" class="form-select" id="selpc">
<option value="">Select...</option>
<option value="F111 (white)" selected="selected">F111 (white)</option>
<option value="ZP (black)">ZP (black)</option>
</select>
</div>
</div>
<div class="mb-1 row">
<label for="handle" class="col-sm-4 col-form-label">Handle:</label>
<div class="col-sm-8">
<select name="handle" class="form-select" id="handle" onchange="javascript:getPCHoptions()">
<option value="">Select...</option>
<option value="PLASTIC">PLASTIC</option>
<option value="HACKEY">HACKEY</option>
<option value="FREEFLY" selected="selected">FREEFLY</option>
<option value="FREEFLY HACKEY">FREEFLY HACKEY</option>
<option value="MONKEY FIST">MONKEY FIST</option>
<option value="FREEFLY MONKEY FIST">FREEFLY MONKEY FIST</option>
<option value="PULLOUT">PULLOUT</option>
</select>
</div>
</div>
<div class="mb-1 row">
<label for="color1" class="col-sm-4 col-form-label">Handle color:</label>
<div class="col-sm-8">
<select name="color1" class="form-select" id="color1">
<option value="">Select...</option>
<option value="WHT">White</option>
<option value="BLK">Black</option>
<option value="SMK">Smoke</option>
<option value="SIL">Silver</option>
<option value="NeYEL">Neon yellow</option>
<option value="YEL">Yellow</option>
<option value="GOL">Gold</option>
<option value="NeORA">Neon orange</option>
<option value="ORA">Orange</option>
<option value="NePNK">Neon pink</option>
<option value="PNK">Pink</option>
<option value="RED">Red</option>
<option value="BUR">Burgundy</option>
<option value="PUR">Purple</option>
<option value="LBL">Light blue</option>
<option value="NeBLU">Neon blue</option>
<option value="RBL">Royal blue</option>
<option value="NBL" selected="selected">Navy blue</option>
<option value="TEAL">Teal</option>
<option value="NeGRN">Neon green</option>
<option value="KGR">Kelly green</option>
<option value="FGR">Forest green</option>
<option value="ODG">Olive Drab</option>
<option value="BRN">Brown</option>
<option value="TAN">Tan</option>
<option value="COY">Coyote</option>
<option value="CAMO_WO">Camo Woodland</option>
<option value="CAMO_DE">Camo Desert</option>
<option value="CAMO_DD">Camo Desert Digital</option>
<option value="CAMO_MUL">Camo Multicam</option>
<option value="CAMO_BLMUL">Camo Black Multicam</option>
<option value="CAMO_MDD">Marpat Desert Digital</option>
<option value="CAMO_MWD">Marpat Woodland digital</option>
<option value="KRYP_MAND">Kryptek Mandrake</option>
<option value="KRYP_HIGH">Kryptek Highlander</option>
<option value="KRYP_RAID">Kryptek Raid</option>
<option value="KRYP_TYPH">Kryptek Typhon</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xs-12 ps-5 py-2">
<div class="card mb-3">
<div class="card-body">
<h5 class="mb-3">MAIN DEPLOYMENT</h5>
<div class="mb-1 row">
<label for="maindeploy" class="col-sm-3 col-form-label">Type:</label>
<div class="col-sm-9">
<select name="maindeploy" class="form-select" id="maindeploy">
<option value="Bottom of container" selected="selected">Bottom of container</option>
<option value="PULLOUT">Pullout</option>
</select>
</div>
</div>
<div class="mb-1 row">
<span class="col-sm-3 col-form-label">&nbsp;</span>
<div class="col-sm-9">
<input class="form-check-input" type="checkbox" name="semistowless" id="semistowless" value="Y">
<label class="form-check-label" for="semistowless">V-Stow bag (Up to size V358) (extra cost)</label>
</div>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h5 class="mb-3">BACK &amp; LEG PADS</h5>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="deluxe" id="deluxe" value="Y" checked="checked">
<label class="form-check-label" for="deluxe">Deluxe backpad</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="3dbackleg" id="3dbackleg" value="Y" checked="checked">
<label class="form-check-label" for="3dbackleg">3D Spacer Foam backpad/legpads (extra cost)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="3dstab" id="3dstab" value="Y" checked="checked">
<label class="form-check-label" for="3dstab">3D Spacer Foam padded stabilizers (extra cost)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="vgh" id="vgh" value="Y" checked="checked">
<label class="form-check-label" for="vgh">Variable Geometry Harness (hip rings)</label>
</div>
<div class="mb-1 row">
<label for="selvgh" class="col-sm-3 col-form-label">&nbsp;</label>
<div class="col-sm-9">
<select name="selvgh" class="form-select" id="selvgh">
<option value="Two piece leg pad" selected="selected">Two piece leg pad</option>
<option value="One piece leg pad">One piece leg pad</option>
</select>
</div>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="rolledlegpad" id="rolledlegpad" value="Y" checked="checked">
<label class="form-check-label" for="rolledlegpad">Rolled edge leg pad (extra cost)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="hookknife" id="hookknife" value="Y" checked="checked">
<label class="form-check-label" for="hookknife">Hook knife (extra cost)</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="freeflymod" id="freeflymod" value="Y" checked="checked">
<label class="form-check-label" for="freeflymod">Freefly mod (extra cost)</label>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h5 class="mb-3">WINGSUIT OPTIONS</h5>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="wspc30" id="wspc30" value="Y" checked="checked">
<label class="form-check-label" for="wspc30">30" Pilot chute</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="wsbridle" id="wsbridle" value="Y" checked="checked">
<label class="form-check-label" for="wsbridle">Wingsuit bridle length</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="wscorners" id="wscorners" value="Y" checked="checked">
<label class="form-check-label" for="wscorners">Dynamic wingsuit corners (extra cost)</label>
</div>
</div>
</div>
</div>
</div>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-content>
<div class="row mx-0 mt-4 mb-0">
<div class="col-sm-6 col-xs-12 ps-5 py-2">
<dl class="row">
<dt class="col-md-8 col-sm-10 col-xs-6">UPT couleurs séparées (sauf V) :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">Bouclerie :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Black</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT anneaux cuissardes :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT harnais réglable :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT Skyhook :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT Stevens :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT dosseret mousse 3D :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT dosseret luxe :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT lombaires mousse 3D :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT mode FF :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT caches élévateurs aimantés :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT Kill line :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT boule cuir :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT poignée FF :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT poignée HD corde :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT poignée secours mousse :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT poignées sur avants :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
</dl>
</div>
<div class="col-sm-6 col-xs-12 ps-5 py-2">
<dl class="row">
<dt class="col-md-8 col-sm-10 col-xs-6">UPT pull out :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT poignée de transport :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT Liserets sur rabat central :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT logo supp. plastron :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-checked me-2"></i> Oui</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT logo supp. sur côté :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT logo sur rabat secours :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT nom brodé :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT POD sans élastiques :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<!-- <dt class="col-md-8 col-sm-10 col-xs-6">UPT Coupe suspente alu :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd> -->
<dt class="col-md-8 col-sm-10 col-xs-6">UPT Coupe suspente alu :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT coupe suspente plast. :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT broderie perso (à partir de) :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT galon contrasté :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT JC coutures libres :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT Design special :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-unchecked me-2"></i> Non</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">UPT Coins libres :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><span class="text-muted"><i class="icosvg-checkbox-checked me-2"></i> Oui</span></dd>
<dt class="col-md-8 col-sm-10 col-xs-6">Rolled edge legpad :</dt><dd class="col-md-4 col-sm-2 col-xs-6"><i class="icosvg-checkbox-checked me-2"></i> Oui</dd>
</dl>
</div>
</div>
</mat-card-content>
</mat-card>
</div> </div>
<mat-divider class="mx-4"></mat-divider> <mat-divider class="my-4"></mat-divider>
<!-- <!--
turquoise turquoise
cyan cyan
@@ -482,13 +168,13 @@ yellow
--> -->
<div class="content"> <div class="content">
<mat-card> <mat-card>
<mat-card-header class="bg-navy rounded-top py-3 px-4"> <mat-card-header class="bg-primary text-bg-primary rounded-top py-3 px-4">
<div class="flex-fill"> <div class="flex-fill">
<mat-card-title> <mat-card-title>
Buttons variants Buttons variants
</mat-card-title> </mat-card-title>
<mat-card-subtitle class="fw-normal"> <mat-card-subtitle class="fw-normal text-white">
turquoise / cyan / navy / purple / raspberry / orange / yellow turquoise / cyan / navy / purple / raspberry / orange / yellow / megna / magenta / pink
</mat-card-subtitle> </mat-card-subtitle>
</div> </div>
<span class="flex-spacer"></span> <span class="flex-spacer"></span>
@@ -3,7 +3,7 @@
.banner { .banner {
background-color: var.$sidebar-footer; background-color: var.$sidebar-footer;
padding: 1.6rem 0 1.3rem 0; padding: 1.3rem 0 1.1rem 0;
margin: -1.5rem -1.5rem 1.5rem -1.5rem; margin: -1.5rem -1.5rem 1.5rem -1.5rem;
.user-img { .user-img {
width: var.$user-img-size; width: var.$user-img-size;
@@ -30,7 +30,6 @@ export class ProfileComponent implements OnInit, OnDestroy {
user: Partial<User> = {} as Partial<User>; user: Partial<User> = {} as Partial<User>;
errors: Errors = { errors: {} }; errors: Errors = { errors: {} };
btnSettingsTitle = 'Paramètres'; btnSettingsTitle = 'Paramètres';
btnPasswordTitle = 'Changer le mot de passe';
constructor( constructor(
private titleService: Title, private titleService: Title,
@@ -1,8 +1,11 @@
<div class="banner bg-navy light text-center mat-elevation-z2"> <div class="banner bg-accent mat-elevation-z2">
<div class="d-flex justify-content-center text-start"> <div class="d-flex mx-2 mx-md-3 mx-lg-4">
<img [src]="user.image" class="user-img" [alt]="user.username" /> <img [src]="user.image" class="user-img me-3" [alt]="user.username" />
<div class="ms-3"> <div class="flex-spacer">
<h4>{{ bannerTitle }}</h4> <h3 class="mb-0">{{bannerTitle}}</h3>
<span class="fs-6">{{user.firstname}} {{user.lastname}}</span>
</div>
<div class="align-self-center text-end">
<button mat-raised-button color="cyan" [routerLink]="['/profile', user.username]"> <button mat-raised-button color="cyan" [routerLink]="['/profile', user.username]">
<mat-icon aria-label="Voir le compte" fontIcon="person"></mat-icon> Mon compte <mat-icon aria-label="Voir le compte" fontIcon="person"></mat-icon> Mon compte
</button> </button>
@@ -18,7 +21,7 @@
<app-list-errors [errors]="errors"></app-list-errors> <app-list-errors [errors]="errors"></app-list-errors>
<div class="container page mt-4"> <div class="container page mt-4">
<div class="row"> <div class="row">
<div class="col-md-6 offset-md-3 col-xs-12"> <div class="col-lg-8 offset-lg-2 col-xl-6 offset-xl-3 col-xs-12">
<form class="row row-cols-lg-auto" [formGroup]="credentialsForm" (ngSubmit)="submitForm()"> <form class="row row-cols-lg-auto" [formGroup]="credentialsForm" (ngSubmit)="submitForm()">
<fieldset [disabled]="isSubmitting" class="w-100"> <fieldset [disabled]="isSubmitting" class="w-100">
<legend [hidden]="true">Mise à jour du mot de passe</legend> <legend [hidden]="true">Mise à jour du mot de passe</legend>
@@ -3,7 +3,7 @@
.banner { .banner {
background-color: var.$sidebar-footer; background-color: var.$sidebar-footer;
padding: 1.6rem 0 1.3rem 0; padding: 1.3rem 0 1.1rem 0;
margin: -1.5rem -1.5rem 1.5rem -1.5rem; margin: -1.5rem -1.5rem 1.5rem -1.5rem;
.user-img { .user-img {
width: var.$user-img-size; width: var.$user-img-size;
@@ -1,14 +1,14 @@
<div class="banner bg-navy light text-center mat-elevation-z2"> <div class="banner bg-accent mat-elevation-z2">
<div class="d-flex justify-content-center text-start"> <div class="d-flex mx-2 mx-md-3 mx-lg-4">
<img [src]="user.image" class="user-img" [alt]="user.username" /> <img [src]="user.image" class="user-img me-3" [alt]="user.username" />
<div class="ms-3"> <div class="flex-spacer">
<h4>{{ bannerTitle }}</h4> <h3 class="mb-0">{{bannerTitle}}</h3>
<span class="fs-6">{{user.firstname}} {{user.lastname}}</span>
</div>
<div class="align-self-center text-end">
<button mat-raised-button color="cyan" [routerLink]="['/profile', user.username]"> <button mat-raised-button color="cyan" [routerLink]="['/profile', user.username]">
<mat-icon aria-label="Voir le compte" fontIcon="person"></mat-icon> Mon compte <mat-icon aria-label="Voir le compte" fontIcon="person"></mat-icon> Mon compte
</button> </button>
<button mat-raised-button color="warn" [routerLink]="['/settings', 'credentials']" class="ms-2">
<mat-icon aria-label="Modifier le compte" fontIcon="password"></mat-icon> {{ btnPasswordTitle }}
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -18,10 +18,22 @@
<app-list-errors [errors]="errors"></app-list-errors> <app-list-errors [errors]="errors"></app-list-errors>
<div class="container page mt-4"> <div class="container page mt-4">
<div class="row"> <div class="row">
<div class="col-md-6 offset-md-3 col-xs-12"> <div class="col-lg-8 offset-lg-2 col-xl-6 offset-xl-3 col-xs-12">
<form class="row row-cols-lg-auto" [formGroup]="settingsForm" (ngSubmit)="submitForm()"> <form class="row row-cols-lg-auto" [formGroup]="settingsForm" (ngSubmit)="submitForm()">
<fieldset [disabled]="isSubmitting" class="w-100"> <fieldset [disabled]="isSubmitting" class="w-100">
<legend [hidden]="true">Informations personnelles</legend> <legend [hidden]="true">Informations personnelles</legend>
<div class="col-12">
<mat-form-field appearance="fill" class="w-100">
<mat-label>Nom d'utilisateur</mat-label>
<input matInput type="text" placeholder="Choisissez un nom d'utilisateur" formControlName="username">
</mat-form-field>
</div>
<div class="col-12">
<mat-form-field appearance="fill" class="w-100">
<mat-label>eMail</mat-label>
<input matInput readonly type="email" placeholder="Indiquez votre adresse email" formControlName="email">
</mat-form-field>
</div>
<div class="col-12"> <div class="col-12">
<mat-form-field appearance="fill" class="w-100"> <mat-form-field appearance="fill" class="w-100">
<mat-label>Nom</mat-label> <mat-label>Nom</mat-label>
@@ -40,48 +52,33 @@
<input matInput type="tel" placeholder="Indiquez votre numéro de téléphone" formControlName="phone"> <input matInput type="tel" placeholder="Indiquez votre numéro de téléphone" formControlName="phone">
</mat-form-field> </mat-form-field>
</div> </div>
<div class="col-12">
<mat-form-field appearance="fill" class="w-100">
<mat-label>Licence</mat-label>
<input matInput type="number" placeholder="Indiquez le un nom de votre société" formControlName="licence">
</mat-form-field>
</div>
<div class="col-12">
<mat-form-field appearance="fill" class="w-100">
<mat-label>Poids</mat-label>
<input matInput type="number" placeholder="Indiquez le poids en Kg" formControlName="poids">
</mat-form-field>
</div>
<div class="col-12"> <div class="col-12">
<mat-form-field appearance="fill" class="w-100"> <mat-form-field appearance="fill" class="w-100">
<mat-label>Photo de profil</mat-label> <mat-label>Photo de profil</mat-label>
<input matInput type="url" placeholder="URL de votre photo" formControlName="image"> <mat-select formControlName="image" [(value)]="user.image">
</mat-form-field> @for (avatar of avatars; track avatar; let idx = $index) {
</div> <mat-option [value]="avatar">
<div class="col-12"> <img src="{{ avatar }}" alt="Avatar {{ idx + 1 }}" class="avatar">
<mat-form-field appearance="fill" class="w-100"> <span>Image {{ idx + 1 }}</span>
<mat-label>Arrière plan</mat-label> </mat-option>
<input matInput type="url" placeholder="URL de votre arrière plan" formControlName="bg_image"> }
</mat-select>
</mat-form-field> </mat-form-field>
</div> </div>
</fieldset> </fieldset>
<fieldset [disabled]="isSubmitting" class="w-100"> <fieldset [disabled]="isSubmitting" class="w-100">
<legend [hidden]="true">Informations de connexion</legend> <legend [hidden]="true">Informations de connexion</legend>
<div class="col-12">
<mat-form-field appearance="fill" class="w-100">
<mat-label>Nom d'utilisateur</mat-label>
<input matInput type="text" placeholder="Choisissez un nom d'utilisateur" formControlName="username">
</mat-form-field>
</div>
<div class="col-12">
<mat-form-field appearance="fill" class="w-100">
<mat-label>eMail</mat-label>
<input matInput readonly type="email" placeholder="Indiquez votre adresse email" formControlName="email">
</mat-form-field>
</div>
<div class="col-12 clearfix"> <div class="col-12 clearfix">
<button mat-raised-button color="primary" [disabled]="!settingsForm.valid" type="submit" class="float-sm-end">{{ btnUpdateTitle }}</button> <mat-divider class="mb-3"></mat-divider>
<button mat-stroked-button color="warn" class="me-3" type="button" (click)="logout()">{{ btnLogoutTitle }}</button> <button mat-raised-button color="primary" [disabled]="!settingsForm.valid" type="submit" class="mb-2 float-end">
<i class="icosvg-floppy-disk"></i> {{ btnUpdateTitle }}
</button>
<button mat-stroked-button color="warn" class="me-2 mb-2" type="button" (click)="logout()">
<i class="icosvg-switch"></i> {{ btnLogoutTitle }}
</button>
<button mat-stroked-button color="orange" class="me-2 mb-2" type="button" [routerLink]="['/settings', 'credentials']">
<mat-icon aria-label="Modifier le compte" fontIcon="password"></mat-icon> {{ btnPasswordTitle }}
</button>
</div> </div>
</fieldset> </fieldset>
</form> </form>
@@ -3,7 +3,7 @@
.banner { .banner {
background-color: var.$sidebar-footer; background-color: var.$sidebar-footer;
padding: 1.6rem 0 1.3rem 0; padding: 1.3rem 0 1.1rem 0;
margin: -1.5rem -1.5rem 1.5rem -1.5rem; margin: -1.5rem -1.5rem 1.5rem -1.5rem;
/* /*
background-color: mat.get-color-from-palette(var.$md-custom-warn, 700); background-color: mat.get-color-from-palette(var.$md-custom-warn, 700);
@@ -33,6 +33,17 @@
font-weight: 300; font-weight: 300;
} }
} }
.settings-page > .container {
margin-top: 2.4rem;
margin-bottom: 2.6rem;
padding-bottom: 1rem;
}
img.avatar {
max-height: 32px;
margin-right: 8px;
}
/* Chrome, Safari, Edge, Opera */ /* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button, input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button { input::-webkit-inner-spin-button {
@@ -8,6 +8,7 @@ import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
@@ -20,7 +21,7 @@ import { ListErrorsComponent } from 'src/app/components/shared';
standalone: true, standalone: true,
imports: [ imports: [
RouterLink, FormsModule, ReactiveFormsModule, RouterLink, FormsModule, ReactiveFormsModule,
MatButtonModule, MatIconModule, MatFormFieldModule, MatInputModule, MatDividerModule, MatButtonModule, MatIconModule, MatDividerModule, MatFormFieldModule, MatInputModule, MatSelectModule,
ListErrorsComponent ListErrorsComponent
], ],
styleUrl: './settings.component.scss', styleUrl: './settings.component.scss',
@@ -38,6 +39,18 @@ export class SettingsComponent implements OnInit, OnDestroy {
errors: Errors = { errors: {} }; errors: Errors = { errors: {} };
isSubmitting = false; isSubmitting = false;
destroyRef = inject(DestroyRef); destroyRef = inject(DestroyRef);
imgPath = '/assets/images/avatars/';
imgExt = '.jpg';
avatars = [
`${this.imgPath}animal_001${this.imgExt}`, `${this.imgPath}animal_002${this.imgExt}`,
`${this.imgPath}animal_003${this.imgExt}`, `${this.imgPath}animal_004${this.imgExt}`,
`${this.imgPath}animal_005${this.imgExt}`, `${this.imgPath}animal_006${this.imgExt}`,
`${this.imgPath}animal_007${this.imgExt}`, `${this.imgPath}animal_008${this.imgExt}`,
`${this.imgPath}animal_009${this.imgExt}`, `${this.imgPath}animal_010${this.imgExt}`,
`${this.imgPath}animal_011${this.imgExt}`, `${this.imgPath}animal_012${this.imgExt}`,
`${this.imgPath}animal_013${this.imgExt}`, `${this.imgPath}animal_014${this.imgExt}`,
`${this.imgPath}animal_015${this.imgExt}`, `${this.imgPath}animal_016${this.imgExt}`
];
constructor( constructor(
private router: Router, private router: Router,
@@ -52,10 +65,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
firstname: ['', Validators.required], firstname: ['', Validators.required],
lastname: ['', Validators.required], lastname: ['', Validators.required],
phone: ['', Validators.required], phone: ['', Validators.required],
licence: '', image: ''
poids: '',
image: '',
bg_image: ''
}); });
// Optional: subscribe to changes on the form : // Optional: subscribe to changes on the form :
// this.settingsForm.valueChanges.pipe(this._scavenger.collect()).subscribe(values => this.updateUser(values)); // this.settingsForm.valueChanges.pipe(this._scavenger.collect()).subscribe(values => this.updateUser(values));
@@ -94,7 +104,10 @@ export class SettingsComponent implements OnInit, OnDestroy {
submitForm() { submitForm() {
this.isSubmitting = true; this.isSubmitting = true;
// update the model /* Update the model */
if (this.avatars.indexOf(this.settingsForm.value.image) === -1) {
this.settingsForm.value.image = `${this.imgPath}animal_001${this.imgExt}`;
}
this.updateUser(this.settingsForm.value); this.updateUser(this.settingsForm.value);
const user$: Observable<{ user: User }> = this.userService.update(this.user).pipe(takeUntilDestroyed(this.destroyRef)); const user$: Observable<{ user: User }> = this.userService.update(this.user).pipe(takeUntilDestroyed(this.destroyRef));
@@ -106,67 +106,75 @@
</mat-menu> </mat-menu>
</div> </div>
<mat-nav-list appAccordion> <mat-nav-list appAccordion>
@for (menuitem of menuItems.getMenuItems(); track menuitem) { @for (menuitem of getMenuItems(); track menuitem) {
<mat-list-item appAccordionLink routerLinkActive="selected" group="{{menuitem.state}}"> <mat-list-item appAccordionLink routerLinkActive="selected" group="{{menuitem.state}}">
@if (menuitem.type === 'link') { @if (!menuitem.parent && menuitem.type === 'link') {
<a appAccordionToggle [routerLink]="['/', menuitem.state]"> <a appAccordionToggle [routerLink]="['/', menuitem.state]">
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 mr-2"></mat-icon> @if (menuitem.icontype === 'mat') {
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 me-2"></mat-icon>
}
@else {
<i class="icosvg-{{menuitem.icon}} fs-3 mt-0 me-3"></i>
}
<span>{{ menuitem.name }}</span> <span>{{ menuitem.name }}</span>
<span class="flex-spacer"></span> <span class="flex-spacer"></span>
@for (badge of menuitem.badge; track badge) { @for (badge of menuitem.badge; track badge) {
<span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span> <span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span>
} }
</a> </a>
} }
@if (menuitem.type === 'menu') { @if (menuitem.type === 'menu') {
<a role="button" aria-hidden="true" (click)="toggleShowQCMMenu()" (keyup)="true"> <a role="button" aria-hidden="true" (click)="toggleShowMenu(menuitem.state)" (keyup)="true">
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 mr-2"></mat-icon> @if (menuitem.icontype === 'mat') {
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 me-2"></mat-icon>
}
@else {
<i class="icosvg-{{menuitem.icon}} fs-3 mt-0 me-3"></i>
}
<span>{{ menuitem.name }}</span> <span>{{ menuitem.name }}</span>
<span class="flex-spacer"></span> <span class="flex-spacer"></span>
@for (badge of menuitem.badge; track badge) { @for (badge of menuitem.badge; track badge) {
<span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span> <span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span>
} }
</a> </a>
} }
</mat-list-item> </mat-list-item>
} @if (menuitem.type === 'menu') {
@if (showQCMMenu) { @for (submenuitem of getMenuItems(); track submenuitem) {
<div> @if ( isVisibleMenu(menuitem.state) === true && submenuitem.parent === menuitem.state && submenuitem.type === 'link') {
@for (menuqcm of menuItems.getMenuQCM(); track menuqcm) { <mat-list-item appAccordionLink routerLinkActive="selected" group="{{ submenuitem.state}}">
<mat-list-item appAccordionLink routerLinkActive="selected" group="{{ menuqcm.state}}"> <a class="ps-4" appAccordionToggle [routerLink]="['/' + submenuitem.parent, submenuitem.state]">
@if ( menuqcm.type === 'link') { @if (menuitem.icontype === 'mat') {
<a class="ps-4" appAccordionToggle [routerLink]="['/qcm', menuqcm.state]"> <mat-icon [fontIcon]="submenuitem.icon" class="mt-0 me-2"></mat-icon>
<mat-icon [fontIcon]="menuqcm.icon" class="mt-0 mr-2"></mat-icon> }
<span>{{ menuqcm.name }}</span> @else {
<span class="flex-spacer"></span> <i class="icosvg-{{submenuitem.icon}} fs-2 mt-0 me-2"></i>
</a> }
<span>{{ submenuitem.name }}</span>
<span class="flex-spacer"></span>
</a>
</mat-list-item>
} }
</mat-list-item>
} }
</div>
} }
}
</mat-nav-list> </mat-nav-list>
</ng-container> </ng-container>
<ng-container *appShowAuthed="false"> <ng-container *appShowAuthed="false">
<div class="user-profile"> <div class="user-profile">
<!-- <div class="profile-img"> <img src="/assets/images/users/user.jpg" alt="user"> </div> --> <!-- <div class="profile-img"> <img src="/assets/images/avatars/default.jpg" alt="user"> </div> -->
</div> </div>
<mat-nav-list appAccordion> <mat-nav-list appAccordion>
@for (menuitem of menuItems.getMenuAuth(); track menuitem) { @for (menuitem of menuItems.getMenuAuth(); track menuitem) {
<mat-list-item appAccordionLink routerLinkActive="selected" group="{{menuitem.state}}"> <mat-list-item appAccordionLink routerLinkActive="selected" group="{{menuitem.state}}">
@if (menuitem.type === 'link') { @if (menuitem.type === 'link') {
<a appAccordionToggle [routerLink]="['/', menuitem.state]"> <a appAccordionToggle [routerLink]="['/', menuitem.state]">
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 mr-2"></mat-icon> @if (menuitem.icontype === 'mat') {
<span>{{ menuitem.name }}</span> <mat-icon [fontIcon]="menuitem.icon" class="mt-0 me-2"></mat-icon>
<span class="flex-spacer"></span> }
@for (badge of menuitem.badge; track badge) { @else {
<span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span> <i class="icosvg-{{menuitem.icon}} fs-2 mt-0 me-2"></i>
} }
</a>
}
@if (menuitem.type === 'menu') {
<a role="button" aria-hidden="true" (click)="showQCMMenu = !showQCMMenu">
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 mr-2"></mat-icon>
<span>{{ menuitem.name }}</span> <span>{{ menuitem.name }}</span>
<span class="flex-spacer"></span> <span class="flex-spacer"></span>
@for (badge of menuitem.badge; track badge) { @for (badge of menuitem.badge; track badge) {
@@ -176,21 +184,6 @@
} }
</mat-list-item> </mat-list-item>
} }
@if (showQCMMenu) {
<div>
@for (menuqcm of menuItems.getMenuQCM(); track menuqcm) {
<mat-list-item appAccordionLink routerLinkActive="selected" group="{{ menuqcm.state}}">
@if ( menuqcm.type === 'link') {
<a class="ps-4" appAccordionToggle [routerLink]="['/', menuqcm.state]">
<mat-icon [fontIcon]="menuqcm.icon" class="mt-0 mr-2"></mat-icon>
<span>{{ menuqcm.name }}</span>
<span class="flex-spacer"></span>
</a>
}
</mat-list-item>
}
</div>
}
</mat-nav-list> </mat-nav-list>
</ng-container> </ng-container>
<app-layout-footer></app-layout-footer> <app-layout-footer></app-layout-footer>
@@ -11,7 +11,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { User } from 'src/app/core/models'; import { Menu, User } from 'src/app/core/models';
import { UserService } from 'src/app/core/services'; import { UserService } from 'src/app/core/services';
import { MenuItems, ShowAuthedDirective, AccordionAnchorDirective, AccordionLinkDirective, AccordionDirective } from 'src/app/components/shared'; import { MenuItems, ShowAuthedDirective, AccordionAnchorDirective, AccordionLinkDirective, AccordionDirective } from 'src/app/components/shared';
//import { SpinnerComponent } from 'src/app/components/shared'; //import { SpinnerComponent } from 'src/app/components/shared';
@@ -43,10 +43,12 @@ export class FullComponent implements OnInit, OnDestroy {
private _mobileQueryListener: () => void; private _mobileQueryListener: () => void;
mobileQuery: MediaQueryList; mobileQuery: MediaQueryList;
currentUser: User = {} as User; currentUser: User = {} as User;
showQCMMenu = false;
today: number = Date.now(); today: number = Date.now();
siteLink = 'https://www.rampeur.com'; siteLink = 'https://www.adastra-cbd.com';
author = 'Solide Apps'; author = 'Ad Astra';
links: Menu[] = [];
visibleMenu: string[] = ['products', 'page'];
//visibleMenu: string[] = ['products'];
constructor( constructor(
private router: Router, private router: Router,
@@ -65,10 +67,29 @@ export class FullComponent implements OnInit, OnDestroy {
this._currentUser = user$.subscribe( this._currentUser = user$.subscribe(
(userData) => { (userData) => {
this.currentUser = userData; this.currentUser = userData;
if (this.currentUser.role === 'Admin') {
//this.links = this.menuItems.getMenuItemsAdmin();
this.links = this.menuItems.getMenuItems();
} else {
this.links = this.menuItems.getMenuItems();
}
} }
); );
} }
getMenuItems(): Menu[] {
return this.links;
}
isVisibleMenu(state: string): boolean {
const indexOf: number = this.visibleMenu.indexOf(state);
if (indexOf !== -1) {
return true;
} else {
return false;
}
}
ngOnDestroy(): void { ngOnDestroy(): void {
this._currentUser.unsubscribe(); this._currentUser.unsubscribe();
this.mobileQuery.removeListener(this._mobileQueryListener); this.mobileQuery.removeListener(this._mobileQueryListener);
@@ -78,8 +99,13 @@ export class FullComponent implements OnInit, OnDestroy {
window.open('https://github.com/rampeur', '_blank'); window.open('https://github.com/rampeur', '_blank');
} }
toggleShowQCMMenu() { toggleShowMenu(state: string) {
this.showQCMMenu = !this.showQCMMenu; const indexOf: number = this.visibleMenu.indexOf(state);
if (indexOf !== -1) {
this.visibleMenu.splice(indexOf, 1);
} else {
this.visibleMenu.push(state);
}
} }
logout() { logout() {
+49 -32
View File
@@ -3,50 +3,67 @@ import { Injectable } from '@angular/core';
import { Menu } from 'src/app/core/models/menu.model'; import { Menu } from 'src/app/core/models/menu.model';
const MENUAUTH: Menu[] = [ const MENUAUTH: Menu[] = [
{ state: 'home', type: 'link', name: 'Accueil', icon: 'home', desc: '' }, { state: 'home', type: 'link', name: 'Accueil', icon: 'home', icontype: 'mat', desc: '' },
{ state: 'login', type: 'link', name: 'Se connecter', icon: 'fingerprint', desc: '' }, { state: 'login', type: 'link', name: 'Se connecter', icon: 'fingerprint', icontype: 'mat', desc: '' },
{ state: 'register', type: 'link', name: 'Créer un compte', icon: 'person_add_alt', desc: '' } { state: 'register', type: 'link', name: 'Créer un compte', icon: 'person_add_alt', icontype: 'mat', desc: '' }
];
const MENUITEMSADMIN: Menu[] = [
{ state: 'home', type: 'link', name: 'Accueil', icon: 'home', icontype: 'mat', desc: '' },
{ state: 'products', type: 'menu', name: 'Produits', icon: 'barcode', desc: '' },
{ parent: 'products', state: 'les-fleurs', type: 'link', name: 'Fleurs', icon: 'marijuana', desc: '' },
{ parent: 'products', state: 'les-resines', type: 'link', name: 'Résines', icon: 'butter', desc: '' },
{ parent: 'products', state: 'les-molecules', type: 'link', name: 'Molécules', icon: 'molecule', desc: '' },
{ state: 'page', type: 'menu', name: 'Documentation', icon: 'books', desc: '' },
{ parent: 'page', state: 'les-fleurs', type: 'link', name: 'Fleurs', icon: 'marijuana', desc: '' },
{ parent: 'page', state: 'les-resines', type: 'link', name: 'Résines', icon: 'butter', desc: '' },
{ parent: 'page', state: 'les-molecules', type: 'link', name: 'Molécules', icon: 'molecule', desc: '' },
{ state: 'dashboard', type: 'link', name: 'Dashboard', icon: 'dashboard', icontype: 'mat', desc: '' },
{ state: 'logbook', type: 'link', name: 'Carnet de saut', icon: 'assignment_turned_in', icontype: 'mat', desc: '' },
{ state: 'aeronefs', type: 'link', name: 'Aéronefs', icon: 'flight', icontype: 'mat', desc: '' },
{ state: 'dropzones', type: 'link', name: 'Drop Zones', icon: 'flight_takeoff', icontype: 'mat', desc: '' },
{ state: 'canopies', type: 'link', name: 'Voiles', icon: 'paragliding', icontype: 'mat', desc: '' },
{ state: 'jumps', type: 'link', name: 'Sauts', icon: 'filter_drama', icontype: 'mat', desc: '' },
{ state: 'calculator', type: 'link', name: 'Taille de voile', icon: 'calculate', icontype: 'mat', desc: '' },
{ state: 'qcm', type: 'menu', name: 'QCM Brevets', icon: 'rule_folder', icontype: 'mat', desc: '' },
{ parent: 'qcm', state: 'bpa', type: 'link', name: 'BPA', icon: 'rule', icontype: 'mat' },
{ parent: 'qcm', state: 'c', type: 'link', name: 'Brevet C', icon: 'rule', icontype: 'mat' },
{ parent: 'qcm', state: 'd', type: 'link', name: 'Brevet D', icon: 'rule', icontype: 'mat' }
]; ];
const MENUITEMS: Menu[] = [ const MENUITEMS: Menu[] = [
{ state: 'home', type: 'link', name: 'Accueil', icon: 'home', desc: '' }, { state: 'home', type: 'link', name: 'Accueil', icon: 'home', icontype: 'mat', desc: '' },
{ state: 'dashboard', type: 'link', name: 'Dashboard', icon: 'dashboard', desc: '' }, { state: 'products', type: 'menu', name: 'Produits', icon: 'barcode', desc: '' },
{ state: 'logbook', type: 'link', name: 'Carnet de saut', icon: 'assignment_turned_in', desc: '' }, { parent: 'products', state: 'les-fleurs', type: 'link', name: 'Fleurs', icon: 'marijuana', desc: '' },
{ state: 'aeronefs', type: 'link', name: 'Aéronefs', icon: 'flight', desc: '' }, { parent: 'products', state: 'les-resines', type: 'link', name: 'Résines', icon: 'butter', desc: '' },
{ state: 'dropzones', type: 'link', name: 'Drop Zones', icon: 'flight_takeoff', desc: '' }, { parent: 'products', state: 'les-molecules', type: 'link', name: 'Molécules', icon: 'molecule', desc: '' },
{ state: 'canopies', type: 'link', name: 'Voiles', icon: 'paragliding', desc: '' }, { state: 'page', type: 'menu', name: 'Documentation', icon: 'books', desc: '' },
{ state: 'jumps', type: 'link', name: 'Sauts', icon: 'filter_drama', desc: '' }, { parent: 'page', state: 'les-fleurs', type: 'link', name: 'Fleurs', icon: 'marijuana', desc: '' },
{ state: 'calculator', type: 'link', name: 'Taille de voile', icon: 'calculate', desc: '' }, { parent: 'page', state: 'les-resines', type: 'link', name: 'Résines', icon: 'butter', desc: '' },
{ state: 'qcm', type: 'menu', name: 'QCM Brevets', icon: 'rule_folder', desc: '' } { parent: 'page', state: 'les-molecules', type: 'link', name: 'Molécules', icon: 'molecule', desc: '' }
]; ];
const MENUPJUMP: Menu[] = [ const MENUPJUMP: Menu[] = [
{ state: '#', type: 'link', name: 'Modifier', icon: 'edit', desc: '', color: 'primary' }, { state: '#', type: 'link', name: 'Modifier', icon: 'edit', icontype: 'mat', desc: '', color: 'primary' },
{ state: '#', type: 'link', name: 'Supprimer', icon: 'delete', desc: '', color: 'raspberry' } { state: '#', type: 'link', name: 'Supprimer', icon: 'delete', icontype: 'mat', desc: '', color: 'raspberry' }
];
const MENUQCM: Menu[] = [
{ state: 'bpa', type: 'link', name: 'BPA', icon: 'rule' },
{ state: 'c', type: 'link', name: 'Brevet C', icon: 'rule' },
{ state: 'd', type: 'link', name: 'Brevet D', icon: 'rule' }
]; ];
const MENUPANEL: Menu[] = [ const MENUPANEL: Menu[] = [
{ state: '#', type: 'link', name: 'Retirer du dashboard', icon: 'add_notes', desc: '' }, { state: '#', type: 'link', name: 'Retirer du dashboard', icon: 'add_notes', icontype: 'mat', desc: '' },
{ state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', desc: '' } { state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', icontype: 'mat', desc: '' }
]; ];
const MENUCALCULATOR: Menu[] = [ const MENUCALCULATOR: Menu[] = [
{ state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', desc: '' }, { state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', icontype: 'mat', desc: '' },
{ state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', desc: '' }, { state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', icontype: 'mat', desc: '' },
{ state: '#', type: 'link', name: 'Partager', icon: 'share', desc: '' } { state: '#', type: 'link', name: 'Partager', icon: 'share', icontype: 'mat', desc: '' }
]; ];
const MENULOGBOOK: Menu[] = [ const MENULOGBOOK: Menu[] = [
{ state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', desc: '' }, { state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', icontype: 'mat', desc: '' },
{ state: '#', type: 'link', name: 'Recherche par participant', icon: 'person_search', desc: '' }, { state: '#', type: 'link', name: 'Recherche par participant', icon: 'person_search', icontype: 'mat', desc: '' },
{ state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', desc: '' }, { state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', icontype: 'mat', desc: '' },
{ state: '#', type: 'link', name: 'Import CSV', icon: 'upload_file', desc: '' } { state: '#', type: 'link', name: 'Import CSV', icon: 'upload_file', icontype: 'mat', desc: '' }
]; ];
@Injectable() @Injectable()
@@ -60,12 +77,12 @@ export class MenuItems {
getMenuItems(): Menu[] { getMenuItems(): Menu[] {
return MENUITEMS; return MENUITEMS;
} }
getMenuItemsAdmin(): Menu[] {
return MENUITEMSADMIN;
}
getMenuLogbook(): Menu[] { getMenuLogbook(): Menu[] {
return MENULOGBOOK; return MENULOGBOOK;
} }
getMenuQCM(): Menu[] {
return MENUQCM;
}
getMenuPanel(): Menu[] { getMenuPanel(): Menu[] {
return MENUPANEL; return MENUPANEL;
} }
+12
View File
@@ -0,0 +1,12 @@
export interface ArticleListConfig {
type: string;
filters: ArticleListFilters;
}
export interface ArticleListFilters {
tag?: string,
author?: string,
favorited?: string,
limit?: number,
offset?: number
}
@@ -0,0 +1,6 @@
import { Article } from './article.model';
export interface ArticleList {
articles: Array<Article>;
articlesCount: number;
}
+28
View File
@@ -0,0 +1,28 @@
import { Profile } from './profile.model';
import { PageLinks, PageMeta } from './shared.model';
import { Tag } from './tag.model';
export interface Article {
slug: string;
title: string;
description: string;
body: string;
author: Profile;
createdAt: string;
updatedAt: string;
tagList: Tag[];
favorited: boolean;
favoritesCount: number;
}
export interface ArticlePageData {
article: Article;
_links: PageLinks;
_meta: PageMeta;
}
export interface ArticlesPageData {
articles: Array<Article>;
_links: PageLinks;
_meta: PageMeta;
}
+10
View File
@@ -0,0 +1,10 @@
import { Article } from './article.model';
export interface Email {
from: string;
to: string;
subject: string;
text: string;
html: string;
article?: Article;
}
+8
View File
@@ -1,5 +1,8 @@
export * from './aeronef.model'; export * from './aeronef.model';
export * from './aeronef-list.model'; export * from './aeronef-list.model';
export * from './article.model';
export * from './article-list.model';
export * from './article-list-config.model';
export * from './application.model'; export * from './application.model';
export * from './application-list.model'; export * from './application-list.model';
export * from './application-list-config.model'; export * from './application-list-config.model';
@@ -11,7 +14,12 @@ export * from './errors.model';
export * from './jump.model'; export * from './jump.model';
export * from './jump-list.model'; export * from './jump-list.model';
export * from './jump-list-config.model'; export * from './jump-list-config.model';
export * from './menu.model';
export * from './product.model';
export * from './product-list.model';
export * from './product-list-config.model';
export * from './profile.model'; export * from './profile.model';
export * from './qcm.model'; export * from './qcm.model';
export * from './shared.model'; export * from './shared.model';
export * from './tag.model';
export * from './user.model'; export * from './user.model';
+1 -11
View File
@@ -1,7 +1,7 @@
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { Profile } from './profile.model'; import { Profile } from './profile.model';
import { PageLinks, PageMeta } from './shared.model'; import { ColumnDefinition, PageLinks, PageMeta } from './shared.model';
export interface Jump { export interface Jump {
slug: string; slug: string;
@@ -200,16 +200,6 @@ export interface JumpsPageData {
jumpsByModuleCount: number;*/ jumpsByModuleCount: number;*/
} }
export interface ColumnDefinition {
key: string;
type: string;
label: string;
required?: boolean;
pattern?: string;
min?: number;
step?: number;
}
export const jumpColumns: Array<ColumnDefinition> = [ export const jumpColumns: Array<ColumnDefinition> = [
{ {
key: 'isSelected', key: 'isSelected',
+2
View File
@@ -1,10 +1,12 @@
import { Badge } from './badge.model'; import { Badge } from './badge.model';
export interface Menu { export interface Menu {
parent?: string;
state: string; state: string;
name: string; name: string;
type: string; type: string;
icon: string; icon: string;
icontype?: string;
desc?: string; desc?: string;
badge?: Badge[]; badge?: Badge[];
color?: string; color?: string;
@@ -0,0 +1,18 @@
export interface ProductListConfig {
type: string;
filters: ProductListFilters;
}
export interface ProductListFilters {
slug?: string;
name?: string;
numero?: number;
barcode?: number;
poid?: number;
sachets?: string;
molecules?: string;
numeroRangeStart?: number;
numeroRangeEnd?: number;
sachetsRangeStart?: number;
sachetsRangeEnd?: number;
}
@@ -0,0 +1,6 @@
import { Product } from './product.model';
export interface ProductList {
products: Array<Product>;
productsCount: number;
}
+86
View File
@@ -0,0 +1,86 @@
import { FormControl } from '@angular/forms';
import { ColumnDefinition, PageLinks, PageMeta } from './shared.model';
export interface Product {
slug: string;
name: string;
numero: number;
barcode: number;
poid?: number;
sachets?: string;
molecules?: string;
isSelected?: boolean;
createdAt: string;
updatedAt: string;
}
export interface ProductForm {
slug: FormControl<string>;
name: FormControl<string>;
numero: FormControl<number>;
barcode: FormControl<number>;
poid: FormControl<number>;
sachets: FormControl<Array<number>>;
molecules: FormControl<Array<string>>;
}
export interface ProductPageData {
product: Product;
_links: PageLinks;
_meta: PageMeta;
}
export interface ProductsPageData {
products: Array<Product>;
_links: PageLinks;
_meta: PageMeta;
}
export const productColumns: Array<ColumnDefinition> = [
{
key: 'isSelected',
type: 'isSelected',
label: ''
},
{
key: 'name',
type: 'text',
label: 'Libellé',
required: true
},
{
key: 'numero',
type: 'numeric',
label: 'Numéro',
required: true,
min: 0,
step: 1
},
{
key: 'barcode',
type: 'numeric',
label: 'Code EAN13',
required: true,
min: 0,
step: 1
},
{
key: 'poid',
type: 'numeric',
label: 'Poid',
min: 0,
step: 1
},
{
key: 'sachets',
type: 'text',
label: 'Sachets'
},
{
key: 'molecules',
type: 'text',
label: 'Molécules'
}
];
+10
View File
@@ -28,3 +28,13 @@ export interface ReduceRow {
key: string[]; key: string[];
value: number; value: number;
} }
export interface ColumnDefinition {
key: string;
type: string;
label: string;
required?: boolean;
pattern?: string;
min?: number;
step?: number;
}
+5
View File
@@ -0,0 +1,5 @@
export interface Tag {
name: string;
createdAt: string;
updatedAt: string;
}
+1 -1
View File
@@ -5,9 +5,9 @@ export interface User {
firstname: string; firstname: string;
lastname: string; lastname: string;
phone: string; phone: string;
token: string;
licence?: number; licence?: number;
poids?: number; poids?: number;
image?: string; image?: string;
bg_image?: string; bg_image?: string;
token: string;
} }
@@ -0,0 +1,12 @@
import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, ResolveFn } from '@angular/router';
import { take } from 'rxjs/operators';
import { ArticlePageData } from 'src/app/core/models';
import { ArticlesService } from 'src/app/core/services';
export const articleResolver: ResolveFn<ArticlePageData> = (
route: ActivatedRouteSnapshot
) => {
return inject(ArticlesService).get(route.params['slug']).pipe(take(1));
};
@@ -0,0 +1,10 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { take } from 'rxjs/operators';
import { Article } from 'src/app/core/models';
import { ArticlesService } from 'src/app/core/services';
export const articlesResolver: ResolveFn<Array<Article>> = () => {
return inject(ArticlesService).getAll().pipe(take(1));
};
+4
View File
@@ -1,4 +1,6 @@
export * from './aeronefs-page-resolver.service'; export * from './aeronefs-page-resolver.service';
export * from './article-resolver.service';
export * from './articles-resolver.service';
export * from './auth-resolver.service'; export * from './auth-resolver.service';
export * from './canopies-page-resolver.service'; export * from './canopies-page-resolver.service';
export * from './dropzones-page-resolver.service'; export * from './dropzones-page-resolver.service';
@@ -6,5 +8,7 @@ export * from './jump-resolver.service';
export * from './jumps-page-resolver.service'; export * from './jumps-page-resolver.service';
export * from './jumps-resolver.service'; export * from './jumps-resolver.service';
export * from './lastjump-resolver.service'; export * from './lastjump-resolver.service';
export * from './product-resolver.service';
export * from './products-resolver.service';
export * from './profile-resolver.service'; export * from './profile-resolver.service';
export * from './qcm-resolver.service'; export * from './qcm-resolver.service';
@@ -0,0 +1,12 @@
import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, ResolveFn } from '@angular/router';
import { take } from 'rxjs/operators';
import { ProductPageData } from 'src/app/core/models';
import { ProductsService } from 'src/app/core/services';
export const productResolver: ResolveFn<ProductPageData> = (
route: ActivatedRouteSnapshot
) => {
return inject(ProductsService).get(route.params['slug']).pipe(take(1));
};
@@ -0,0 +1,10 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { take } from 'rxjs/operators';
import { Product } from 'src/app/core/models';
import { ProductsService } from 'src/app/core/services';
export const productsResolver: ResolveFn<Array<Product>> = () => {
return inject(ProductsService).getAll().pipe(take(1));
};
+65
View File
@@ -0,0 +1,65 @@
import { Injectable } from '@angular/core';
import { HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ApiService } from './api.service';
import { Article, ArticleList, ArticleListConfig, ArticleListFilters, ArticlePageData } from '../models';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class ArticlesService {
constructor(
private apiService: ApiService
) { }
query(config: ArticleListConfig): Observable<ArticleList> {
// Convert any filters over to Angular's URLSearchParams
const params: ArticleListFilters = {} as ArticleListFilters;
Object.assign(params, config.filters);
return this.apiService
.get(
'/articles' + ((config.type === 'feed') ? '/feed' : ''),
new HttpParams({ fromObject: <{
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
}>params })
);
}
get(slug: string): Observable<ArticlePageData> {
return this.apiService.get('/articles/' + slug)/*.pipe(map(data => {
data.article.tagList = data.article.tagNameTagTagLists;
delete data.article.tagNameTagTagLists;
return data;
}));*/
}
getAll(): Observable<Array<Article>> {
return this.apiService.get('/articles').pipe(map(data => data.articles));
}
destroy(slug: string) {
return this.apiService.delete('/articles/' + slug);
}
save(article: Article): Observable<Article> {
// If we're updating an existing article
if (article.slug) {
return this.apiService.put('/articles/' + article.slug, { article: article })
.pipe(map(data => data.article));
// Otherwise, create a new article
} else {
return this.apiService.post('/articles/', { article: article })
.pipe(map(data => data.article));
}
}
favorite(slug: string): Observable<Article> {
return this.apiService.post('/articles/' + slug + '/favorite');
}
unfavorite(slug: string): Observable<Article> {
return this.apiService.delete('/articles/' + slug + '/favorite');
}
}
+2
View File
@@ -1,6 +1,7 @@
export * from './aeronefs.service'; export * from './aeronefs.service';
export * from './api.service'; export * from './api.service';
export * from './applications.service'; export * from './applications.service';
export * from './articles.service';
export * from './calculator.service'; export * from './calculator.service';
export * from './canopies.service'; export * from './canopies.service';
export * from './dropzones.service'; export * from './dropzones.service';
@@ -8,6 +9,7 @@ export * from './jumps.service';
export * from './jump-table.service'; export * from './jump-table.service';
export * from './jwt.service'; export * from './jwt.service';
export * from './pages.service'; export * from './pages.service';
export * from './products.service';
export * from './profiles.service'; export * from './profiles.service';
export * from './qcm.service'; export * from './qcm.service';
export * from './user.service'; export * from './user.service';
+94
View File
@@ -0,0 +1,94 @@
import { Injectable } from '@angular/core';
import { HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiService } from './api.service';
import {
ProductList, ProductListConfig, ProductListFilters,
Product, ProductPageData
} from 'src/app/core/models';
@Injectable({ providedIn: 'root' })
export class ProductsService {
constructor(
private apiService: ApiService
) { }
query(config: ProductListConfig): Observable<ProductList> {
// Convert any filters over to Angular's URLSearchParams
/*
const params: ProductListFilters = {
date: config.filters.date,
lieu: config.filters.lieu,
oaci: config.filters.oaci,
aeronef: config.filters.aeronef,
imat: config.filters.imat,
hauteur: config.filters.hauteur,
voile: config.filters.voile,
taille: config.filters.taille,
categorie: config.filters.categorie,
module: config.filters.module,
participants: config.filters.participants,
accessoires: config.filters.accessoires,
zone: config.filters.zone,
author: config.filters.author,
limit: config.filters.limit,
offset: config.filters.offset,
numeroRangeStart: config.filters.numeroRangeStart!,
numeroRangeEnd: config.filters.numeroRangeEnd!,
tailleRangeStart: config.filters.tailleRangeStart!,
tailleRangeEnd: config.filters.tailleRangeEnd!,
participantsRangeStart: config.filters.participantsRangeStart!,
participantsRangeEnd: config.filters.participantsRangeEnd!,
hauteurRangeStart: config.filters.hauteurRangeStart!,
hauteurRangeEnd: config.filters.hauteurRangeEnd!
};
*/
const params: ProductListFilters = {} as ProductListFilters;
Object.assign(params, config.filters);
return this.apiService
.get(
'/products' + ((config.type === 'feed') ? '/feed' : ''),
new HttpParams({ fromObject: <{
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
}>params })
);
}
get(slug: string): Observable<ProductPageData> {
return this.apiService.get(`/products/${slug}`);
}
getAll(): Observable<Array<Product>> {
return this.apiService.get('/products').pipe(map(data => data.products));
}
getAllByCategorie(): Observable<Array<Product>> {
return this.apiService.get('/products/allByCategorie').pipe(map(data => data.products));
}
create(product: Product): Observable<Product> {
return this.apiService.post('/products/', { product: product }).pipe(map(data => data.product));
}
update(product: Product): Observable<Product> {
return this.apiService.put(`/products/${product.slug}`, { product: product }).pipe(map(data => data.product));
}
destroy(slug: string): Observable<boolean> {
return this.apiService.delete(`/products/${slug}`).pipe(map(data => data.deleted));
}
save(product: Product): Observable<Product> {
// If we're updating an existing product
if (product.slug) {
return this.apiService.put(`/products/${product.slug}`, { product: product }).pipe(map(data => data.product));
// Otherwise, create a new product
} else {
return this.apiService.post('/products/', { product: product }).pipe(map(data => data.product));
}
}
}
+1 -1
View File
@@ -63,7 +63,7 @@ export class UserService implements OnDestroy {
attemptAuth(type: string, credentials?: { email: string; password: string; }): Observable<{ user: User }> { attemptAuth(type: string, credentials?: { email: string; password: string; }): Observable<{ user: User }> {
const route = (type === 'login') ? '/login' : ''; const route = (type === 'login') ? '/login' : '';
const user$: Observable<{ user: User }> = this.http.post<{ user: User }>(`/users${route}`, { user: credentials }); const user$: Observable<{ user: User }> = this.http.post<{ user: User }>(`/user${route}`, { user: credentials });
/*return user$.pipe(map( /*return user$.pipe(map(
(data: any) => { (data: any) => {
this.setAuth(data.user); this.setAuth(data.user);
+51 -11
View File
@@ -3,19 +3,19 @@ import { Routes } from '@angular/router';
import { import {
AeronefsComponent, CalculatorComponent, CanopiesComponent, CredentialsComponent, AeronefsComponent, CalculatorComponent, CanopiesComponent, CredentialsComponent,
DashboardComponent, DropzonesComponent, JumpComponent, JumpsComponent, DashboardComponent, DropzonesComponent, JumpComponent, JumpsComponent,
LogbookComponent, ProfileComponent, QcmComponent, SettingsComponent LogbookComponent, PageComponent, PagesComponent, ProductComponent, ProductsComponent, ProfileComponent, QcmComponent, SettingsComponent
} from 'src/app/components'; } from 'src/app/components';
import { authGuard } from 'src/app/core/guards'; import { authGuard, adminGuard } from 'src/app/core/guards';
import { import {
aeronefsPageResolver, authResolver, canopiesPageResolver, dropZonesPageResolver, aeronefsPageResolver, articleResolver, articlesResolver, authResolver, canopiesPageResolver, dropZonesPageResolver,
jumpResolver, jumpsPageResolver, lastjumpResolver, profileResolver, qcmResolver jumpResolver, jumpsPageResolver, lastjumpResolver, productResolver, productsResolver, profileResolver, qcmResolver
} from 'src/app/core/resolvers'; } from 'src/app/core/resolvers';
export const AUTH_ROUTES: Routes = [ export const AUTH_ROUTES: Routes = [
{ {
path: 'aeronefs', path: 'aeronefs',
component: AeronefsComponent, component: AeronefsComponent,
canActivate: [ authGuard ], canActivate: [ authGuard, adminGuard ],
resolve: { resolve: {
aeronefsPageData: aeronefsPageResolver aeronefsPageData: aeronefsPageResolver
} }
@@ -31,7 +31,7 @@ export const AUTH_ROUTES: Routes = [
{ {
path: 'canopies', path: 'canopies',
component: CanopiesComponent, component: CanopiesComponent,
canActivate: [ authGuard ], canActivate: [ authGuard, adminGuard ],
resolve: { resolve: {
canopiesPageData: canopiesPageResolver canopiesPageData: canopiesPageResolver
} }
@@ -39,7 +39,7 @@ export const AUTH_ROUTES: Routes = [
{ {
path: 'dashboard', path: 'dashboard',
component: DashboardComponent, component: DashboardComponent,
canActivate: [ authGuard ], canActivate: [ authGuard, adminGuard ],
resolve: { resolve: {
isAuthenticated: authResolver isAuthenticated: authResolver
} }
@@ -47,7 +47,7 @@ export const AUTH_ROUTES: Routes = [
{ {
path: 'dropzones', path: 'dropzones',
component: DropzonesComponent, component: DropzonesComponent,
canActivate: [ authGuard ], canActivate: [ authGuard, adminGuard ],
resolve: { resolve: {
dropZonesPageData: dropZonesPageResolver dropZonesPageData: dropZonesPageResolver
} }
@@ -55,7 +55,7 @@ export const AUTH_ROUTES: Routes = [
{ {
path: 'jumps', path: 'jumps',
component: JumpsComponent, component: JumpsComponent,
canActivate: [ authGuard ], canActivate: [ authGuard, adminGuard ],
resolve: { resolve: {
pageData: jumpsPageResolver pageData: jumpsPageResolver
} }
@@ -63,7 +63,7 @@ export const AUTH_ROUTES: Routes = [
{ {
path: 'jump/:slug', path: 'jump/:slug',
component: JumpComponent, component: JumpComponent,
canActivate: [ authGuard ], canActivate: [ authGuard, adminGuard ],
resolve: { resolve: {
pageData: jumpResolver pageData: jumpResolver
} }
@@ -71,12 +71,52 @@ export const AUTH_ROUTES: Routes = [
{ {
path: 'logbook', path: 'logbook',
component: LogbookComponent, component: LogbookComponent,
canActivate: [ authGuard ], canActivate: [ authGuard, adminGuard ],
resolve: { resolve: {
isAuthenticated: authResolver, isAuthenticated: authResolver,
lastjump: lastjumpResolver lastjump: lastjumpResolver
} }
}, },
{
path: 'page/:slug',
component: PageComponent,
canActivate: [ authGuard ],
resolve: {
pageData: articleResolver
}
},
{
path: 'pages',
component: PagesComponent,
canActivate: [ authGuard ],
resolve: {
pageData: articlesResolver
}
},
{
path: 'products',
component: ProductsComponent,
canActivate: [ authGuard, adminGuard ],
resolve: {
pageData: productsResolver
}
},
{
path: 'products/:category',
component: ProductsComponent,
canActivate: [ authGuard, adminGuard ],
resolve: {
pageData: productsResolver
}
},
{
path: 'product/:slug',
component: ProductComponent,
canActivate: [ authGuard, adminGuard ],
resolve: {
pageData: productResolver
}
},
{ {
path: 'profile', path: 'profile',
component: ProfileComponent, component: ProfileComponent,
+18 -1
View File
@@ -1,7 +1,8 @@
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { AuthComponent, HomeComponent } from 'src/app/components'; import { AuthComponent, HomeComponent, PageComponent, PagesComponent } from 'src/app/components';
import { noauthGuard } from 'src/app/core/guards'; import { noauthGuard } from 'src/app/core/guards';
import { articleResolver, articlesResolver } from 'src/app/core/resolvers';
export const NOAUTH_ROUTES: Routes = [ export const NOAUTH_ROUTES: Routes = [
{ {
@@ -18,6 +19,22 @@ export const NOAUTH_ROUTES: Routes = [
component: AuthComponent, component: AuthComponent,
canActivate: [ noauthGuard ] canActivate: [ noauthGuard ]
}, },
{
path: 'pages',
component: PagesComponent,
canActivate: [ noauthGuard ],
resolve: {
pageData: articlesResolver
}
},
{
path: 'page/:slug',
component: PageComponent,
canActivate: [ noauthGuard ],
resolve: {
pageData: articleResolver
}
},
{ {
path: '', path: '',
redirectTo: 'home', redirectTo: 'home',
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/ms-icon-70x70.png"/>
<square150x150logo src="/ms-icon-150x150.png"/>
<square310x310logo src="/ms-icon-310x310.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

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