Mise à jour articles et produits
@@ -53,3 +53,6 @@ sonar.properties
|
||||
**/*.copy.ts
|
||||
**/*.copy.scss
|
||||
**/*.copy.html
|
||||
|
||||
.next/
|
||||
next-env.d.ts
|
||||
@@ -1,4 +1,4 @@
|
||||
# HeadupAngular
|
||||
# Ad Astra
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.2.2.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="container auth-page">
|
||||
<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>
|
||||
<p class="text-xs-center">
|
||||
@if (authType === 'register') {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { trigger, state, style, animate, transition } from '@angular/animations';
|
||||
import { Component, DestroyRef, inject, OnInit } from '@angular/core';
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
|
||||
import { ListErrorsComponent, } from 'src/app/components/shared';
|
||||
import { Errors, Jump } from 'src/app/core/models';
|
||||
import { JumpsService } from 'src/app/core/services';
|
||||
import { Errors } from 'src/app/core/models';
|
||||
//import { UserService } from 'src/app/core/services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
@@ -35,28 +34,18 @@ import { JumpsService } from 'src/app/core/services';
|
||||
]
|
||||
})
|
||||
export class HomeComponent implements OnInit {
|
||||
private _lastjump: Subscription = new Subscription();
|
||||
private _lastjump$: Observable<Jump> = new Observable();
|
||||
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 lastJump: Jump = {} as Jump;
|
||||
public destroyRef = inject(DestroyRef);
|
||||
public now = new Date();
|
||||
|
||||
constructor(
|
||||
private _jumpsService: JumpsService
|
||||
private titleService: Title
|
||||
//private _userService: UserService
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this._lastjump$ = this._jumpsService.getLastJump().pipe(takeUntilDestroyed(this.destroyRef));
|
||||
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()
|
||||
this.titleService.setTitle(this.title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@ export * from './home/home.component';
|
||||
export * from './jump/jump.component';
|
||||
export * from './jumps/jumps.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 './qcm/qcm.component';
|
||||
export * from './settings';
|
||||
|
||||
@@ -23,44 +23,6 @@
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<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-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
@@ -127,4 +89,42 @@
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</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>
|
||||
@@ -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>
|
||||
<mat-icon fontIcon="close" class="pointer" (click)="closeDialog()"></mat-icon>
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
<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">
|
||||
<h5>Le {{ jump.date | date: 'dd/MM/yyyy' }} à {{ jump.lieu }} {{ jump.zone ? jump.zone : jump.oaci }} :</h5>
|
||||
</div>
|
||||
@@ -20,12 +20,12 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-light-info fw-normal">Aeronef:</dt>
|
||||
<dt>Aeronef:</dt>
|
||||
<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>
|
||||
@if (jump.programme) {
|
||||
<dt class="text-light-info fw-normal">Programme:</dt>
|
||||
<dt>Programme:</dt>
|
||||
<dd>{{ jump.programme }}</dd>
|
||||
}
|
||||
</dl>
|
||||
@@ -33,13 +33,13 @@
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
@if (!jump.x2data) {
|
||||
<dt class="text-light-info fw-normal">Hauteur:</dt>
|
||||
<dt>Hauteur:</dt>
|
||||
<dd>{{ jump.hauteur }} m</dd>
|
||||
<dt class="text-light-info fw-normal">Déploiement:</dt>
|
||||
<dt>Déploiement:</dt>
|
||||
<dd>{{ jump.deploiement }} m</dd>
|
||||
}
|
||||
@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>
|
||||
}
|
||||
</dl>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<mat-card>
|
||||
<mat-card-header class="p-0">
|
||||
<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>
|
||||
@if (jumpsCount < lastJump.numero) {
|
||||
<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();
|
||||
});
|
||||
});
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
<div class="banner light text-center mat-elevation-z2">
|
||||
<div class="d-flex justify-content-center text-start">
|
||||
<img [src]="user.image" class="user-img" [alt]="user.username" />
|
||||
<div class="ms-3">
|
||||
<h4>{{ bannerTitle }}</h4>
|
||||
<button mat-raised-button color="warn" routerLink="/settings" class="ms-2">
|
||||
<div class="banner bg-accent mat-elevation-z2">
|
||||
<div class="d-flex mx-2 mx-md-3 mx-lg-4">
|
||||
<img [src]="user.image" class="user-img me-3" [alt]="user.username" />
|
||||
<div class="flex-spacer">
|
||||
<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="warn" routerLink="/settings">
|
||||
<mat-icon aria-label="Modifier le compte" fontIcon="settings"></mat-icon> {{ btnSettingsTitle }}
|
||||
</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>
|
||||
@@ -22,63 +22,60 @@
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<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>
|
||||
<div class="text-center">
|
||||
<a href="/" class="d-block p-4"><img height="72" width="90" src="assets/images/vector/upt_logo.png" alt="Logo UPT"></a>
|
||||
<mat-divider class="border-primary w-50 mx-auto"></mat-divider>
|
||||
<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>
|
||||
<mat-divider class="border-primary w-50 mx-auto"></mat-divider>
|
||||
</div>
|
||||
<div class="d-none d-lg-flex justify-content-center mt-5 mb-0">
|
||||
<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;">
|
||||
</div>
|
||||
<div class="d-flex d-lg-none justify-content-center mt-5 mb-0">
|
||||
<img class="img-fluid" src="assets/images/vector/v306_NWG_01.png" alt="v306 Navy White Gold preview">
|
||||
</div>
|
||||
<div class="d-flex text-center mt-4 mb-0 py-4">
|
||||
<figure class="flex-fill mx-5">
|
||||
<img class="figure-img img-fluid img-thumbnail" src="assets/images/vector/ContainerA.png" alt="" title="ContainerA" 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/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 class="row mt-2">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal mb-0">
|
||||
<dt>Nom:</dt>
|
||||
<dd>{{ user.lastname }}</dd>
|
||||
<dt>Prénom:</dt>
|
||||
<dd>{{ user.firstname }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal mb-0">
|
||||
<dt>Email:</dt>
|
||||
<dd>{{ user.email }}</dd>
|
||||
<dt>Téléphone:</dt>
|
||||
<dd>{{ user.phone }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</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>
|
||||
<table class="table table-dark table-striped table-condensed mat-elevation-z2">
|
||||
<thead>
|
||||
<tr role="row" class="table-dark">
|
||||
<th class="align-middle w-25 ps-4">
|
||||
<div class="text-left">
|
||||
Main Std Fit
|
||||
Date
|
||||
</div>
|
||||
</th>
|
||||
<th class="align-middle w-25">
|
||||
<div class="text-left">
|
||||
Main Full Fit
|
||||
Numéro
|
||||
</div>
|
||||
</th>
|
||||
<th class="align-middle w-25">
|
||||
<div class="text-left">
|
||||
Reserve Std Fit
|
||||
Statut
|
||||
</div>
|
||||
</th>
|
||||
<th class="align-middle w-25">
|
||||
<div class="text-left">
|
||||
Reserve Full Fit
|
||||
Total
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
@@ -86,391 +83,80 @@
|
||||
<tbody>
|
||||
<tr role="row" class="align-middle">
|
||||
<td class="cell ps-4">
|
||||
<span class="text-orange fw-bold">Crossfire 3 119</span><br>
|
||||
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>
|
||||
20/04/2025 13:37
|
||||
</td>
|
||||
<td class="cell">
|
||||
<span class="text-teal fw-bold text-decoration-underline">Crossfire 3 129</span><br>
|
||||
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>
|
||||
#aa2025042013371
|
||||
</td>
|
||||
<td class="cell">
|
||||
Icarus Reserve 109<br>
|
||||
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>
|
||||
<span class="text-green">Terminé</span>
|
||||
</td>
|
||||
<td class="cell">
|
||||
<span class="text-teal fw-bold text-decoration-underline">Icarus Reserve 119</span><br>
|
||||
Micro Raven 120<br>
|
||||
Nano 113<br>
|
||||
PD 113<br>
|
||||
R-Max 118<br>
|
||||
Smart 110<br>
|
||||
Speed 120<br>
|
||||
Tempo 120<br>
|
||||
56.00 €
|
||||
</td>
|
||||
</tr>
|
||||
<tr role="row" class="align-middle">
|
||||
<td class="cell ps-4">
|
||||
20/04/2025 13:42
|
||||
</td>
|
||||
<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>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</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">
|
||||
<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"> </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"> </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"> </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"> </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 & 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"> </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>
|
||||
<mat-divider class="mx-4"></mat-divider>
|
||||
<mat-divider class="my-4"></mat-divider>
|
||||
<!--
|
||||
turquoise
|
||||
cyan
|
||||
@@ -482,13 +168,13 @@ yellow
|
||||
-->
|
||||
<div class="content">
|
||||
<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">
|
||||
<mat-card-title>
|
||||
Buttons variants
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle class="fw-normal">
|
||||
turquoise / cyan / navy / purple / raspberry / orange / yellow
|
||||
<mat-card-subtitle class="fw-normal text-white">
|
||||
turquoise / cyan / navy / purple / raspberry / orange / yellow / megna / magenta / pink
|
||||
</mat-card-subtitle>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
.banner {
|
||||
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;
|
||||
.user-img {
|
||||
width: var.$user-img-size;
|
||||
|
||||
@@ -30,7 +30,6 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
||||
user: Partial<User> = {} as Partial<User>;
|
||||
errors: Errors = { errors: {} };
|
||||
btnSettingsTitle = 'Paramètres';
|
||||
btnPasswordTitle = 'Changer le mot de passe';
|
||||
|
||||
constructor(
|
||||
private titleService: Title,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<div class="banner bg-navy light text-center mat-elevation-z2">
|
||||
<div class="d-flex justify-content-center text-start">
|
||||
<img [src]="user.image" class="user-img" [alt]="user.username" />
|
||||
<div class="ms-3">
|
||||
<h4>{{ bannerTitle }}</h4>
|
||||
<div class="banner bg-accent mat-elevation-z2">
|
||||
<div class="d-flex mx-2 mx-md-3 mx-lg-4">
|
||||
<img [src]="user.image" class="user-img me-3" [alt]="user.username" />
|
||||
<div class="flex-spacer">
|
||||
<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]">
|
||||
<mat-icon aria-label="Voir le compte" fontIcon="person"></mat-icon> Mon compte
|
||||
</button>
|
||||
@@ -18,7 +21,7 @@
|
||||
<app-list-errors [errors]="errors"></app-list-errors>
|
||||
<div class="container page mt-4">
|
||||
<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()">
|
||||
<fieldset [disabled]="isSubmitting" class="w-100">
|
||||
<legend [hidden]="true">Mise à jour du mot de passe</legend>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
.banner {
|
||||
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;
|
||||
.user-img {
|
||||
width: var.$user-img-size;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<div class="banner bg-navy light text-center mat-elevation-z2">
|
||||
<div class="d-flex justify-content-center text-start">
|
||||
<img [src]="user.image" class="user-img" [alt]="user.username" />
|
||||
<div class="ms-3">
|
||||
<h4>{{ bannerTitle }}</h4>
|
||||
<div class="banner bg-accent mat-elevation-z2">
|
||||
<div class="d-flex mx-2 mx-md-3 mx-lg-4">
|
||||
<img [src]="user.image" class="user-img me-3" [alt]="user.username" />
|
||||
<div class="flex-spacer">
|
||||
<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]">
|
||||
<mat-icon aria-label="Voir le compte" fontIcon="person"></mat-icon> Mon compte
|
||||
</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>
|
||||
@@ -18,10 +18,22 @@
|
||||
<app-list-errors [errors]="errors"></app-list-errors>
|
||||
<div class="container page mt-4">
|
||||
<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()">
|
||||
<fieldset [disabled]="isSubmitting" class="w-100">
|
||||
<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">
|
||||
<mat-form-field appearance="fill" class="w-100">
|
||||
<mat-label>Nom</mat-label>
|
||||
@@ -40,48 +52,33 @@
|
||||
<input matInput type="tel" placeholder="Indiquez votre numéro de téléphone" formControlName="phone">
|
||||
</mat-form-field>
|
||||
</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">
|
||||
<mat-form-field appearance="fill" class="w-100">
|
||||
<mat-label>Photo de profil</mat-label>
|
||||
<input matInput type="url" placeholder="URL de votre photo" formControlName="image">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<mat-form-field appearance="fill" class="w-100">
|
||||
<mat-label>Arrière plan</mat-label>
|
||||
<input matInput type="url" placeholder="URL de votre arrière plan" formControlName="bg_image">
|
||||
<mat-select formControlName="image" [(value)]="user.image">
|
||||
@for (avatar of avatars; track avatar; let idx = $index) {
|
||||
<mat-option [value]="avatar">
|
||||
<img src="{{ avatar }}" alt="Avatar {{ idx + 1 }}" class="avatar">
|
||||
<span>Image {{ idx + 1 }}</span>
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset [disabled]="isSubmitting" class="w-100">
|
||||
<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">
|
||||
<button mat-raised-button color="primary" [disabled]="!settingsForm.valid" type="submit" class="float-sm-end">{{ btnUpdateTitle }}</button>
|
||||
<button mat-stroked-button color="warn" class="me-3" type="button" (click)="logout()">{{ btnLogoutTitle }}</button>
|
||||
<mat-divider class="mb-3"></mat-divider>
|
||||
<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>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
.banner {
|
||||
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;
|
||||
/*
|
||||
background-color: mat.get-color-from-palette(var.$md-custom-warn, 700);
|
||||
@@ -33,6 +33,17 @@
|
||||
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 */
|
||||
input::-webkit-outer-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 { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
|
||||
@@ -20,7 +21,7 @@ import { ListErrorsComponent } from 'src/app/components/shared';
|
||||
standalone: true,
|
||||
imports: [
|
||||
RouterLink, FormsModule, ReactiveFormsModule,
|
||||
MatButtonModule, MatIconModule, MatFormFieldModule, MatInputModule, MatDividerModule,
|
||||
MatButtonModule, MatIconModule, MatDividerModule, MatFormFieldModule, MatInputModule, MatSelectModule,
|
||||
ListErrorsComponent
|
||||
],
|
||||
styleUrl: './settings.component.scss',
|
||||
@@ -38,6 +39,18 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
errors: Errors = { errors: {} };
|
||||
isSubmitting = false;
|
||||
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(
|
||||
private router: Router,
|
||||
@@ -52,10 +65,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
firstname: ['', Validators.required],
|
||||
lastname: ['', Validators.required],
|
||||
phone: ['', Validators.required],
|
||||
licence: '',
|
||||
poids: '',
|
||||
image: '',
|
||||
bg_image: ''
|
||||
image: ''
|
||||
});
|
||||
// Optional: subscribe to changes on the form :
|
||||
// this.settingsForm.valueChanges.pipe(this._scavenger.collect()).subscribe(values => this.updateUser(values));
|
||||
@@ -94,7 +104,10 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
submitForm() {
|
||||
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);
|
||||
|
||||
const user$: Observable<{ user: User }> = this.userService.update(this.user).pipe(takeUntilDestroyed(this.destroyRef));
|
||||
|
||||
@@ -106,67 +106,75 @@
|
||||
</mat-menu>
|
||||
</div>
|
||||
<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}}">
|
||||
@if (menuitem.type === 'link') {
|
||||
@if (!menuitem.parent && menuitem.type === 'link') {
|
||||
<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 class="flex-spacer"></span>
|
||||
@for (badge of menuitem.badge; track badge) {
|
||||
<span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span>
|
||||
}
|
||||
</a>
|
||||
}
|
||||
@if (menuitem.type === 'menu') {
|
||||
<a role="button" aria-hidden="true" (click)="toggleShowQCMMenu()" (keyup)="true">
|
||||
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 mr-2"></mat-icon>
|
||||
}
|
||||
@if (menuitem.type === 'menu') {
|
||||
<a role="button" aria-hidden="true" (click)="toggleShowMenu(menuitem.state)" (keyup)="true">
|
||||
@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 class="flex-spacer"></span>
|
||||
@for (badge of menuitem.badge; track badge) {
|
||||
<span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span>
|
||||
}
|
||||
</a>
|
||||
}
|
||||
}
|
||||
</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]="['/qcm', menuqcm.state]">
|
||||
<mat-icon [fontIcon]="menuqcm.icon" class="mt-0 mr-2"></mat-icon>
|
||||
<span>{{ menuqcm.name }}</span>
|
||||
<span class="flex-spacer"></span>
|
||||
</a>
|
||||
@if (menuitem.type === 'menu') {
|
||||
@for (submenuitem of getMenuItems(); track submenuitem) {
|
||||
@if ( isVisibleMenu(menuitem.state) === true && submenuitem.parent === menuitem.state && submenuitem.type === 'link') {
|
||||
<mat-list-item appAccordionLink routerLinkActive="selected" group="{{ submenuitem.state}}">
|
||||
<a class="ps-4" appAccordionToggle [routerLink]="['/' + submenuitem.parent, submenuitem.state]">
|
||||
@if (menuitem.icontype === 'mat') {
|
||||
<mat-icon [fontIcon]="submenuitem.icon" class="mt-0 me-2"></mat-icon>
|
||||
}
|
||||
@else {
|
||||
<i class="icosvg-{{submenuitem.icon}} fs-2 mt-0 me-2"></i>
|
||||
}
|
||||
<span>{{ submenuitem.name }}</span>
|
||||
<span class="flex-spacer"></span>
|
||||
</a>
|
||||
</mat-list-item>
|
||||
}
|
||||
</mat-list-item>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</mat-nav-list>
|
||||
</ng-container>
|
||||
<ng-container *appShowAuthed="false">
|
||||
<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>
|
||||
<mat-nav-list appAccordion>
|
||||
@for (menuitem of menuItems.getMenuAuth(); track menuitem) {
|
||||
<mat-list-item appAccordionLink routerLinkActive="selected" group="{{menuitem.state}}">
|
||||
@if (menuitem.type === 'link') {
|
||||
<a appAccordionToggle [routerLink]="['/', menuitem.state]">
|
||||
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 mr-2"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
<span class="flex-spacer"></span>
|
||||
@for (badge of menuitem.badge; track badge) {
|
||||
<span matBadge="{{ badge.value }}" matBadgeColor="warn" matBadgeOverlap="false"></span>
|
||||
@if (menuitem.icontype === 'mat') {
|
||||
<mat-icon [fontIcon]="menuitem.icon" class="mt-0 me-2"></mat-icon>
|
||||
}
|
||||
@else {
|
||||
<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 class="flex-spacer"></span>
|
||||
@for (badge of menuitem.badge; track badge) {
|
||||
@@ -176,21 +184,6 @@
|
||||
}
|
||||
</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>
|
||||
</ng-container>
|
||||
<app-layout-footer></app-layout-footer>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
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 { MenuItems, ShowAuthedDirective, AccordionAnchorDirective, AccordionLinkDirective, AccordionDirective } 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;
|
||||
mobileQuery: MediaQueryList;
|
||||
currentUser: User = {} as User;
|
||||
showQCMMenu = false;
|
||||
today: number = Date.now();
|
||||
siteLink = 'https://www.rampeur.com';
|
||||
author = 'Solide Apps';
|
||||
siteLink = 'https://www.adastra-cbd.com';
|
||||
author = 'Ad Astra';
|
||||
links: Menu[] = [];
|
||||
visibleMenu: string[] = ['products', 'page'];
|
||||
//visibleMenu: string[] = ['products'];
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
@@ -65,10 +67,29 @@ export class FullComponent implements OnInit, OnDestroy {
|
||||
this._currentUser = user$.subscribe(
|
||||
(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 {
|
||||
this._currentUser.unsubscribe();
|
||||
this.mobileQuery.removeListener(this._mobileQueryListener);
|
||||
@@ -78,8 +99,13 @@ export class FullComponent implements OnInit, OnDestroy {
|
||||
window.open('https://github.com/rampeur', '_blank');
|
||||
}
|
||||
|
||||
toggleShowQCMMenu() {
|
||||
this.showQCMMenu = !this.showQCMMenu;
|
||||
toggleShowMenu(state: string) {
|
||||
const indexOf: number = this.visibleMenu.indexOf(state);
|
||||
if (indexOf !== -1) {
|
||||
this.visibleMenu.splice(indexOf, 1);
|
||||
} else {
|
||||
this.visibleMenu.push(state);
|
||||
}
|
||||
}
|
||||
|
||||
logout() {
|
||||
|
||||
@@ -3,50 +3,67 @@ import { Injectable } from '@angular/core';
|
||||
import { Menu } from 'src/app/core/models/menu.model';
|
||||
|
||||
const MENUAUTH: Menu[] = [
|
||||
{ state: 'home', type: 'link', name: 'Accueil', icon: 'home', desc: '' },
|
||||
{ state: 'login', type: 'link', name: 'Se connecter', icon: 'fingerprint', desc: '' },
|
||||
{ state: 'register', type: 'link', name: 'Créer un compte', icon: 'person_add_alt', desc: '' }
|
||||
{ state: 'home', type: 'link', name: 'Accueil', icon: 'home', icontype: 'mat', 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', 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[] = [
|
||||
{ state: 'home', type: 'link', name: 'Accueil', icon: 'home', desc: '' },
|
||||
{ state: 'dashboard', type: 'link', name: 'Dashboard', icon: 'dashboard', desc: '' },
|
||||
{ state: 'logbook', type: 'link', name: 'Carnet de saut', icon: 'assignment_turned_in', desc: '' },
|
||||
{ state: 'aeronefs', type: 'link', name: 'Aéronefs', icon: 'flight', desc: '' },
|
||||
{ state: 'dropzones', type: 'link', name: 'Drop Zones', icon: 'flight_takeoff', desc: '' },
|
||||
{ state: 'canopies', type: 'link', name: 'Voiles', icon: 'paragliding', desc: '' },
|
||||
{ state: 'jumps', type: 'link', name: 'Sauts', icon: 'filter_drama', desc: '' },
|
||||
{ state: 'calculator', type: 'link', name: 'Taille de voile', icon: 'calculate', desc: '' },
|
||||
{ state: 'qcm', type: 'menu', name: 'QCM Brevets', icon: 'rule_folder', desc: '' }
|
||||
{ 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: '' }
|
||||
];
|
||||
|
||||
const MENUPJUMP: Menu[] = [
|
||||
{ state: '#', type: 'link', name: 'Modifier', icon: 'edit', desc: '', color: 'primary' },
|
||||
{ state: '#', type: 'link', name: 'Supprimer', icon: 'delete', 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' }
|
||||
{ state: '#', type: 'link', name: 'Modifier', icon: 'edit', icontype: 'mat', desc: '', color: 'primary' },
|
||||
{ state: '#', type: 'link', name: 'Supprimer', icon: 'delete', icontype: 'mat', desc: '', color: 'raspberry' }
|
||||
];
|
||||
|
||||
const MENUPANEL: Menu[] = [
|
||||
{ state: '#', type: 'link', name: 'Retirer du dashboard', icon: 'add_notes', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', 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', icontype: 'mat', desc: '' }
|
||||
];
|
||||
|
||||
const MENUCALCULATOR: Menu[] = [
|
||||
{ state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Partager', icon: 'share', desc: '' }
|
||||
{ state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', icontype: 'mat', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', icontype: 'mat', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Partager', icon: 'share', icontype: 'mat', desc: '' }
|
||||
];
|
||||
|
||||
const MENULOGBOOK: Menu[] = [
|
||||
{ state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Recherche par participant', icon: 'person_search', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Paramètres d\'affichage', icon: 'display_settings', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Import CSV', icon: 'upload_file', desc: '' }
|
||||
{ state: '#', type: 'link', name: 'Recherche avancée', icon: 'search', icontype: 'mat', 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', icontype: 'mat', desc: '' },
|
||||
{ state: '#', type: 'link', name: 'Import CSV', icon: 'upload_file', icontype: 'mat', desc: '' }
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
@@ -60,12 +77,12 @@ export class MenuItems {
|
||||
getMenuItems(): Menu[] {
|
||||
return MENUITEMS;
|
||||
}
|
||||
getMenuItemsAdmin(): Menu[] {
|
||||
return MENUITEMSADMIN;
|
||||
}
|
||||
getMenuLogbook(): Menu[] {
|
||||
return MENULOGBOOK;
|
||||
}
|
||||
getMenuQCM(): Menu[] {
|
||||
return MENUQCM;
|
||||
}
|
||||
getMenuPanel(): Menu[] {
|
||||
return MENUPANEL;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Article } from './article.model';
|
||||
|
||||
export interface Email {
|
||||
from: string;
|
||||
to: string;
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
article?: Article;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
export * from './aeronef.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-list.model';
|
||||
export * from './application-list-config.model';
|
||||
@@ -11,7 +14,12 @@ export * from './errors.model';
|
||||
export * from './jump.model';
|
||||
export * from './jump-list.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 './qcm.model';
|
||||
export * from './shared.model';
|
||||
export * from './tag.model';
|
||||
export * from './user.model';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
import { Profile } from './profile.model';
|
||||
import { PageLinks, PageMeta } from './shared.model';
|
||||
import { ColumnDefinition, PageLinks, PageMeta } from './shared.model';
|
||||
|
||||
export interface Jump {
|
||||
slug: string;
|
||||
@@ -200,16 +200,6 @@ export interface JumpsPageData {
|
||||
jumpsByModuleCount: number;*/
|
||||
}
|
||||
|
||||
export interface ColumnDefinition {
|
||||
key: string;
|
||||
type: string;
|
||||
label: string;
|
||||
required?: boolean;
|
||||
pattern?: string;
|
||||
min?: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
export const jumpColumns: Array<ColumnDefinition> = [
|
||||
{
|
||||
key: 'isSelected',
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Badge } from './badge.model';
|
||||
|
||||
export interface Menu {
|
||||
parent?: string;
|
||||
state: string;
|
||||
name: string;
|
||||
type: string;
|
||||
icon: string;
|
||||
icontype?: string;
|
||||
desc?: string;
|
||||
badge?: Badge[];
|
||||
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;
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
];
|
||||
@@ -28,3 +28,13 @@ export interface ReduceRow {
|
||||
key: string[];
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface ColumnDefinition {
|
||||
key: string;
|
||||
type: string;
|
||||
label: string;
|
||||
required?: boolean;
|
||||
pattern?: string;
|
||||
min?: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface Tag {
|
||||
name: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
@@ -5,9 +5,9 @@ export interface User {
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
phone: string;
|
||||
token: string;
|
||||
licence?: number;
|
||||
poids?: number;
|
||||
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));
|
||||
};
|
||||
@@ -1,4 +1,6 @@
|
||||
export * from './aeronefs-page-resolver.service';
|
||||
export * from './article-resolver.service';
|
||||
export * from './articles-resolver.service';
|
||||
export * from './auth-resolver.service';
|
||||
export * from './canopies-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-resolver.service';
|
||||
export * from './lastjump-resolver.service';
|
||||
export * from './product-resolver.service';
|
||||
export * from './products-resolver.service';
|
||||
export * from './profile-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));
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './aeronefs.service';
|
||||
export * from './api.service';
|
||||
export * from './applications.service';
|
||||
export * from './articles.service';
|
||||
export * from './calculator.service';
|
||||
export * from './canopies.service';
|
||||
export * from './dropzones.service';
|
||||
@@ -8,6 +9,7 @@ export * from './jumps.service';
|
||||
export * from './jump-table.service';
|
||||
export * from './jwt.service';
|
||||
export * from './pages.service';
|
||||
export * from './products.service';
|
||||
export * from './profiles.service';
|
||||
export * from './qcm.service';
|
||||
export * from './user.service';
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export class UserService implements OnDestroy {
|
||||
|
||||
attemptAuth(type: string, credentials?: { email: string; password: string; }): Observable<{ user: User }> {
|
||||
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(
|
||||
(data: any) => {
|
||||
this.setAuth(data.user);
|
||||
|
||||
@@ -3,19 +3,19 @@ import { Routes } from '@angular/router';
|
||||
import {
|
||||
AeronefsComponent, CalculatorComponent, CanopiesComponent, CredentialsComponent,
|
||||
DashboardComponent, DropzonesComponent, JumpComponent, JumpsComponent,
|
||||
LogbookComponent, ProfileComponent, QcmComponent, SettingsComponent
|
||||
LogbookComponent, PageComponent, PagesComponent, ProductComponent, ProductsComponent, ProfileComponent, QcmComponent, SettingsComponent
|
||||
} from 'src/app/components';
|
||||
import { authGuard } from 'src/app/core/guards';
|
||||
import { authGuard, adminGuard } from 'src/app/core/guards';
|
||||
import {
|
||||
aeronefsPageResolver, authResolver, canopiesPageResolver, dropZonesPageResolver,
|
||||
jumpResolver, jumpsPageResolver, lastjumpResolver, profileResolver, qcmResolver
|
||||
aeronefsPageResolver, articleResolver, articlesResolver, authResolver, canopiesPageResolver, dropZonesPageResolver,
|
||||
jumpResolver, jumpsPageResolver, lastjumpResolver, productResolver, productsResolver, profileResolver, qcmResolver
|
||||
} from 'src/app/core/resolvers';
|
||||
|
||||
export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'aeronefs',
|
||||
component: AeronefsComponent,
|
||||
canActivate: [ authGuard ],
|
||||
canActivate: [ authGuard, adminGuard ],
|
||||
resolve: {
|
||||
aeronefsPageData: aeronefsPageResolver
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'canopies',
|
||||
component: CanopiesComponent,
|
||||
canActivate: [ authGuard ],
|
||||
canActivate: [ authGuard, adminGuard ],
|
||||
resolve: {
|
||||
canopiesPageData: canopiesPageResolver
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: DashboardComponent,
|
||||
canActivate: [ authGuard ],
|
||||
canActivate: [ authGuard, adminGuard ],
|
||||
resolve: {
|
||||
isAuthenticated: authResolver
|
||||
}
|
||||
@@ -47,7 +47,7 @@ export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'dropzones',
|
||||
component: DropzonesComponent,
|
||||
canActivate: [ authGuard ],
|
||||
canActivate: [ authGuard, adminGuard ],
|
||||
resolve: {
|
||||
dropZonesPageData: dropZonesPageResolver
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'jumps',
|
||||
component: JumpsComponent,
|
||||
canActivate: [ authGuard ],
|
||||
canActivate: [ authGuard, adminGuard ],
|
||||
resolve: {
|
||||
pageData: jumpsPageResolver
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'jump/:slug',
|
||||
component: JumpComponent,
|
||||
canActivate: [ authGuard ],
|
||||
canActivate: [ authGuard, adminGuard ],
|
||||
resolve: {
|
||||
pageData: jumpResolver
|
||||
}
|
||||
@@ -71,12 +71,52 @@ export const AUTH_ROUTES: Routes = [
|
||||
{
|
||||
path: 'logbook',
|
||||
component: LogbookComponent,
|
||||
canActivate: [ authGuard ],
|
||||
canActivate: [ authGuard, adminGuard ],
|
||||
resolve: {
|
||||
isAuthenticated: authResolver,
|
||||
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',
|
||||
component: ProfileComponent,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
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 { articleResolver, articlesResolver } from 'src/app/core/resolvers';
|
||||
|
||||
export const NOAUTH_ROUTES: Routes = [
|
||||
{
|
||||
@@ -18,6 +19,22 @@ export const NOAUTH_ROUTES: Routes = [
|
||||
component: AuthComponent,
|
||||
canActivate: [ noauthGuard ]
|
||||
},
|
||||
{
|
||||
path: 'pages',
|
||||
component: PagesComponent,
|
||||
canActivate: [ noauthGuard ],
|
||||
resolve: {
|
||||
pageData: articlesResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'page/:slug',
|
||||
component: PageComponent,
|
||||
canActivate: [ noauthGuard ],
|
||||
resolve: {
|
||||
pageData: articleResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'home',
|
||||
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
@@ -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>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 465 B |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 19 KiB |