Implémentation SkydiverId API
This commit is contained in:
@@ -1,51 +1,49 @@
|
||||
<div @flyInOut>
|
||||
<div class="content">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuAeronef" aria-label="Menu Aeronef">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuAeronef="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-4">
|
||||
@if (displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xl-4 col-lg-5 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-8 col-lg-7 col-md-6 col-sm-12">
|
||||
<!--<app-bar-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [colors]="seriesColor" label="Nombre total de sauts" [legend]="false"></app-bar-chart>-->
|
||||
<app-bars-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor" [legend]="false"></app-bars-chart>
|
||||
</div>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuAeronef" aria-label="Menu Aeronef">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuAeronef="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill py-2">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [rows]="seriesRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-4">
|
||||
@if (displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xl-4 col-lg-5 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-8 col-lg-7 col-md-6 col-sm-12">
|
||||
<!--<app-bar-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [colors]="seriesColor" label="Nombre total de sauts" [legend]="false"></app-bar-chart>-->
|
||||
<app-bars-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor" [legend]="false"></app-bars-chart>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill py-2">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [rows]="seriesRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="container content auth-page">
|
||||
<div class="container auth-page">
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-3 col-xs-12">
|
||||
<h1 class="text-xs-center">{{ title }}</h1>
|
||||
|
||||
@@ -1,259 +1,257 @@
|
||||
<div @flyInOut>
|
||||
<div class="calculator-page content">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span class="me-2 fs-6"><span class="fs-5 mx-1">DT48</span> - 08 février 2024</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuCalculator" aria-label="Menu Calculator">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuCalculator="matMenu">
|
||||
@for (menuitem of menuItems.getMenuCalculator(); track menuitem) {
|
||||
@if (menuitem.type === 'link') {
|
||||
<button mat-menu-item [routerLink]="['/', menuitem.state]">
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span class="me-2 fs-6"><span class="fs-5 mx-1">DT48</span> - 08 février 2024</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuCalculator" aria-label="Menu Calculator">
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuCalculator="matMenu">
|
||||
@for (menuitem of menuItems.getMenuCalculator(); track menuitem) {
|
||||
@if (menuitem.type === 'link') {
|
||||
<button mat-menu-item [routerLink]="['/', menuitem.state]">
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card class="calculator">
|
||||
<mat-card-header class="bg-warn rounded-top pt-1 pb-2 px-2">
|
||||
<form #searchFormData="ngForm" class="inputs-form">
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Nombre de sauts</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.jumps" (change)="refresh()" type="number" min="0" max="2000"
|
||||
step="1" name="jumps">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Poids nu</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.weight" (change)="refresh()" type="number" min="30"
|
||||
max="110" name="weight">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Poids équipement</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.gear" (change)="refresh()" type="number" min="0"
|
||||
name="gear">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Taille actuelle</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.current" (change)="refresh()" type="number" min="0"
|
||||
name="current" id="current" placeholder="Taille actuelle">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="flex-fill"></div>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="my-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-8 col-sm-12">
|
||||
<table class="table table-striped table-dark table-hover mat-elevation-z2">
|
||||
<thead>
|
||||
<tr class="warn">
|
||||
<th class="first"></th>
|
||||
<th class="text-nowrap">Taille actuelle</th>
|
||||
<th class="text-nowrap">Taille min</th>
|
||||
<th class="text-nowrap">Taille min -11%</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="warn">
|
||||
<th class="first">Taille de voile en ft<sup>2</sup></th>
|
||||
<td class="font-monospace text-{{ info.state.color }}"> ●
|
||||
{{ info.sizesFeet.current }} </td>
|
||||
<td class="font-monospace text-success"> ● {{ info.sizesFeet.min }} </td>
|
||||
<td class="font-monospace text-warning"> ● {{ info.sizesFeet.min11 }} </td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Charge alaire</th>
|
||||
<td class="font-monospace">{{ info.charges.current | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.charges.min | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.charges.min11 | number : '1.2-3' }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Taille de voile en m<sup>2</sup></th>
|
||||
<td class="font-monospace">{{ info.sizesMeter.current | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.sizesMeter.min | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.sizesMeter.min11 | number : '1.2-3' }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Coefficient kilo/livres</th>
|
||||
<td colspan="3" class="font-monospace text-start">{{ coeffKgLbs }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Coefficient pied/mètre</th>
|
||||
<td colspan="3" class="font-monospace text-start">{{ coeffFtM }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Formule charge alaire</th>
|
||||
<td colspan="3" class="font-monospace text-start">
|
||||
( ( {{ inputs.weight }} + {{ inputs.gear }} ) * {{ coeffKgLbs }} ) /
|
||||
{{ inputs.current }} = {{ info.charges.current | number : '1.5' }}<br />
|
||||
( ( poids nu en kg + poids equipement en kg ) * coefficient kg/lbs ) / taille de
|
||||
voile en ft<sup>2</sup>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-8 col-sm-12">
|
||||
@if (displayCharts) {
|
||||
<div class="linechart position-relative w-100 mt-3">
|
||||
<canvas baseChart class="mx-auto" width="800" height="280"
|
||||
[data]="chartConfig.lineChartData" [options]="chartConfig.lineChartOptions"
|
||||
[legend]="chartConfig.lineChartLegend" [type]="'line'">
|
||||
</canvas>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<h4 class="py-2 mb-0">Tailles de voile pour {{ inputs.weight }} kg</h4>
|
||||
<div>
|
||||
Prochain changement de valeurs dans {{ ((this.currentRange.end + 1) - inputs.jumps) }}
|
||||
{{ ((this.currentRange.end + 1) - inputs.jumps) > 1 ? 'sauts' : 'saut'}}<br />
|
||||
</div>
|
||||
<div class="text-end">
|
||||
{{ ((((inputs.jumps - this.currentRange.start) + 1) / ((this.currentRange.end - this.currentRange.start) + 1)) * 100) | number : '1.2-2' }}%
|
||||
de la tranche actuelle ({{this.currentRange.start}} à {{this.currentRange.end}})
|
||||
</div>
|
||||
<mat-progress-bar mode="determinate"
|
||||
[value]="((((inputs.jumps - this.currentRange.start) + 1) / ((this.currentRange.end - this.currentRange.start) + 1)) * 100)"
|
||||
color="warn"></mat-progress-bar>
|
||||
<table class="table table-striped table-dark table-hover mat-elevation-z2 mb-1">
|
||||
<thead>
|
||||
<tr class="warn">
|
||||
<th class="first"></th>
|
||||
@for (header of sizesHeader; track header; let index = $index) {
|
||||
<th class="pl-3 pr-3 {{ tableHeader[(index+1)].active }}">{{ header.name }}</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="warn">
|
||||
<th class="first">Taille de voile en ft<sup>2</sup></th>
|
||||
@for (value of sizesValuesFeet; track value; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">{{ value }}</td>
|
||||
}
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Abbattement 11%</th>
|
||||
@for (value of sizesMinValuesFeet; track value; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">{{ value }}</td>
|
||||
}
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Charge alaire</th>
|
||||
@for (charge of sizesCharges; track charge; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">
|
||||
{{ charge | number : '1.2-3' }}</td>
|
||||
}
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="first">Taille de voile en m<sup>2</sup></th>
|
||||
@for (value of sizesValuesMeter; track value; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">
|
||||
{{ value | number : '1.2-2' }}</td>
|
||||
}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<h4 class="py-2 mb-0">Tableau des tailles de voile par poids</h4>
|
||||
<div class="text-end">
|
||||
{{ ((inputs.jumps / 1600) * 100) | number : '1.2-2' }}% de la plage complète (0 à 1600)
|
||||
</div>
|
||||
<mat-progress-bar mode="determinate" [value]="((inputs.jumps / 1600) * 100)" color="warn">
|
||||
</mat-progress-bar>
|
||||
<table mat-table [dataSource]="canopy_size_table" matSort
|
||||
class="table table-striped table-dark table-hover mat-elevation-z2">
|
||||
<!-- weight column -->
|
||||
<ng-container matColumnDef="weight">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header class="first {{tableHeader[0].active}}">
|
||||
{{tableHeader[0].name}} </th>
|
||||
<th mat-cell *matCellDef="let element;" class="text-end ps-3 pe-4 fs-6" role="button"
|
||||
(click)="setCurrentWeight(element.weight)">
|
||||
<span class="text-warn d-block">{{element.weight}}</span>
|
||||
</th>
|
||||
</ng-container>
|
||||
<!-- range_1 column -->
|
||||
<ng-container matColumnDef="range_1">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[1].active}}">
|
||||
{{tableHeader[1].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[0].active}}">{{element.ranges[0].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_2 column -->
|
||||
<ng-container matColumnDef="range_2">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[2].active}}">
|
||||
{{tableHeader[2].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[1].active}}">{{element.ranges[1].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_3 column -->
|
||||
<ng-container matColumnDef="range_3">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[3].active}}">
|
||||
{{tableHeader[3].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[2].active}}">{{element.ranges[2].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_4 column -->
|
||||
<ng-container matColumnDef="range_4">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[4].active}}">
|
||||
{{tableHeader[4].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[3].active}}">{{element.ranges[3].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_5 column -->
|
||||
<ng-container matColumnDef="range_5">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[5].active}}">
|
||||
{{tableHeader[5].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[4].active}}">{{element.ranges[4].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_6 column -->
|
||||
<ng-container matColumnDef="range_6">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[6].active}}">
|
||||
{{tableHeader[6].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[5].active}}">{{element.ranges[5].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_7 column -->
|
||||
<ng-container matColumnDef="range_7">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[7].active}}">
|
||||
{{tableHeader[7].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[6].active}}">{{element.ranges[6].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_8 column -->
|
||||
<ng-container matColumnDef="range_8">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[8].active}}">
|
||||
{{tableHeader[8].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[7].active}}">{{element.ranges[7].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_9 column -->
|
||||
<ng-container matColumnDef="range_9">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[9].active}}">
|
||||
{{tableHeader[9].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[8].active}}">{{element.ranges[8].value}}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: false" class="warn"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||
class="text-middle font-monospace warn {{row.active}}" id="weight_{{row.weight}}"></tr>
|
||||
</table>
|
||||
<div class="clearfix">
|
||||
<mat-paginator [pageSize]="50" [pageSizeOptions]="[5, 10, 15, 25, 50]" showFirstLastButtons
|
||||
class="pull-right mb-1 mt-2"></mat-paginator>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card class="calculator">
|
||||
<mat-card-header class="bg-warn rounded-top pt-1 pb-2 px-2">
|
||||
<form #searchFormData="ngForm" class="inputs-form">
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Nombre de sauts</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.jumps" (change)="refresh()" type="number" min="0" max="2000"
|
||||
step="1" name="jumps">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Poids nu</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.weight" (change)="refresh()" type="number" min="30"
|
||||
max="110" name="weight">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Poids équipement</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.gear" (change)="refresh()" type="number" min="0"
|
||||
name="gear">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="bg-crystal me-3" subscriptSizing="dynamic" color="accent">
|
||||
<mat-label>Taille actuelle</mat-label>
|
||||
<input matInput [(ngModel)]="inputs.current" (change)="refresh()" type="number" min="0"
|
||||
name="current" id="current" placeholder="Taille actuelle">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="flex-fill"></div>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="my-4">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-8 col-sm-12">
|
||||
<table class="table table-striped table-dark table-hover mat-elevation-z2">
|
||||
<thead>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4"></th>
|
||||
<th class="text-nowrap">Taille actuelle</th>
|
||||
<th class="text-nowrap">Taille min</th>
|
||||
<th class="text-nowrap">Taille min -11%</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Taille de voile en ft<sup>2</sup></th>
|
||||
<td class="font-monospace text-{{ info.state.color }}"> ●
|
||||
{{ info.sizesFeet.current }} </td>
|
||||
<td class="font-monospace text-success"> ● {{ info.sizesFeet.min }} </td>
|
||||
<td class="font-monospace text-warning"> ● {{ info.sizesFeet.min11 }} </td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Charge alaire</th>
|
||||
<td class="font-monospace">{{ info.charges.current | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.charges.min | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.charges.min11 | number : '1.2-3' }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Taille de voile en m<sup>2</sup></th>
|
||||
<td class="font-monospace">{{ info.sizesMeter.current | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.sizesMeter.min | number : '1.2-3' }}</td>
|
||||
<td class="font-monospace">{{ info.sizesMeter.min11 | number : '1.2-3' }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Coefficient kilo/livres</th>
|
||||
<td colspan="3" class="font-monospace text-start">{{ coeffKgLbs }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Coefficient pied/mètre</th>
|
||||
<td colspan="3" class="font-monospace text-start">{{ coeffFtM }}</td>
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Formule charge alaire</th>
|
||||
<td colspan="3" class="font-monospace text-start">
|
||||
( ( {{ inputs.weight }} + {{ inputs.gear }} ) * {{ coeffKgLbs }} ) /
|
||||
{{ inputs.current }} = {{ info.charges.current | number : '1.5' }}<br />
|
||||
( ( poids nu en kg + poids equipement en kg ) * coefficient kg/lbs ) / taille de
|
||||
voile en ft<sup>2</sup>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-8 col-sm-12">
|
||||
@if (displayCharts) {
|
||||
<div class="linechart position-relative w-100 mt-3">
|
||||
<canvas baseChart class="mx-auto" width="800" height="280"
|
||||
[data]="chartConfig.lineChartData" [options]="chartConfig.lineChartOptions"
|
||||
[legend]="chartConfig.lineChartLegend" [type]="'line'">
|
||||
</canvas>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<h4 class="py-2 mb-0">Tailles de voile pour {{ inputs.weight }} kg</h4>
|
||||
<div>
|
||||
Prochain changement de valeurs dans {{ ((this.currentRange.end + 1) - inputs.jumps) }}
|
||||
{{ ((this.currentRange.end + 1) - inputs.jumps) > 1 ? 'sauts' : 'saut'}}<br />
|
||||
</div>
|
||||
<div class="text-end">
|
||||
{{ ((((inputs.jumps - this.currentRange.start) + 1) / ((this.currentRange.end - this.currentRange.start) + 1)) * 100) | number : '1.2-2' }}%
|
||||
de la tranche actuelle ({{this.currentRange.start}} à {{this.currentRange.end}})
|
||||
</div>
|
||||
<mat-progress-bar mode="determinate"
|
||||
[value]="((((inputs.jumps - this.currentRange.start) + 1) / ((this.currentRange.end - this.currentRange.start) + 1)) * 100)"
|
||||
color="warn"></mat-progress-bar>
|
||||
<table class="table table-striped table-dark table-hover mat-elevation-z2 mb-1">
|
||||
<thead>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4"></th>
|
||||
@for (header of sizesHeader; track header; let index = $index) {
|
||||
<th class="pl-3 pr-3 {{ tableHeader[(index+1)].active }}">{{ header.name }}</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="warn">
|
||||
<th class="firtext-nowrap pe-4st">Taille de voile en ft<sup>2</sup></th>
|
||||
@for (value of sizesValuesFeet; track value; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">{{ value }}</td>
|
||||
}
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Abbattement 11%</th>
|
||||
@for (value of sizesMinValuesFeet; track value; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">{{ value }}</td>
|
||||
}
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Charge alaire</th>
|
||||
@for (charge of sizesCharges; track charge; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">
|
||||
{{ charge | number : '1.2-3' }}</td>
|
||||
}
|
||||
</tr>
|
||||
<tr class="warn">
|
||||
<th class="text-nowrap pe-4">Taille de voile en m<sup>2</sup></th>
|
||||
@for (value of sizesValuesMeter; track value; let index = $index) {
|
||||
<td class="pl-3 text-nowrap {{ tableHeader[(index+1)].active }}">
|
||||
{{ value | number : '1.2-2' }}</td>
|
||||
}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<h4 class="py-2 mb-0">Tableau des tailles de voile par poids</h4>
|
||||
<div class="text-end">
|
||||
{{ ((inputs.jumps / 1600) * 100) | number : '1.2-2' }}% de la plage complète (0 à 1600)
|
||||
</div>
|
||||
<mat-progress-bar mode="determinate" [value]="((inputs.jumps / 1600) * 100)" color="warn">
|
||||
</mat-progress-bar>
|
||||
<table mat-table [dataSource]="canopy_size_table" matSort
|
||||
class="table table-striped table-dark table-hover mat-elevation-z2">
|
||||
<!-- weight column -->
|
||||
<ng-container matColumnDef="weight">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header class="text-nowrap pe-4 {{tableHeader[0].active}}">
|
||||
{{tableHeader[0].name}} </th>
|
||||
<th mat-cell *matCellDef="let element;" class="text-end ps-3 pe-4 fs-6" role="button"
|
||||
(click)="setCurrentWeight(element.weight)">
|
||||
<span class="text-warn d-block">{{element.weight}}</span>
|
||||
</th>
|
||||
</ng-container>
|
||||
<!-- range_1 column -->
|
||||
<ng-container matColumnDef="range_1">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[1].active}}">
|
||||
{{tableHeader[1].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[0].active}}">{{element.ranges[0].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_2 column -->
|
||||
<ng-container matColumnDef="range_2">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[2].active}}">
|
||||
{{tableHeader[2].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[1].active}}">{{element.ranges[1].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_3 column -->
|
||||
<ng-container matColumnDef="range_3">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[3].active}}">
|
||||
{{tableHeader[3].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[2].active}}">{{element.ranges[2].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_4 column -->
|
||||
<ng-container matColumnDef="range_4">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[4].active}}">
|
||||
{{tableHeader[4].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[3].active}}">{{element.ranges[3].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_5 column -->
|
||||
<ng-container matColumnDef="range_5">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[5].active}}">
|
||||
{{tableHeader[5].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[4].active}}">{{element.ranges[4].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_6 column -->
|
||||
<ng-container matColumnDef="range_6">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[6].active}}">
|
||||
{{tableHeader[6].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[5].active}}">{{element.ranges[5].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_7 column -->
|
||||
<ng-container matColumnDef="range_7">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[7].active}}">
|
||||
{{tableHeader[7].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[6].active}}">{{element.ranges[6].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_8 column -->
|
||||
<ng-container matColumnDef="range_8">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[8].active}}">
|
||||
{{tableHeader[8].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[7].active}}">{{element.ranges[7].value}}</td>
|
||||
</ng-container>
|
||||
<!-- range_9 column -->
|
||||
<ng-container matColumnDef="range_9">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef class="{{tableHeader[9].active}}">
|
||||
{{tableHeader[9].name}} </th>
|
||||
<td mat-cell *matCellDef="let element;"
|
||||
class="pl-3 text-nowrap fs-6 {{element.ranges[8].active}}">{{element.ranges[8].value}}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: false" class="warn"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||
class="text-middle font-monospace warn {{row.active}}" id="weight_{{row.weight}}"></tr>
|
||||
</table>
|
||||
<div class="clearfix">
|
||||
<mat-paginator [pageSize]="50" [pageSizeOptions]="[5, 10, 15, 25, 50]" showFirstLastButtons
|
||||
class="pull-right mb-1 mt-2"></mat-paginator>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
@@ -1,58 +1,56 @@
|
||||
<div @flyInOut>
|
||||
<div class="content">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuCanopyModel" aria-label="Menu Canopy Model">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuCanopyModel="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-4">
|
||||
@if (displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-lg-6 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesModelHeader" [names]="seriesModelName" [values]="seriesModelValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-3 col-lg-6 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-12 col-md-12">
|
||||
<!--<app-pie-chart [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeValue" [colors]="seriesColor"></app-pie-chart>-->
|
||||
<app-bars-chart [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeRow" [colors]="seriesColor" [legend]="false"></app-bars-chart>
|
||||
</div>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuCanopyModel" aria-label="Menu Canopy Model">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuCanopyModel="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-12">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesModelHeader" [names]="seriesModelName" [values]="seriesModelValue" [rows]="seriesModelRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-12">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeValue" [rows]="seriesSizeRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-4">
|
||||
@if (displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-lg-6 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesModelHeader" [names]="seriesModelName" [values]="seriesModelValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-3 col-lg-6 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-12 col-md-12">
|
||||
<!--<app-pie-chart [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeValue" [colors]="seriesColor"></app-pie-chart>-->
|
||||
<app-bars-chart [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeRow" [colors]="seriesColor" [legend]="false"></app-bars-chart>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-12">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesModelHeader" [names]="seriesModelName" [values]="seriesModelValue" [rows]="seriesModelRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-12">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesSizeHeader" [names]="seriesSizeName" [values]="seriesSizeValue" [rows]="seriesSizeRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
@@ -1,38 +1,36 @@
|
||||
<div class="content">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fst-italic0 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<ng-container *appShowAuthed="true">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<app-jumps-by-month></app-jumps-by-month>
|
||||
</div>
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fst-italic0 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<ng-container *appShowAuthed="true">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<app-jumps-by-month></app-jumps-by-month>
|
||||
</div>
|
||||
<div class="row mt-sm-2 mt-lg-3">
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<app-drop-zones></app-drop-zones>
|
||||
</div>
|
||||
<div class="col-md-8 col-sm-12">
|
||||
<app-drop-zones-bar></app-drop-zones-bar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-sm-2 mt-lg-3">
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<app-drop-zones></app-drop-zones>
|
||||
</div>
|
||||
<div class="row mt-sm-2 mt-lg-3">
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<app-aeronefs></app-aeronefs>
|
||||
</div>
|
||||
<div class="col-md-8 col-sm-12">
|
||||
<app-aeronefs-bar></app-aeronefs-bar>
|
||||
</div>
|
||||
<div class="col-md-8 col-sm-12">
|
||||
<app-drop-zones-bar></app-drop-zones-bar>
|
||||
</div>
|
||||
<div class="row mt-sm-2 mt-lg-3">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-canopy-models></app-canopy-models>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-canopy-sizes></app-canopy-sizes>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-sm-2 mt-lg-3">
|
||||
<div class="col-md-4 col-sm-12">
|
||||
<app-aeronefs></app-aeronefs>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="col-md-8 col-sm-12">
|
||||
<app-aeronefs-bar></app-aeronefs-bar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-sm-2 mt-lg-3">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-canopy-models></app-canopy-models>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-canopy-sizes></app-canopy-sizes>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -1,57 +1,55 @@
|
||||
<div @flyInOut>
|
||||
<div class="content">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuDropzone" aria-label="Menu Dropzone">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuDropzone="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-4">
|
||||
@if (displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xl-4 col-lg-5 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-8 col-lg-7 col-md-6 col-sm-12">
|
||||
<app-bars-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor" [legend]="false"></app-bars-chart>
|
||||
<!--
|
||||
@if (displayCharts) {
|
||||
<div class="barchart position-relative w-100 my-1">
|
||||
<x-chartist [configuration]="chartBarConfig"></x-chartist>
|
||||
</div>
|
||||
}
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuDropzone" aria-label="Menu Dropzone">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuDropzone="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill py-2">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [rows]="seriesRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-4">
|
||||
@if (displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xl-4 col-lg-5 col-md-6 col-sm-12">
|
||||
<app-pie-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [colors]="seriesColor"></app-pie-chart>
|
||||
</div>
|
||||
<div class="col-xl-8 col-lg-7 col-md-6 col-sm-12">
|
||||
<app-bars-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor" [legend]="false"></app-bars-chart>
|
||||
<!--
|
||||
@if (displayCharts) {
|
||||
<div class="barchart position-relative w-100 my-1">
|
||||
<x-chartist [configuration]="chartBarConfig"></x-chartist>
|
||||
</div>
|
||||
}
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill py-2">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<app-history-table [headers]="seriesHeader" [names]="seriesName" [values]="seriesValue" [rows]="seriesRow" [colors]="seriesColorClass"></app-history-table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
|
||||
/* Home */
|
||||
//@use 'variable' as var;
|
||||
|
||||
|
||||
/*
|
||||
#FF41F8
|
||||
#F0060B
|
||||
#F0070C
|
||||
#CC26D5
|
||||
#7702FF
|
||||
|
||||
#35A2EB
|
||||
#B7DDD0
|
||||
#B0D9CB
|
||||
#D9CAAE
|
||||
#9788C7
|
||||
*/
|
||||
|
||||
@@ -41,6 +41,8 @@ export class HomeComponent implements OnInit {
|
||||
public errors: Errors = { errors: {} };
|
||||
public lastJump: Jump = {} as Jump;
|
||||
public destroyRef = inject(DestroyRef);
|
||||
public now = new Date();
|
||||
public isJumpDay = false;
|
||||
|
||||
constructor(
|
||||
private _jumpsService: JumpsService
|
||||
@@ -50,6 +52,12 @@ export class HomeComponent implements OnInit {
|
||||
this._lastjump$ = this._jumpsService.getLastJump().pipe(takeUntilDestroyed(this.destroyRef));
|
||||
this._lastjump = this._lastjump$.subscribe((jump) => {
|
||||
this.lastJump = jump;
|
||||
this.isJumpDay = this._isToday(new Date(this.lastJump.date));
|
||||
});
|
||||
}
|
||||
private _isToday(someDate: Date) {
|
||||
return someDate.getDate() == this.now.getDate() &&
|
||||
someDate.getMonth() == this.now.getMonth() &&
|
||||
someDate.getFullYear() == this.now.getFullYear()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export * from './dashboard/dashboard.component';
|
||||
export * from './dropzone/dropzone.component';
|
||||
export * from './home/home.component';
|
||||
export * from './jump/jump.component';
|
||||
export * from './jumps/jumps.component';
|
||||
export * from './logbook/logbook.component';
|
||||
export * from './profile/profile.component';
|
||||
export * from './qcm/qcm.component';
|
||||
|
||||
@@ -1,132 +1,283 @@
|
||||
<div @flyInOut>
|
||||
<div class="content">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuAeronef" aria-label="Menu Aeronef">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">
|
||||
{{title}}
|
||||
<span class="fs-3 text-accent ms-2">{{ jump.categorie }} {{ jump.module }}</span>
|
||||
<span class="fs-3 text-muted ms-2">{{ jump.voile }} {{ jump.taille }}</span>
|
||||
</h1>
|
||||
<span class="me-2 fs-6">
|
||||
Le <span class="fs-5 mx-1 text-primary">{{ jump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
à <span class="fs-5 mx-1 text-primary">{{ jump.lieu }} {{ jump.oaci }}</span>
|
||||
@if (jump.x2data) {
|
||||
depuis <span class="fs-5 mx-1 text-primary">{{ jump.aeronef }} {{ jump.imat }}</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-4">
|
||||
<button mat-button [routerLink]="['/jump', prevSlug]" aria-label="Saut précédent" color="accent">
|
||||
<mat-icon fontIcon="navigate_before"></mat-icon> {{ jump.numero - 1}}
|
||||
</button>
|
||||
<div class="vr mx-2 fs-3 align-middle"></div>
|
||||
<span class="mx-2 align-middle">{{ jump.numero }}</span>
|
||||
<div class="vr mx-2 fs-3 align-middle"></div>
|
||||
<button mat-button [routerLink]="['/jump', nextSlug]" aria-label="Saut suivant" color="accent">
|
||||
<mat-icon fontIcon="navigate_next" iconPositionEnd></mat-icon> {{ jump.numero +1}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuJump" aria-label="Menu saut">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuJump="matMenu">
|
||||
@if (jump.x2data) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon fontIcon="language" color="accent"></mat-icon>
|
||||
<span class="text-accent fw-medium">Obtenir le KML</span>
|
||||
</button>
|
||||
<mat-menu #menuAeronef="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-2">
|
||||
@if(displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xxl-6 col-xs-12">
|
||||
<app-linearea-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor"></app-linearea-chart>
|
||||
</div>
|
||||
<div class="col-xxl-6 col-xs-12">
|
||||
<app-bars-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor" [legend]="true"></app-bars-chart>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<table class="table table-striped table-dark table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th *ngFor='let name of seriesHeader; let i=index' class="text-end"> {{ name }} </th>
|
||||
<th class="text-end">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor='let values of seriesRow; let i=index'>
|
||||
<th class="{{seriesColorClass[i]}} text-end"> ● {{ seriesName[i] }} </th>
|
||||
<td *ngFor='let value of values; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value }}</span></ng-template>
|
||||
</td>
|
||||
<th class="{{seriesColorClass[i]}} font-monospace text-end">{{ seriesRowTotal[i] }}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th class="text-end">Total</th>
|
||||
<th *ngFor='let value of seriesColTotal; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value }}</span></ng-template>
|
||||
</th>
|
||||
<th class="font-monospace text-end">{{ grandTotal }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-end">Moyenne</th>
|
||||
<th *ngFor='let value of seriesRowAvg; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value | number : '1.0-1' }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value | number : '1.0-1' }}</span></ng-template>
|
||||
</th>
|
||||
<th class="font-monospace text-end">{{ grandAvg | number : '1.0-1' }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-end">Moyenne <small>3 dernières années</small></th>
|
||||
<th *ngFor='let value of seriesRowAvgLastYears; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value | number : '1.0-1' }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value | number : '1.0-1' }}</span></ng-template>
|
||||
</th>
|
||||
<th class="font-monospace text-end">{{ grandAvgLastYears | number : '1.0-1' }}</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="mt-3">
|
||||
<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"></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-divider></mat-divider>
|
||||
}
|
||||
@for (menuitem of menuItems.getMenuJump(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon" [color]="menuitem.color"></mat-icon>
|
||||
<span class="text-{{menuitem.color}} fw-medium">{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-light-info fw-normal">Aeronef:</dt>
|
||||
<dd>{{ jump.aeronef }} {{ jump.imat }}</dd>
|
||||
<dt class="text-light-info fw-normal">Voile:</dt>
|
||||
<dd>{{ jump.voile }} {{ jump.taille }} ft<sup>2</sup></dd>
|
||||
@if (jump.programme) {
|
||||
<dt class="text-light-info fw-normal">Programme:</dt>
|
||||
<dd>{{ jump.programme }}</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
@if (!jump.x2data) {
|
||||
<dt class="text-light-info fw-normal">Hauteur:</dt>
|
||||
<dd>{{ jump.hauteur }} m</dd>
|
||||
<dt class="text-light-info fw-normal">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>
|
||||
<dd>{{ file.name }}</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
@if (jump.x2data) {
|
||||
<mat-card class="mt-3">
|
||||
<mat-card-content>
|
||||
<div class="row my-3">
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6>Hauteur</h6>
|
||||
@if (jump.x2data.altitude.exit > 4200) {
|
||||
<mat-icon class="align-middle fs-5 me-2" fontIcon="mood" color="primary"></mat-icon>
|
||||
}
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.altitude.exit }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6 class="d-flex justify-content-center"><mat-icon fontIcon="timer" class="me-1 fs-5"></mat-icon> Durée de la chute</h6>
|
||||
<span class="text-primary fw-medium fs-4">{{ formatDuration(jump.x2data.duration.freeFall) }}</span>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6>Déploiement</h6>
|
||||
@if (jump.x2data.altitude.deployment < 650) {
|
||||
<mat-icon class="align-middle fs-5 me-2" fontIcon="warning" color="raspberry"></mat-icon>
|
||||
<span class="text-raspberry fw-medium fs-5">{{ jump.x2data.altitude.deployment }} m</span>
|
||||
}
|
||||
@else if (jump.x2data.altitude.deployment < 750) {
|
||||
<mat-icon class="align-middle fs-5 me-2" fontIcon="local_florist" color="warn"></mat-icon>
|
||||
<span class="text-warn fw-medium fs-5">{{ jump.x2data.altitude.deployment }} m</span>
|
||||
}
|
||||
@else if (jump.x2data.altitude.deployment > 1000) {
|
||||
<mat-icon class="align-middle fs-5 me-2" fontIcon="mood" color="primary"></mat-icon>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.altitude.deployment }} m</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="{{ jump.accessoires ? 'col-sm-6 ' : '' }}col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="people" class="me-2"></mat-icon> Participants ({{ jump.sautants.length + 1 }})</h5>
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<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="person"></mat-icon>
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
@if (jump.accessoires) {
|
||||
<div class="col-sm-6 col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="auto_awesome" class="me-2"></mat-icon> Accessoires</h5>
|
||||
<span class="badge rounded-pill bg-navy-light mb-2 me-1 pe-3 py-1 fw-normal">
|
||||
<mat-icon class="align-middle me-1" fontIcon="auto_awesome"></mat-icon>
|
||||
{{ jump.accessoires }}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="mt-3">
|
||||
<mat-card-content>
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="speed" class="me-1"></mat-icon> Vitesse en chute</h5>
|
||||
</div>
|
||||
<div class="col-sm-6 hidden-xs text-center">
|
||||
<h6>Verticale</h6>
|
||||
</div>
|
||||
<div class="col-sm-6 hidden-xs text-center">
|
||||
<h6>Horizontale</h6>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse maximale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.vertical.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.vertical.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse maximale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.horizontal.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.horizontal.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="mt-3">
|
||||
<mat-card-content>
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="speed" class="me-1"></mat-icon> Vitesse sous voile</h5>
|
||||
</div>
|
||||
<div class="col-sm-6 hidden-xs text-center">
|
||||
<h6>Verticale</h6>
|
||||
</div>
|
||||
<div class="col-sm-6 hidden-xs text-center">
|
||||
<h6>Horizontale</h6>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse maximale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.vertical.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.vertical.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse maximale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.horizontal.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Vitesse moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.horizontal.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="mt-3">
|
||||
<mat-card-content>
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="square_foot" class="me-1"></mat-icon> Finesse</h5>
|
||||
</div>
|
||||
<div class="col-sm-6 hidden-xs text-center">
|
||||
<h6>En chute</h6>
|
||||
</div>
|
||||
<div class="col-sm-6 hidden-xs text-center">
|
||||
<h6>Sous voile</h6>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Max</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.freeFall.max | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.freeFall.avg | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Max</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.underCanopy.max | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.underCanopy.avg | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="mt-3">
|
||||
<mat-card-content>
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="straighten" class="me-1"></mat-icon> Distance</h5>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance au largage</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.exitFromLandingZone| number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Durée sous voile</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ formatDuration(jump.x2data.duration.underCanopy) }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance totale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.totalFlying | number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.totalHorizontal | number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
@else {
|
||||
<mat-card class="mt-3">
|
||||
<mat-card-content>
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="people" class="me-1"></mat-icon> Participants ({{ jump.sautants.length + 1 }})</h5>
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<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="person"></mat-icon>
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="mt-3">
|
||||
<mat-card-content>
|
||||
<div class="d-flex justify-content-center my-3 fs-5"><mat-icon fontIcon="satellite_alt" class="me-2"></mat-icon> Aucune donnée GPS</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
@@ -6,10 +6,12 @@ describe('JumpComponent', () => {
|
||||
let component: JumpComponent;
|
||||
let fixture: ComponentFixture<JumpComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [JumpComponent]
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [JumpComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(JumpComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -1,248 +1,71 @@
|
||||
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 { ActivatedRoute } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DatePipe, DecimalPipe } from '@angular/common';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
|
||||
import { MenuItems } from 'src/app/components/shared';
|
||||
import { BarsChartComponent, CircleChartComponent, LineChartComponent, LineAreaChartComponent } from 'src/app/components/shared/helpers-chart';
|
||||
import { UtilitiesService } from 'src/app/core/services';
|
||||
import { Jump, JumpByCategorie, JumpByDate, JumpByDateByModule, JumpByModule, JumpsPageData, JumpYears } from 'src/app/core/models';
|
||||
import { ListErrorsComponent, MenuItems } from 'src/app/components/shared';
|
||||
import { Errors, Jump, JumpPageData } from 'src/app/core/models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-jump',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule, MatCardModule, MatDividerModule,
|
||||
MatExpansionModule, MatIconModule, MatMenuModule,
|
||||
BarsChartComponent, CircleChartComponent, LineChartComponent, LineAreaChartComponent
|
||||
DatePipe, DecimalPipe, RouterLink,
|
||||
MatButtonModule, MatCardModule,
|
||||
MatDividerModule, MatIconModule, MatMenuModule,
|
||||
ListErrorsComponent
|
||||
],
|
||||
templateUrl: './jump.component.html',
|
||||
styleUrl: './jump.component.scss',
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({ transform: 'translateX(0)' })),
|
||||
transition('void => *', [
|
||||
style({ transform: 'translateX(-100%)' }),
|
||||
animate(200)
|
||||
]),
|
||||
transition('* => void', [
|
||||
style({ transform: 'translateX(100%)' }),
|
||||
animate(200)
|
||||
])
|
||||
])
|
||||
]
|
||||
styleUrl: './jump.component.scss'
|
||||
})
|
||||
export class JumpComponent implements OnInit, OnDestroy {
|
||||
private _data: Subscription = new Subscription();
|
||||
private _jumpsByCategorie: Array<JumpByCategorie> = [];
|
||||
private _jumpsByModule: Array<JumpByModule> = [];
|
||||
private _jumpsByDate: Array<JumpByDate> = [];
|
||||
public lastJump: Jump = {} as Jump;
|
||||
public title: string = 'Sauts';
|
||||
public subtitle: string = 'Nombre total de sauts par mois';
|
||||
public displayCharts = false;
|
||||
public title: string = '';
|
||||
public errors: Errors = { errors: {} };
|
||||
public destroyRef = inject(DestroyRef);
|
||||
public min = 0;
|
||||
public max = 0;
|
||||
public grandAvg = 0;
|
||||
public grandAvgLastYears = 0;
|
||||
public grandTotal = 0;
|
||||
public seriesHeader: string[] = [];
|
||||
public seriesName: string[] = [];
|
||||
public seriesRow: Array<Array<number>> = [];
|
||||
public seriesColor: {
|
||||
backgroundColor: string[],
|
||||
borderColor: string[]
|
||||
} = {
|
||||
backgroundColor: this._utilitiesService.getSeriesColors(0.8, 'all'),
|
||||
borderColor: this._utilitiesService.getSeriesColors(1, 'all')
|
||||
};
|
||||
public seriesTypesHeader: string[] = [];
|
||||
public seriesTypesName: string[] = [];
|
||||
public seriesTypesRow: Array<Array<number>> = [];
|
||||
public seriesTypesRowCumulated: Array<Array<number>> = [];
|
||||
public seriesTypesColor: {
|
||||
backgroundColor: string[],
|
||||
borderColor: string[]
|
||||
} = {
|
||||
backgroundColor: this._utilitiesService.getSeriesColors(0.8, 'pastels'),
|
||||
borderColor: this._utilitiesService.getSeriesColors(1, 'pastels')
|
||||
};
|
||||
public seriesColorClass: string[] = this._utilitiesService.getChartColors();
|
||||
public seriesRowTotal: number[] = [];
|
||||
public seriesRowAvg: number[] = [];
|
||||
public seriesRowAvgLastYears: number[] = [];
|
||||
public seriesColTotal: number[] = [];
|
||||
public seriesColTotalClosed: number[] = [];
|
||||
public seriesColTotalLastYears: number[] = [];
|
||||
public jump: Jump = {} as Jump;
|
||||
public prevSlug: string = '';
|
||||
public nextSlug: string = '';
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private _utilitiesService: UtilitiesService,
|
||||
public menuItems: MenuItems
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
const data$: Observable<{ jumpsPageData: JumpsPageData }> = this.route.data as Observable<{ jumpsPageData: JumpsPageData }>;
|
||||
this._data = data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data: { jumpsPageData: JumpsPageData }) => {
|
||||
const pageData: JumpsPageData = data.jumpsPageData;
|
||||
this.lastJump = pageData.lastjump;
|
||||
const currentYear: number = new Date().getFullYear();
|
||||
const types: Array<string> = ['FF', 'Solo', 'VR', 'Wingsuit'];
|
||||
const verticals: Array<string> = ['Chutas', 'Head Up', 'Head Down'];
|
||||
const others: Array<string> = ['Track', 'Trace'];
|
||||
const modules: Array<string> = [...verticals, ...others];
|
||||
this.min = pageData.jumpsByYears[0].year;
|
||||
this.max = pageData.jumpsByYears[(pageData.jumpsByYears.length-1)].year;
|
||||
|
||||
this.seriesHeader = this._utilitiesService.getMonthsList();
|
||||
const values: Array<number> = this.seriesHeader.map(() => 0);
|
||||
this.seriesColTotal = [...values];
|
||||
this.seriesColTotalClosed = [...values];
|
||||
this.seriesColTotalLastYears = [...values];
|
||||
this.seriesName = pageData.jumpsByYears.map((row: JumpYears) => {
|
||||
this.seriesRow.push([...values]);
|
||||
return row.year.toString();
|
||||
});
|
||||
|
||||
this._jumpsByDate = pageData.jumpsByDate.map((row: JumpByDate) => {
|
||||
this.seriesRow[(row.year-this.min)][(row.month-1)] = row.count;
|
||||
this.seriesColTotal[(row.month-1)] += row.count;
|
||||
if (row.year < currentYear) {
|
||||
this.seriesColTotalClosed[(row.month-1)] += row.count;
|
||||
}
|
||||
if (row.year >= (currentYear-3) && row.year < currentYear) {
|
||||
this.seriesColTotalLastYears[(row.month-1)] += row.count;
|
||||
}
|
||||
return row;
|
||||
});
|
||||
this.seriesRow.forEach((row: number[]) => {
|
||||
const total = row.reduce((partialSum, a) => partialSum + a, 0);
|
||||
this.seriesRowTotal.push(total);
|
||||
});
|
||||
this.seriesColTotalClosed.forEach((row: number) => {
|
||||
const value: number = (row/(this.seriesName.length-2)); // -2 pour ne pas compter la première année
|
||||
this.seriesRowAvg.push(parseInt(value.toFixed(0)));
|
||||
});
|
||||
this.seriesColTotalLastYears.forEach((row: number) => {
|
||||
const value: number = (row/3); // Les 3 dernières années
|
||||
this.seriesRowAvgLastYears.push(parseInt(value.toFixed(0)));
|
||||
});
|
||||
this.grandAvg = this.seriesRowAvg.reduce((partialSum, accumulated) => partialSum + accumulated, 0);
|
||||
this.grandAvgLastYears = this.seriesRowAvgLastYears.reduce((partialSum, accumulated) => partialSum + accumulated, 0);
|
||||
this.grandTotal = this.seriesColTotal.reduce((partialSum, accumulated) => partialSum + accumulated, 0);
|
||||
|
||||
pageData.jumpsByCategory.forEach((row: JumpByCategorie) => {
|
||||
if (types.indexOf(row.categorie) !== -1) {
|
||||
this._jumpsByCategorie.push(row);
|
||||
}
|
||||
});
|
||||
|
||||
//const focus: Array<string> = ['FF', 'Solo']; // ['FF']
|
||||
let verticalCount:number = 0;
|
||||
let otherCount:number = 0;
|
||||
pageData.jumpsByModule.forEach((row: JumpByModule) => {
|
||||
//if (focus.indexOf(row.categorie) !== -1 && modules.indexOf(row.module) !== -1) {
|
||||
if (modules.indexOf(row.module) !== -1) {
|
||||
if (verticals.indexOf(row.module) !== -1) {
|
||||
verticalCount += row.count;
|
||||
} else {
|
||||
otherCount += row.count;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (verticalCount > 0) {
|
||||
const row:JumpByModule = {
|
||||
categorie: 'FF',
|
||||
module: 'Vertical',
|
||||
count: verticalCount
|
||||
}
|
||||
this._jumpsByModule.push(row);
|
||||
}
|
||||
if (otherCount > 0) {
|
||||
const row:JumpByModule = {
|
||||
categorie: 'FF',
|
||||
module: 'Track/Trace',
|
||||
count: otherCount
|
||||
}
|
||||
this._jumpsByModule.push(row);
|
||||
}
|
||||
|
||||
for (let year = 0; year < pageData.jumpsByYears.length; year++) {
|
||||
for (let index = 0; index < 12; index++) {
|
||||
const month: string = ((index+1) < 10) ? `0${(index+1)}` : `${(index+1)}`;
|
||||
this.seriesTypesHeader.push(`${month}-${pageData.jumpsByYears[year].year}`);
|
||||
}
|
||||
}
|
||||
const emptyValues: Array<number> = this.seriesTypesHeader.map(() => 0);
|
||||
this.seriesTypesName = types;
|
||||
this.seriesTypesName.forEach(() => {
|
||||
this.seriesTypesRow.push([...emptyValues]);
|
||||
});
|
||||
pageData.jumpsByDateByModule.forEach((row: JumpByDateByModule) => {
|
||||
const indexOf: number = this.seriesTypesName.indexOf(row.categorie);
|
||||
if (indexOf !== -1) {
|
||||
this.seriesTypesRow[indexOf][(((row.year-this.min)*12)+row.month-1)] += row.count;
|
||||
}
|
||||
});
|
||||
this.seriesTypesRowCumulated = this.seriesTypesRow.map((row: Array<number>) => {
|
||||
let accumulated: number = 0;
|
||||
return row.map((value: number) => {
|
||||
accumulated += value;
|
||||
return accumulated;
|
||||
});
|
||||
});
|
||||
this.seriesTypesHeader = this.seriesTypesHeader.slice((pageData.jumpsByDateByModule[0].month+1), -(pageData.jumpsByDateByModule[(pageData.jumpsByDateByModule.length-1)].month));
|
||||
this.seriesTypesRow = this.seriesTypesRow.map((row: Array<number>) => {
|
||||
return row.slice((pageData.jumpsByDateByModule[0].month+1), -(pageData.jumpsByDateByModule[(pageData.jumpsByDateByModule.length-1)].month));
|
||||
});
|
||||
this.seriesTypesRowCumulated = this.seriesTypesRowCumulated.map((row: Array<number>) => {
|
||||
return row.slice((pageData.jumpsByDateByModule[0].month+1), -(pageData.jumpsByDateByModule[(pageData.jumpsByDateByModule.length-1)].month));
|
||||
});
|
||||
/*
|
||||
console.log('this.min', this.min);
|
||||
console.log('this.max', this.max);
|
||||
console.log('this.lastJump', this.lastJump);
|
||||
console.log('this.seriesHeader', this.seriesHeader);
|
||||
console.log('this.seriesName', this.seriesName);
|
||||
console.log('this.seriesRow', this.seriesRow);
|
||||
console.log('this.seriesRowTotal', this.seriesRowTotal);
|
||||
console.log('this.seriesRowAvg', this.seriesRowAvg);
|
||||
console.log('this.seriesRowAvgLastYears', this.seriesRowAvgLastYears);
|
||||
console.log('this._jumpsByCategorie', this._jumpsByCategorie);
|
||||
console.log('this._jumpsByModule', this._jumpsByModule);
|
||||
console.log('this.seriesTypesHeader', this.seriesTypesHeader);
|
||||
console.log('this.seriesTypesName', this.seriesTypesName);
|
||||
console.log('this.seriesTypesRow', this.seriesTypesRow);
|
||||
console.log('this.seriesTypesRowCumulated', this.seriesTypesRowCumulated);
|
||||
*/
|
||||
this.displayCharts = true;
|
||||
const data$: Observable<{ pageData: JumpPageData }> = this.route.data as Observable<{ pageData: JumpPageData }>;
|
||||
this._data = data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data: { pageData: JumpPageData }) => {
|
||||
this.jump = data.pageData.jump;
|
||||
this.prevSlug = data.pageData.prevJump?.slug;
|
||||
this.nextSlug = data.pageData.nextJump?.slug;
|
||||
this.title = `Saut n° ${this.jump.numero} `;
|
||||
});
|
||||
/*
|
||||
const data$: Observable<Jump> = this.route.data as Observable<Jump>;
|
||||
this._data = data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data: Jump) => {
|
||||
this.jump = data;
|
||||
console.log('jump : ', this.jump);
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._data.unsubscribe();
|
||||
}
|
||||
|
||||
public getCategories(): Array<JumpByCategorie> {
|
||||
return this._jumpsByCategorie;
|
||||
formatDuration(duration: number): string {
|
||||
const start = new Date(0);
|
||||
start.setSeconds(duration);
|
||||
return start.toISOString().substring(14, 19);
|
||||
}
|
||||
|
||||
public getModules(): Array<JumpByModule> {
|
||||
return this._jumpsByModule;
|
||||
}
|
||||
|
||||
public getModuleColor(index: number): number {
|
||||
return (this._jumpsByCategorie.length + index);
|
||||
private _resetErrors(): void {
|
||||
this.errors = { errors: {} };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<div @flyInOut>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuJumps" aria-label="Menu sauts">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuJumps="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ subtitle }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-2">
|
||||
@if(displayCharts) {
|
||||
<div class="row">
|
||||
<div class="col-xxl-6 col-xs-12">
|
||||
<app-linearea-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor"></app-linearea-chart>
|
||||
</div>
|
||||
<div class="col-xxl-6 col-xs-12">
|
||||
<app-bars-chart [headers]="seriesHeader" [names]="seriesName" [values]="seriesRow" [colors]="seriesColor" [legend]="true"></app-bars-chart>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<mat-divider class="mt-3 mb-0"></mat-divider>
|
||||
<table class="table table-striped table-dark table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th *ngFor='let name of seriesHeader; let i=index' class="text-end"> {{ name }} </th>
|
||||
<th class="text-end">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor='let values of seriesRow; let i=index'>
|
||||
<th class="{{seriesColorClass[i]}} text-end"> ● {{ seriesName[i] }} </th>
|
||||
<td *ngFor='let value of values; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value }}</span></ng-template>
|
||||
</td>
|
||||
<th class="{{seriesColorClass[i]}} font-monospace text-end">{{ seriesRowTotal[i] }}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th class="text-end">Total</th>
|
||||
<th *ngFor='let value of seriesColTotal; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value }}</span></ng-template>
|
||||
</th>
|
||||
<th class="font-monospace text-end">{{ grandTotal }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-end">Moyenne</th>
|
||||
<th *ngFor='let value of seriesRowAvg; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value | number : '1.0-1' }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value | number : '1.0-1' }}</span></ng-template>
|
||||
</th>
|
||||
<th class="font-monospace text-end">{{ grandAvg | number : '1.0-1' }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-end">Moyenne <small>3 dernières années</small></th>
|
||||
<th *ngFor='let value of seriesRowAvgLastYears; let i=index' class="font-monospace text-end">
|
||||
<span *ngIf="value; else elseBlock" class="pr-1">{{ value | number : '1.0-1' }}</span>
|
||||
<ng-template #elseBlock><span class="text-empty pr-1">{{ value | number : '1.0-1' }}</span></ng-template>
|
||||
</th>
|
||||
<th class="font-monospace text-end">{{ grandAvgLastYears | number : '1.0-1' }}</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card class="mt-3">
|
||||
<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"></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,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { JumpsComponent } from './jumps.component';
|
||||
|
||||
describe('JumpsComponent', () => {
|
||||
let component: JumpsComponent;
|
||||
let fixture: ComponentFixture<JumpsComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [JumpsComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(JumpsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,249 @@
|
||||
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 { ActivatedRoute } from '@angular/router';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
|
||||
import { MenuItems } from 'src/app/components/shared';
|
||||
import { BarsChartComponent, CircleChartComponent, LineChartComponent, LineAreaChartComponent } from 'src/app/components/shared/helpers-chart';
|
||||
import { UtilitiesService } from 'src/app/core/services';
|
||||
import { Jump, JumpByCategorie, JumpByDate, JumpByDateByModule, JumpByModule, JumpsPageData, JumpYears } from 'src/app/core/models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-jumps',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule, MatCardModule, MatDividerModule,
|
||||
MatExpansionModule, MatIconModule, MatMenuModule,
|
||||
BarsChartComponent, CircleChartComponent, LineChartComponent, LineAreaChartComponent
|
||||
],
|
||||
templateUrl: './jumps.component.html',
|
||||
styleUrl: './jumps.component.scss',
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({ transform: 'translateX(0)' })),
|
||||
transition('void => *', [
|
||||
style({ transform: 'translateX(-100%)' }),
|
||||
animate(200)
|
||||
]),
|
||||
transition('* => void', [
|
||||
style({ transform: 'translateX(100%)' }),
|
||||
animate(200)
|
||||
])
|
||||
])
|
||||
]
|
||||
})
|
||||
export class JumpsComponent implements OnInit, OnDestroy {
|
||||
private _data: Subscription = new Subscription();
|
||||
private _jumpsByCategorie: Array<JumpByCategorie> = [];
|
||||
private _jumpsByModule: Array<JumpByModule> = [];
|
||||
private _jumpsByDate: Array<JumpByDate> = [];
|
||||
public lastJump: Jump = {} as Jump;
|
||||
public title: string = 'Sauts';
|
||||
public subtitle: string = 'Nombre total de sauts par mois';
|
||||
public displayCharts = false;
|
||||
public destroyRef = inject(DestroyRef);
|
||||
public min = 0;
|
||||
public max = 0;
|
||||
public grandAvg = 0;
|
||||
public grandAvgLastYears = 0;
|
||||
public grandTotal = 0;
|
||||
public seriesHeader: string[] = [];
|
||||
public seriesName: string[] = [];
|
||||
public seriesRow: Array<Array<number>> = [];
|
||||
public seriesColor: {
|
||||
backgroundColor: string[],
|
||||
borderColor: string[]
|
||||
} = {
|
||||
backgroundColor: this._utilitiesService.getSeriesColors(0.8, 'all'),
|
||||
borderColor: this._utilitiesService.getSeriesColors(1, 'all')
|
||||
};
|
||||
public seriesTypesHeader: string[] = [];
|
||||
public seriesTypesName: string[] = [];
|
||||
public seriesTypesRow: Array<Array<number>> = [];
|
||||
public seriesTypesRowCumulated: Array<Array<number>> = [];
|
||||
public seriesTypesColor: {
|
||||
backgroundColor: string[],
|
||||
borderColor: string[]
|
||||
} = {
|
||||
backgroundColor: this._utilitiesService.getSeriesColors(0.8, 'pastels'),
|
||||
borderColor: this._utilitiesService.getSeriesColors(1, 'pastels')
|
||||
};
|
||||
public seriesColorClass: string[] = this._utilitiesService.getChartColors();
|
||||
public seriesRowTotal: number[] = [];
|
||||
public seriesRowAvg: number[] = [];
|
||||
public seriesRowAvgLastYears: number[] = [];
|
||||
public seriesColTotal: number[] = [];
|
||||
public seriesColTotalClosed: number[] = [];
|
||||
public seriesColTotalLastYears: number[] = [];
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private _utilitiesService: UtilitiesService,
|
||||
public menuItems: MenuItems
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
const data$: Observable<{ pageData: JumpsPageData }> = this.route.data as Observable<{ pageData: JumpsPageData }>;
|
||||
this._data = data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data: { pageData: JumpsPageData }) => {
|
||||
const pageData: JumpsPageData = data.pageData;
|
||||
this.lastJump = pageData.lastjump;
|
||||
const currentYear: number = new Date().getFullYear();
|
||||
const types: Array<string> = ['FF', 'Solo', 'VR', 'Wingsuit'];
|
||||
const verticals: Array<string> = ['Chutas', 'Head Up', 'Head Down'];
|
||||
const others: Array<string> = ['Track', 'Trace'];
|
||||
const modules: Array<string> = [...verticals, ...others];
|
||||
const minoration = 2; // -2 pour ne pas compter la première année
|
||||
this.min = pageData.jumpsByYears[0].year;
|
||||
this.max = pageData.jumpsByYears[(pageData.jumpsByYears.length-1)].year;
|
||||
|
||||
this.seriesHeader = this._utilitiesService.getMonthsList();
|
||||
const values: Array<number> = this.seriesHeader.map(() => 0);
|
||||
this.seriesColTotal = [...values];
|
||||
this.seriesColTotalClosed = [...values];
|
||||
this.seriesColTotalLastYears = [...values];
|
||||
this.seriesName = pageData.jumpsByYears.map((row: JumpYears) => {
|
||||
this.seriesRow.push([...values]);
|
||||
return row.year.toString();
|
||||
});
|
||||
|
||||
this._jumpsByDate = pageData.jumpsByDate.map((row: JumpByDate) => {
|
||||
this.seriesRow[(row.year-this.min)][(row.month-1)] = row.count;
|
||||
this.seriesColTotal[(row.month-1)] += row.count;
|
||||
if (row.year < currentYear) {
|
||||
this.seriesColTotalClosed[(row.month-1)] += row.count;
|
||||
}
|
||||
if (row.year >= (currentYear-3) && row.year < currentYear) {
|
||||
this.seriesColTotalLastYears[(row.month-1)] += row.count;
|
||||
}
|
||||
return row;
|
||||
});
|
||||
this.seriesRow.forEach((row: number[]) => {
|
||||
const total = row.reduce((partialSum, a) => partialSum + a, 0);
|
||||
this.seriesRowTotal.push(total);
|
||||
});
|
||||
this.seriesColTotalClosed.forEach((row: number) => {
|
||||
const value: number = (row/(this.seriesName.length-minoration));
|
||||
this.seriesRowAvg.push(parseInt(value.toFixed(0)));
|
||||
});
|
||||
this.seriesColTotalLastYears.forEach((row: number) => {
|
||||
const value: number = (row/3); // Les 3 dernières années
|
||||
this.seriesRowAvgLastYears.push(parseInt(value.toFixed(0)));
|
||||
});
|
||||
this.grandAvg = this.seriesRowAvg.reduce((partialSum, accumulated) => partialSum + accumulated, 0);
|
||||
this.grandAvgLastYears = this.seriesRowAvgLastYears.reduce((partialSum, accumulated) => partialSum + accumulated, 0);
|
||||
this.grandTotal = this.seriesColTotal.reduce((partialSum, accumulated) => partialSum + accumulated, 0);
|
||||
|
||||
pageData.jumpsByCategory.forEach((row: JumpByCategorie) => {
|
||||
if (types.indexOf(row.categorie) !== -1) {
|
||||
this._jumpsByCategorie.push(row);
|
||||
}
|
||||
});
|
||||
|
||||
//const focus: Array<string> = ['FF', 'Solo']; // ['FF']
|
||||
let verticalCount:number = 0;
|
||||
let otherCount:number = 0;
|
||||
pageData.jumpsByModule.forEach((row: JumpByModule) => {
|
||||
//if (focus.indexOf(row.categorie) !== -1 && modules.indexOf(row.module) !== -1) {
|
||||
if (modules.indexOf(row.module) !== -1) {
|
||||
if (verticals.indexOf(row.module) !== -1) {
|
||||
verticalCount += row.count;
|
||||
} else {
|
||||
otherCount += row.count;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (verticalCount > 0) {
|
||||
const row:JumpByModule = {
|
||||
categorie: 'FF',
|
||||
module: 'Vertical',
|
||||
count: verticalCount
|
||||
}
|
||||
this._jumpsByModule.push(row);
|
||||
}
|
||||
if (otherCount > 0) {
|
||||
const row:JumpByModule = {
|
||||
categorie: 'FF',
|
||||
module: 'Track/Trace',
|
||||
count: otherCount
|
||||
}
|
||||
this._jumpsByModule.push(row);
|
||||
}
|
||||
|
||||
for (let year = 0; year < pageData.jumpsByYears.length; year++) {
|
||||
for (let index = 0; index < 12; index++) {
|
||||
const month: string = ((index+1) < 10) ? `0${(index+1)}` : `${(index+1)}`;
|
||||
this.seriesTypesHeader.push(`${month}-${pageData.jumpsByYears[year].year}`);
|
||||
}
|
||||
}
|
||||
const emptyValues: Array<number> = this.seriesTypesHeader.map(() => 0);
|
||||
this.seriesTypesName = types;
|
||||
this.seriesTypesName.forEach(() => {
|
||||
this.seriesTypesRow.push([...emptyValues]);
|
||||
});
|
||||
pageData.jumpsByDateByModule.forEach((row: JumpByDateByModule) => {
|
||||
const indexOf: number = this.seriesTypesName.indexOf(row.categorie);
|
||||
if (indexOf !== -1) {
|
||||
this.seriesTypesRow[indexOf][(((row.year-this.min)*12)+row.month-1)] += row.count;
|
||||
}
|
||||
});
|
||||
this.seriesTypesRowCumulated = this.seriesTypesRow.map((row: Array<number>) => {
|
||||
let accumulated: number = 0;
|
||||
return row.map((value: number) => {
|
||||
accumulated += value;
|
||||
return accumulated;
|
||||
});
|
||||
});
|
||||
this.seriesTypesHeader = this.seriesTypesHeader.slice((pageData.jumpsByDateByModule[0].month+1), -(pageData.jumpsByDateByModule[(pageData.jumpsByDateByModule.length-1)].month));
|
||||
this.seriesTypesRow = this.seriesTypesRow.map((row: Array<number>) => {
|
||||
return row.slice((pageData.jumpsByDateByModule[0].month+1), -(pageData.jumpsByDateByModule[(pageData.jumpsByDateByModule.length-1)].month));
|
||||
});
|
||||
this.seriesTypesRowCumulated = this.seriesTypesRowCumulated.map((row: Array<number>) => {
|
||||
return row.slice((pageData.jumpsByDateByModule[0].month+1), -(pageData.jumpsByDateByModule[(pageData.jumpsByDateByModule.length-1)].month));
|
||||
});
|
||||
/*
|
||||
console.log('this.min', this.min);
|
||||
console.log('this.max', this.max);
|
||||
console.log('this.lastJump', this.lastJump);
|
||||
console.log('this.seriesHeader', this.seriesHeader);
|
||||
console.log('this.seriesName', this.seriesName);
|
||||
console.log('this.seriesRow', this.seriesRow);
|
||||
console.log('this.seriesRowTotal', this.seriesRowTotal);
|
||||
console.log('this.seriesRowAvg', this.seriesRowAvg);
|
||||
console.log('this.seriesRowAvgLastYears', this.seriesRowAvgLastYears);
|
||||
console.log('this._jumpsByCategorie', this._jumpsByCategorie);
|
||||
console.log('this._jumpsByModule', this._jumpsByModule);
|
||||
console.log('this.seriesTypesHeader', this.seriesTypesHeader);
|
||||
console.log('this.seriesTypesName', this.seriesTypesName);
|
||||
console.log('this.seriesTypesRow', this.seriesTypesRow);
|
||||
console.log('this.seriesTypesRowCumulated', this.seriesTypesRowCumulated);
|
||||
*/
|
||||
this.displayCharts = true;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._data.unsubscribe();
|
||||
}
|
||||
|
||||
public getCategories(): Array<JumpByCategorie> {
|
||||
return this._jumpsByCategorie;
|
||||
}
|
||||
|
||||
public getModules(): Array<JumpByModule> {
|
||||
return this._jumpsByModule;
|
||||
}
|
||||
|
||||
public getModuleColor(index: number): number {
|
||||
return (this._jumpsByCategorie.length + index);
|
||||
}
|
||||
}
|
||||
@@ -245,7 +245,7 @@
|
||||
}
|
||||
<span class="badge rounded-pill bg-accent mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle pointer me-1" fontIcon="person"></mat-icon>
|
||||
ju_solide
|
||||
{{ lastJump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
}
|
||||
<span class="badge rounded-pill bg-accent mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle pointer me-1" fontIcon="person"></mat-icon>
|
||||
ju_solide
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,69 +1,214 @@
|
||||
<h2 mat-dialog-title class="bg-cyan 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-title class="bg-navy-light 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">
|
||||
<h5 class="border-bottom text-light-info border-light-subtle pb-2 mb-3">Le {{ jump.date | date: 'dd/MM/yyyy' }} à {{ jump.lieu }} :</h5>
|
||||
<div class="row mb-3">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-cyan fw-normal">Lieu:</dt>
|
||||
<dd>{{jump.lieu}}</dd>
|
||||
<dt class="text-cyan fw-normal">Oaci:</dt>
|
||||
<dd>{{jump.oaci}}</dd>
|
||||
<dt class="text-cyan fw-normal">Aeronef:</dt>
|
||||
<dd>{{jump.aeronef}}</dd>
|
||||
<dt class="text-cyan fw-normal">Imat:</dt>
|
||||
<dd>{{jump.imat}}</dd>
|
||||
<dt class="text-cyan fw-normal">Voile:</dt>
|
||||
<dd>{{jump.voile}}</dd>
|
||||
<dt class="text-cyan fw-normal">Taille:</dt>
|
||||
<dd>{{jump.taille}} ft<sup>2</sup></dd>
|
||||
<dt class="text-cyan fw-normal">Participants:</dt>
|
||||
<dd>{{jump.participants}}</dd>
|
||||
<dt class="text-cyan fw-normal">Sautants:</dt>
|
||||
<dd>
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<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="person"></mat-icon>
|
||||
ju_solide
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-cyan fw-normal">Hauteur:</dt>
|
||||
<dd>{{jump.hauteur}} m</dd>
|
||||
<dt class="text-cyan fw-normal">Deploiement:</dt>
|
||||
<dd>{{jump.deploiement}} m</dd>
|
||||
<dt class="text-cyan fw-normal">Categorie:</dt>
|
||||
<dd>{{jump.categorie}}</dd>
|
||||
<dt class="text-cyan fw-normal">Module:</dt>
|
||||
<dd>{{jump.module}}</dd>
|
||||
<dt class="text-cyan fw-normal">Accessoires:</dt>
|
||||
<dd>{{jump.accessoires}}</dd>
|
||||
<dt class="text-cyan fw-normal">Zone:</dt>
|
||||
<dd>{{jump.zone}}</dd>
|
||||
<dt class="text-cyan fw-normal">Dossier media:</dt>
|
||||
<dd>{{jump.dossier}}</dd>
|
||||
<dt class="text-cyan fw-normal">Video:</dt>
|
||||
<dd>{{jump.video}}</dd>
|
||||
<dt class="text-cyan fw-normal">Programme:</dt>
|
||||
<dd>{{jump.programme}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="d-flex border-bottom border-light-subtle text-light-info 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>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<span class="text-accent fs-5">{{ jump.categorie }}</span>
|
||||
@if (jump.module) {
|
||||
<span class="fs-5 mx-2">/</span>
|
||||
<span class="text-muted fs-5">{{ jump.module }}</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-light-info fw-normal">Aeronef:</dt>
|
||||
<dd>{{ jump.aeronef }} {{ jump.imat }}</dd>
|
||||
<dt class="text-light-info fw-normal">Voile:</dt>
|
||||
<dd>{{ jump.voile }} {{ jump.taille }} ft<sup>2</sup></dd>
|
||||
@if (jump.programme) {
|
||||
<dt class="text-light-info fw-normal">Programme:</dt>
|
||||
<dd>{{ jump.programme }}</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
@if (!jump.x2data) {
|
||||
<dt class="text-light-info fw-normal">Hauteur:</dt>
|
||||
<dd>{{ jump.hauteur }} m</dd>
|
||||
<dt class="text-light-info fw-normal">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>
|
||||
<dd>{{ file.name }}</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
@if (jump.x2data) {
|
||||
<div class="row my-3">
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6>Hauteur</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.altitude.exit }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6 class="d-flex justify-content-center"><mat-icon fontIcon="timer" class="me-1 fs-5"></mat-icon> Durée de la chute</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{formatDuration(jump.x2data.duration.freeFall)}}</span>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6>Déploiement</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.altitude.deployment }} m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-4">
|
||||
<div class="{{ jump.accessoires ? 'col-sm-6 ' : '' }}col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="people" class="me-1"></mat-icon> Participants ({{ jump.sautants.length + 1 }})</h5>
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<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="person"></mat-icon>
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
@if (jump.accessoires) {
|
||||
<div class="col-sm-6 col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="auto_awesome" class="me-1"></mat-icon> Accessoires</h5>
|
||||
<span class="badge rounded-pill bg-navy-light mb-2 me-1 pe-3 py-1 fw-normal">
|
||||
<mat-icon class="align-middle me-1" fontIcon="auto_awesome"></mat-icon>
|
||||
{{ jump.accessoires }}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="speed" class="me-1"></mat-icon> Vitesse en chute</h5>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.vertical.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.horizontal.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.vertical.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.horizontal.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="row mt-4 mb-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="speed" class="me-1"></mat-icon> Vitesse sous voile</h5>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.vertical.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.horizontal.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.vertical.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.horizontal.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="square_foot" class="me-1"></mat-icon> Finesse</h5>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-12 text-center">
|
||||
<h6>Finesse en chute</h6>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-12 text-center">
|
||||
<h6>Finesse sous voile</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Max</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.freeFall.max | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.freeFall.avg | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Max</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.underCanopy.max | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.underCanopy.avg | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="straighten" class="me-1"></mat-icon> Distance</h5>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance au largage</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.exitFromLandingZone| number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6 class="d-flex justify-content-center"><mat-icon fontIcon="timer" class="me-1 fs-5"></mat-icon> Durée sous voile</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{formatDuration(jump.x2data.duration.underCanopy)}}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance totale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.totalFlying | number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.totalHorizontal | number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
}
|
||||
@else {
|
||||
<div class="row my-4">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="people" class="me-1"></mat-icon> Participants ({{ jump.sautants.length + 1 }})</h5>
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<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="person"></mat-icon>
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="d-flex justify-content-center my-3 fs-5"><mat-icon fontIcon="satellite_alt" class="me-2"></mat-icon> Aucune donnée GPS</div>
|
||||
<mat-divider></mat-divider>
|
||||
}
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<span class="flex-spacer"></span>
|
||||
<button mat-button (click)="closeDialog()" class="me-1">Fermer</button>
|
||||
<span class="flex-spacer"></span>
|
||||
<button mat-button (click)="closeDialog()" class="me-1">Fermer</button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DatePipe, DecimalPipe } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
@@ -12,11 +12,9 @@ import { Jump } from 'src/app/core/models';
|
||||
templateUrl: 'jump-view.dialog.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
DatePipe,
|
||||
MatButtonModule,
|
||||
MatDialogModule,
|
||||
MatDividerModule,
|
||||
MatIconModule
|
||||
DatePipe, DecimalPipe,
|
||||
MatButtonModule, MatDialogModule,
|
||||
MatDividerModule, MatIconModule
|
||||
]
|
||||
})
|
||||
export class JumpViewDialogComponent {
|
||||
@@ -30,4 +28,10 @@ export class JumpViewDialogComponent {
|
||||
// close the dialog without result
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
formatDuration(duration: number): string {
|
||||
const start = new Date(0);
|
||||
start.setSeconds(duration);
|
||||
return start.toISOString().substring(14, 19);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,105 @@
|
||||
<div @flyInOut>
|
||||
<div class="content">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button (click)="openAddDialog()" aria-label="Ajouter">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button (click)="openAddDialog()" aria-label="Ajouter">
|
||||
<mat-icon fontIcon="add_notes"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuJump" aria-label="Menu Jump">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuJump="matMenu">
|
||||
<button mat-menu-item (click)="openAddDialog()">
|
||||
<mat-icon fontIcon="add_notes"></mat-icon>
|
||||
<span>Ajouter un saut</span>
|
||||
</button>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuJump" aria-label="Menu Jump">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuJump="matMenu">
|
||||
<button mat-menu-item (click)="openAddDialog()">
|
||||
<mat-icon fontIcon="add_notes"></mat-icon>
|
||||
<span>Ajouter un saut</span>
|
||||
@for (menuitem of menuItems.getMenuLogbook(); track menuitem) {
|
||||
@if (menuitem.type === 'link') {
|
||||
<button mat-menu-item [routerLink]="['/', menuitem.state]">
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
@for (menuitem of menuItems.getMenuLogbook(); track menuitem) {
|
||||
@if (menuitem.type === 'link') {
|
||||
<button mat-menu-item [routerLink]="['/', menuitem.state]">
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
@if (jumpsToHundred() <= 20) {
|
||||
<div class="d-flex pb-2 pe-2">
|
||||
<span class="flex-spacer"></span>
|
||||
<mat-icon fontIcon="warning" color="teal"></mat-icon>
|
||||
<div class="text-end text-teal align-self-center ms-2">
|
||||
{{ lastJump.numero + jumpsToHundred() }}<sup>ème</sup> à venir dans {{ jumpsToHundred() }}
|
||||
{{ jumpsToHundred() > 1 ? 'sauts' : 'saut' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<mat-card>
|
||||
<mat-card-header class="p-0">
|
||||
<mat-expansion-panel class="w-100">
|
||||
<mat-expansion-panel-header class="bg-navy-light">
|
||||
<mat-panel-title>Filtrer les sauts</mat-panel-title>
|
||||
@if (jumpsCount < lastJump.numero) {
|
||||
<mat-panel-description>
|
||||
{{ jumpsCount }} {{ jumpsCount > 1 ? 'sauts' : 'saut' }} sur {{ lastJump.numero }} correspondent à la recherche
|
||||
</mat-panel-description>
|
||||
}
|
||||
</mat-expansion-panel-header>
|
||||
<div class="d-flex mt-3 mb-0">
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="yearRange">Année du saut</label>
|
||||
<label for="yearRange">de <span class="fw-bold">{{listConfig.filters.yearRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.yearRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.year.start" [max]="rangesMinMax.year.end" step="1" discrete>
|
||||
<input id="yearRange" matSliderStartThumb [(ngModel)]="listConfig.filters.yearRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.yearRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="numeroRange">Numéro du saut</label>
|
||||
<label for="numeroRange">de <span class="fw-bold">{{listConfig.filters.numeroRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.numeroRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.numero.start" [max]="rangesMinMax.numero.end" step="1" discrete>
|
||||
<input id="numeroRange" matSliderStartThumb [(ngModel)]="listConfig.filters.numeroRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.numeroRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="tailleRange">Taille de la voile</label>
|
||||
<label for="tailleRange">de <span class="fw-bold">{{listConfig.filters.tailleRangeStart}}</span> ft<sup>2</sup> à <span class="fw-bold">{{listConfig.filters.tailleRangeEnd}}</span> ft<sup>2</sup></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.taille.start" [max]="rangesMinMax.taille.end" step="1" discrete>
|
||||
<input id="tailleRange" matSliderStartThumb [(ngModel)]="listConfig.filters.tailleRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.tailleRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="participantsRange">Taille du groupe</label>
|
||||
<label for="participantsRange">de <span class="fw-bold">{{listConfig.filters.participantsRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.participantsRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.participants.start" [max]="rangesMinMax.participants.end" step="1" discrete>
|
||||
<input id="participantsRange" matSliderStartThumb [(ngModel)]="listConfig.filters.participantsRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.participantsRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-2">
|
||||
<app-jump-table (countChange)="setTotalResults($event)" #jumpTable [config]="listConfig" [isUser]="isUser" [showAdd]="canModify" [title]="title">
|
||||
</app-jump-table>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
@if (jumpsToHundred() <= 20) {
|
||||
<div class="d-flex pb-2 pe-2">
|
||||
<span class="flex-spacer"></span>
|
||||
<mat-icon fontIcon="warning" color="teal"></mat-icon>
|
||||
<div class="text-end text-teal align-self-center ms-2">
|
||||
{{ lastJump.numero + jumpsToHundred() }}<sup>ème</sup> à venir dans {{ jumpsToHundred() }}
|
||||
{{ jumpsToHundred() > 1 ? 'sauts' : 'saut' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<mat-card>
|
||||
<mat-card-header class="p-0">
|
||||
<mat-expansion-panel class="w-100">
|
||||
<mat-expansion-panel-header class="bg-navy-light">
|
||||
<mat-panel-title>Filtrer les sauts</mat-panel-title>
|
||||
@if (jumpsCount < lastJump.numero) {
|
||||
<mat-panel-description>
|
||||
{{ jumpsCount }} {{ jumpsCount > 1 ? 'sauts' : 'saut' }} sur {{ lastJump.numero }} correspondent à la recherche
|
||||
</mat-panel-description>
|
||||
}
|
||||
</mat-expansion-panel-header>
|
||||
<div class="d-flex mt-3 mb-0">
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="yearRange">Année du saut</label>
|
||||
<label for="yearRange">de <span class="fw-bold">{{listConfig.filters.yearRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.yearRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.year.start" [max]="rangesMinMax.year.end" step="1" discrete>
|
||||
<input id="yearRange" matSliderStartThumb [(ngModel)]="listConfig.filters.yearRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.yearRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="numeroRange">Numéro du saut</label>
|
||||
<label for="numeroRange">de <span class="fw-bold">{{listConfig.filters.numeroRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.numeroRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.numero.start" [max]="rangesMinMax.numero.end" step="1" discrete>
|
||||
<input id="numeroRange" matSliderStartThumb [(ngModel)]="listConfig.filters.numeroRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.numeroRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="tailleRange">Taille de la voile</label>
|
||||
<label for="tailleRange">de <span class="fw-bold">{{listConfig.filters.tailleRangeStart}}</span> ft<sup>2</sup> à <span class="fw-bold">{{listConfig.filters.tailleRangeEnd}}</span> ft<sup>2</sup></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.taille.start" [max]="rangesMinMax.taille.end" step="1" discrete>
|
||||
<input id="tailleRange" matSliderStartThumb [(ngModel)]="listConfig.filters.tailleRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.tailleRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="participantsRange">Taille du groupe</label>
|
||||
<label for="participantsRange">de <span class="fw-bold">{{listConfig.filters.participantsRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.participantsRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.participants.start" [max]="rangesMinMax.participants.end" step="1" discrete>
|
||||
<input id="participantsRange" matSliderStartThumb [(ngModel)]="listConfig.filters.participantsRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.participantsRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-2">
|
||||
<app-jump-table (countChange)="setTotalResults($event)" #jumpTable [config]="listConfig" [isUser]="isUser" [showAdd]="canModify" [title]="title">
|
||||
</app-jump-table>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
@@ -12,14 +12,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-page content">
|
||||
<h1>{{title}}</h1>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="profile-page">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2 text-end"></div>
|
||||
</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-content>
|
||||
<div class="text-center">
|
||||
<a href="index.html" class="d-block p-4"><img height="72" width="90" src="assets/images/vector/upt_logo.png" alt="Logo UPT"></a>
|
||||
<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>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
.banner {
|
||||
background-color: var.$sidebar-footer;
|
||||
padding: 1.6rem 0 1.3rem 0;
|
||||
margin: -1.5rem -1.5rem 1.5rem -1.5rem;
|
||||
.user-img {
|
||||
width: var.$user-img-size;
|
||||
height: var.$user-img-size;
|
||||
|
||||
@@ -1,32 +1,43 @@
|
||||
<div class="content">
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">
|
||||
{{ title }}
|
||||
<span class="ms-2 text-muted fs-4">{{ questionsCount }} {{ questionsCount > 1 ? 'questions' : 'question' }}</span>
|
||||
</h1>
|
||||
<span class="me-2 fs-6">{{ subtitle }}</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuAeronef" aria-label="Menu Aeronef">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuAeronef="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">
|
||||
{{ title }}
|
||||
<span class="ms-2 text-muted fs-4">{{ questionsCount }} {{ questionsCount > 1 ? 'questions' : 'question' }}</span>
|
||||
</h1>
|
||||
<span class="me-2 fs-6">{{ subtitle }}</span>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<app-list-errors [errors]="errors"></app-list-errors>
|
||||
<mat-card [hidden]="!questions.length">
|
||||
<mat-card-content class="mt-4">
|
||||
<form [formGroup]="qcmForm">
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuAeronef" aria-label="Menu Aeronef">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuAeronef="matMenu">
|
||||
@for (menuitem of menuItems.getMenuPanel(); track menuitem) {
|
||||
<button mat-menu-item>
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
<app-list-errors [errors]="errors"></app-list-errors>
|
||||
@if (questions.controls.length) {
|
||||
<mat-card [hidden]="!questions.controls.length">
|
||||
<div class="mx-3 my-5">
|
||||
<mat-progress-bar [mode]="loading ? 'query' : 'determinate'" [value]="loading ? 0 : (totalAnswer/questionsCount*100)" color="accent"></mat-progress-bar>
|
||||
<mat-progress-bar class="mt-0" [mode]="loading ? 'query' : 'determinate'" [value]="loading ? 0 : ((timeProgress/timeMax)*100)" [color]="timeProgressColor"></mat-progress-bar>
|
||||
<ol class="squareItem clearfix">
|
||||
@for (category of qcm.categories; track category.num) {
|
||||
@for (question of category.questions; track question.num) {
|
||||
<li class="rounded-0">{{ question.num }}</li>
|
||||
}
|
||||
}
|
||||
</ol>
|
||||
</div>
|
||||
<mat-card-content>
|
||||
<form [formGroup]="qcmForm" (ngSubmit)="onSubmit()">
|
||||
@for (category of qcm.categories; track category; let index = $index) {
|
||||
<mat-accordion>
|
||||
<mat-expansion-panel class="mb-3" [expanded]="expandedIndex === index" (opened)="setExpandedIndex(index)">
|
||||
@@ -43,9 +54,9 @@
|
||||
@for (question of category.questions; track question; let index = $index) {
|
||||
<div class="mb-3" [formGroupName]="index">
|
||||
<label class="fs-5 fw-medium mb-2" for="radio-group-{{ category.num }}-{{ question.num }}">{{ question.num }} - {{ question.libelle }}</label>
|
||||
<mat-radio-group id="radio-group-{{ category.num }}-{{ question.num }}" class="d-flex flex-column" (change)="onChange()">
|
||||
<mat-radio-group formControlName="choice" id="radio-group-{{ category.num }}-{{ question.num }}" class="d-flex flex-column" (change)="onChange(category, question, $event.value)">
|
||||
@for (choice of question.choices; track choice) {
|
||||
<mat-radio-button class="example-radio-button" [value]="choice.index" [color]="choice.correct ? 'primary' : 'warn'">{{ choice.libelle }}</mat-radio-button>
|
||||
<mat-radio-button [value]="choice.index" [color]="choice.correct ? 'primary' : 'warn'">{{ choice.libelle }}</mat-radio-button>
|
||||
}
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
@@ -56,5 +67,17 @@
|
||||
}
|
||||
</form>
|
||||
</mat-card-content>
|
||||
<!--
|
||||
<form [formGroup]="qcmForm">
|
||||
<div class="mb-3 ms-4" formArrayName="questions">
|
||||
@for (question of questions.controls; track question; let index = $index) {
|
||||
<div class="mb-3" [formGroupName]="index">
|
||||
<label class="fs-5 fw-medium mb-2" for="radio-group-{{ index }}">{{ index }} - Libellé</label>
|
||||
<input formControlName="choice" type="text"/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
-->
|
||||
</mat-card>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/* QCM */
|
||||
@use 'variable' as var;
|
||||
|
||||
ol.squareItem {
|
||||
padding: 0;
|
||||
margin: 1rem 0;
|
||||
li {
|
||||
color: var.$primary;
|
||||
background-color: var.$sidenav-content; //var.$dark;
|
||||
background-image: none;
|
||||
border: 1px solid var.$dark; //var.$extra-muted;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
list-style-type: none;
|
||||
margin: 0.17em;
|
||||
padding: 0.34em 0;
|
||||
text-align: center;
|
||||
width: 33px;
|
||||
height: 33px;
|
||||
&.active {
|
||||
border-color: var.$info;
|
||||
}
|
||||
&.valid {
|
||||
color: var.$sidenav-content;
|
||||
border-color: var.$success;
|
||||
background-color: var.$primary;
|
||||
}
|
||||
&.defer {
|
||||
border-color: var.$info-dark;
|
||||
background-color: var.$navy-light;
|
||||
}
|
||||
&.error {
|
||||
border-color: var.$purple-light;
|
||||
background-color: var.$secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,15 @@ import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { interval, Observable, Subscription } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { MenuItems } from 'src/app/components/shared';
|
||||
import { ListErrorsComponent, } from 'src/app/components/shared';
|
||||
import { Errors, Qcm, User } from 'src/app/core/models';
|
||||
import { ListErrorsComponent } from 'src/app/components/shared';
|
||||
import { Errors, Qcm, QcmCategory, QcmQuestion, User } from 'src/app/core/models';
|
||||
import { QcmService, UserService } from 'src/app/core/services';
|
||||
import data from 'src/qcm-bpa.json';
|
||||
|
||||
@@ -23,7 +26,8 @@ import data from 'src/qcm-bpa.json';
|
||||
imports: [
|
||||
FormsModule, ReactiveFormsModule,
|
||||
MatButtonModule, MatCardModule, MatDividerModule, MatExpansionModule,
|
||||
MatIconModule, MatMenuModule, MatRadioModule,
|
||||
MatIconModule, MatMenuModule, MatProgressBarModule, MatRadioModule,
|
||||
MatSelectModule,
|
||||
ListErrorsComponent
|
||||
],
|
||||
templateUrl: './qcm.component.html',
|
||||
@@ -32,6 +36,8 @@ import data from 'src/qcm-bpa.json';
|
||||
export class QcmComponent implements OnInit, OnDestroy {
|
||||
private _currentUser: Subscription = new Subscription();
|
||||
private _data: Subscription = new Subscription();
|
||||
private _timer: Subscription = new Subscription();
|
||||
private _refresh = 1000;
|
||||
public qcm: Qcm = {} as Qcm;
|
||||
public currentUser: User = {} as User;
|
||||
public canModify = false;
|
||||
@@ -42,6 +48,11 @@ export class QcmComponent implements OnInit, OnDestroy {
|
||||
public expandedIndex = 0;
|
||||
public questionsCount = 0;
|
||||
public qcmForm!: FormGroup;
|
||||
public loading = true;
|
||||
public totalAnswer = 0;
|
||||
public timeProgress = 0;
|
||||
public timeProgressColor = 'primary';
|
||||
public timeMax = (60*45);
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@@ -56,15 +67,16 @@ export class QcmComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
get questions(): FormArray {
|
||||
return <FormArray>this.qcmForm.controls['questions'];
|
||||
return this.qcmForm.controls['questions'] as FormArray;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
//this.importQuestions();
|
||||
//this.importChoices();
|
||||
//this._importQuestions();
|
||||
//this._importChoices();
|
||||
// Retreive the prefetched QCM
|
||||
const data$: Observable<{ qcm: Qcm }> = this.route.data as Observable<{ qcm: Qcm }>;
|
||||
this._data = data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data: { qcm: Qcm }) => {
|
||||
this.questionsCount = 0;
|
||||
this.qcm = data.qcm;
|
||||
if (this.qcm.name === 'bpa') {
|
||||
this.title = 'QCM du BPA';
|
||||
@@ -77,13 +89,7 @@ export class QcmComponent implements OnInit, OnDestroy {
|
||||
this.qcm.categories.forEach(category => {
|
||||
this.questionsCount += category.questions.length;
|
||||
category.questions.forEach(question => {
|
||||
this.questions.push(
|
||||
this.fb.group({
|
||||
id: new FormControl({ value: question.num, disabled: false }),
|
||||
choice: ['']
|
||||
//choice: new FormControl({ value: '', disabled: false })
|
||||
})
|
||||
);
|
||||
this.addQuestion(question);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -93,6 +99,7 @@ export class QcmComponent implements OnInit, OnDestroy {
|
||||
this.currentUser = userData;
|
||||
this.canModify = userData.role === 'Admin';
|
||||
});
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -100,15 +107,61 @@ export class QcmComponent implements OnInit, OnDestroy {
|
||||
this._currentUser.unsubscribe();
|
||||
}
|
||||
|
||||
private _starTimer(): void {
|
||||
this._timer = interval(1000)
|
||||
.pipe(take(this.timeMax))
|
||||
.subscribe(() => {
|
||||
this.timeProgress++;
|
||||
if (this.timeProgress > (this.timeMax/100*70) && this.timeProgress <= (this.timeMax/100*90)) {
|
||||
this.timeProgressColor = 'warning';
|
||||
} else if (this.timeProgress > (this.timeMax/100*90) && this.timeProgress <= (this.timeMax/100*98)) {
|
||||
this.timeProgressColor = 'danger';
|
||||
} else if (this.timeProgress > (this.timeMax/100*98)) {
|
||||
this.timeProgressColor = 'purple';
|
||||
}
|
||||
if (this.timeProgress >= this.timeMax) {
|
||||
this.timeProgressColor = 'accent';
|
||||
this._timer.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addQuestion(question: QcmQuestion) {
|
||||
const item = this.fb.group({
|
||||
id: [question.num],
|
||||
choice: ['']
|
||||
});
|
||||
this.questions.push(item);
|
||||
}
|
||||
|
||||
setExpandedIndex(index: number) {
|
||||
this.expandedIndex = index;
|
||||
}
|
||||
|
||||
onChange() {
|
||||
//this.isDisabled = true;
|
||||
onChange(category: QcmCategory, question: QcmQuestion, value: unknown) {
|
||||
//console.log('onChange', category.num, question.num, value);
|
||||
if (this.timeProgress === 0) {
|
||||
this._starTimer();
|
||||
}
|
||||
this.qcm.categories[(category.num-1)].questions[(question.num-1)].choices.forEach(choice => {
|
||||
if (choice.index === value) {
|
||||
if (choice.correct) {
|
||||
console.log(`Réponse à la question ${question.num} de la catégorie ${category.num} : correcte`);
|
||||
} else {
|
||||
console.log(`Réponse à la question ${question.num} de la catégorie ${category.num} : incorrecte`);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.totalAnswer++;
|
||||
this.questions.controls[(question.num-1)].disable();
|
||||
//console.log(this.questions.controls[(question.num-1)].disabled);
|
||||
}
|
||||
|
||||
importChoices() {
|
||||
onSubmit() {
|
||||
console.log('onSubmit');
|
||||
}
|
||||
|
||||
private _importChoices() {
|
||||
try {
|
||||
data.categories.forEach((category) => {
|
||||
category.questions.forEach(question => {
|
||||
@@ -132,7 +185,7 @@ export class QcmComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
importQuestions() {
|
||||
private _importQuestions() {
|
||||
try {
|
||||
data.categories.forEach((category) => {
|
||||
//Object.assign(this.jump, category);
|
||||
@@ -154,4 +207,8 @@ export class QcmComponent implements OnInit, OnDestroy {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
private _resetErrors(): void {
|
||||
this.errors = { errors: {} };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="credentials-page content">
|
||||
<div class="credentials-page">
|
||||
<h1>{{title}}</h1>
|
||||
<mat-divider></mat-divider>
|
||||
<app-list-errors [errors]="errors"></app-list-errors>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
.banner {
|
||||
background-color: var.$sidebar-footer;
|
||||
padding: 1.6rem 0 1.3rem 0;
|
||||
margin: -1.5rem -1.5rem 1.5rem -1.5rem;
|
||||
.user-img {
|
||||
width: var.$user-img-size;
|
||||
height: var.$user-img-size;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-page content">
|
||||
<div class="settings-page">
|
||||
<h1>{{title}}</h1>
|
||||
<mat-divider></mat-divider>
|
||||
<app-list-errors [errors]="errors"></app-list-errors>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
.banner {
|
||||
background-color: var.$sidebar-footer;
|
||||
padding: 1.6rem 0 1.3rem 0;
|
||||
margin: -1.5rem -1.5rem 1.5rem -1.5rem;
|
||||
/*
|
||||
background-color: mat.get-color-from-palette(var.$md-custom-warn, 700);
|
||||
margin-bottom: 1rem;
|
||||
|
||||
+2
-1
@@ -74,6 +74,7 @@ export class JumpsByMonthComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private _loadJumpByDate() {
|
||||
const minoration = 2; // -2 pour ne pas compter la première année
|
||||
this.seriesHeader = this._utilitiesService.getMonthsList();
|
||||
const values: Array<number> = this.seriesHeader.map(() => 0);
|
||||
this.seriesColTotal = [...values];
|
||||
@@ -111,7 +112,7 @@ export class JumpsByMonthComponent implements OnInit, OnDestroy {
|
||||
this.seriesRowTotal.push(total);
|
||||
});
|
||||
this.seriesColTotalClosed.forEach((row: number) => {
|
||||
const value: number = (row/(this.seriesName.length-2)); // -2 pour ne pas compter la première année
|
||||
const value: number = (row/(this.seriesName.length-minoration));
|
||||
this.seriesRowAvg.push(parseInt(value.toFixed(0)));
|
||||
});
|
||||
this.seriesColTotalLastYears.forEach((row: number) => {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</button>
|
||||
<mat-spinner [diameter]="32" [hidden]="!loading" color="accent" class="align-middle ms-2"></mat-spinner>
|
||||
<span [hidden]="jumpsCount || loading" class="ms-2 fst-italic">Aucun saut à afficher pour le moment.</span>
|
||||
<span [hidden]="!loading" class="ms-2 fst-italic">Actualisation des sauts en cours</span>
|
||||
<span [hidden]="!loading" class="ms-2 fst-italic">Chargement des sauts en cours</span>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-6 col-xs-12 px-0">
|
||||
@@ -224,7 +224,7 @@
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container> -->
|
||||
<!-- Video Column -->
|
||||
<!-- Video Column
|
||||
<ng-container matColumnDef="video">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef> Video </th>
|
||||
<td class="pl-3 text-nowrap" mat-cell *matCellDef="let element">
|
||||
@@ -236,6 +236,41 @@
|
||||
</ngx-skeleton-loader>
|
||||
}
|
||||
</td>
|
||||
</ng-container> -->
|
||||
<!-- Files Column -->
|
||||
<ng-container matColumnDef="files">
|
||||
<th class="pl-3" mat-header-cell *matHeaderCellDef> Fichiers </th>
|
||||
<td class="pl-3 text-nowrap" mat-cell *matCellDef="let element">
|
||||
@if (element.files) {
|
||||
@for (file of element.files; track $index) {
|
||||
@switch (file.type) {
|
||||
@case ('video') {
|
||||
<button mat-button (click)="openViewDialog(element)" color="navy-light">
|
||||
<mat-icon fontIcon="videocam me-2"></mat-icon> {{ file.type }}
|
||||
</button>
|
||||
}
|
||||
@case ('image') {
|
||||
<button mat-button (click)="openViewDialog(element)" color="navy-light">
|
||||
<mat-icon fontIcon="image me-2"></mat-icon> {{ file.type }}
|
||||
</button>
|
||||
}
|
||||
@case ('kml') {
|
||||
<button mat-button (click)="openViewDialog(element)" color="navy-light">
|
||||
<mat-icon fontIcon="language me-2"></mat-icon> {{ file.type }}
|
||||
</button>
|
||||
}
|
||||
@default {
|
||||
<button mat-button (click)="openViewDialog(element)" color="navy-light">
|
||||
<mat-icon fontIcon="description me-2"></mat-icon> {{ file.type }}
|
||||
</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (!element.numero) {
|
||||
<ngx-skeleton-loader [theme]="{width: '30px', height: '13px', marginBottom: '0px'}"></ngx-skeleton-loader>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
@@ -247,13 +282,17 @@
|
||||
<mat-icon>more_vert</mat-icon>
|
||||
</button>
|
||||
<mat-menu [hidden]="!showAdd" #actionMenu="matMenu">
|
||||
<button mat-menu-item [routerLink]="['/jump', element.slug]">
|
||||
<mat-icon aria-label="Voir" fontIcon="visibility" color="info" class="mini"></mat-icon>
|
||||
<span class="text-info">Détails</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="openEditDialog(element)">
|
||||
<mat-icon aria-label="Modifier" fontIcon="edit" color="primary" class="mini"></mat-icon>
|
||||
<span class="text-primary">Modifier</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="openDeleteDialog(element)">
|
||||
<mat-icon aria-label="Supprimer" fontIcon="delete" color="danger" class="mini"></mat-icon>
|
||||
<span class="text-danger">Supprimer</span>
|
||||
<mat-icon aria-label="Supprimer" fontIcon="delete" color="raspberry" class="mini"></mat-icon>
|
||||
<span class="text-raspberry">Supprimer</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import { take } from 'rxjs/operators';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { FrenchPaginator } from 'src/app/components/shared/french-paginator.component';
|
||||
import { ColumnDefinition, Errors, Jump, JumpList, JumpListConfig, jumpColumns } from 'src/app/core/models';
|
||||
import { ColumnDefinition, Errors, Jump, JumpByDay, JumpFile, JumpFileType, JumpList, JumpListConfig, jumpColumns } from 'src/app/core/models';
|
||||
import { JumpsService, JumpTableService } from 'src/app/core/services';
|
||||
import { JumpAddDialogComponent, JumpDeleteDialogComponent, JumpEditDialogComponent, JumpViewDialogComponent } from 'src/app/components/logbook/dialogs'
|
||||
|
||||
@@ -41,6 +41,7 @@ import { JumpAddDialogComponent, JumpDeleteDialogComponent, JumpEditDialogCompon
|
||||
})
|
||||
export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChecked {
|
||||
private _jumps: Subscription = new Subscription();
|
||||
private _subscriptions: Array<Subscription> = [];
|
||||
//private _jump: Subscription = new Subscription();
|
||||
private _query: JumpListConfig = { type: 'all', filters: {} };
|
||||
//private _lastjump: Jump = {} as Jump;
|
||||
@@ -53,13 +54,13 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
'numero', 'date', 'lieu', 'oaci', 'aeronef',
|
||||
'imat', 'hauteur', 'voile', 'taille', 'categorie',
|
||||
'module', 'participants', 'programme', 'accessoires',
|
||||
'zone', 'video', 'actions'
|
||||
'zone', 'files', 'actions'
|
||||
];*/
|
||||
public displayedColumns: string[] = [
|
||||
'numero', 'date', 'lieu', 'aeronef',
|
||||
'hauteur', 'voile', 'categorie',
|
||||
'participants', 'zone', 'accessoires',
|
||||
'video', 'actions'
|
||||
'files', 'actions'
|
||||
];
|
||||
//public displayedColumns: string[] = jumpColumns.map((col) => col.key)
|
||||
public columnsSchema: Array<ColumnDefinition> = jumpColumns;
|
||||
@@ -115,7 +116,9 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
|
||||
ngOnDestroy() {
|
||||
this._jumps.unsubscribe();
|
||||
//this._jump.unsubscribe();
|
||||
this._subscriptions.forEach(subscription => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterContentChecked() {
|
||||
@@ -155,6 +158,9 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
const jumps$: Observable<JumpList> = this._jumpsService.query(this._query);
|
||||
this._jumps = jumps$.subscribe({
|
||||
next: (data: JumpList) => {
|
||||
//this.updateJumps(data.jumps);
|
||||
//this.linkSkydiverIdJumps();
|
||||
//this.getKmlJumpFiles(data.jumps);
|
||||
this.loading = false;
|
||||
this.jumps = new MatTableDataSource<Jump>(data.jumps);
|
||||
this.jumpsCount = data.jumpsCount;
|
||||
@@ -170,12 +176,150 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
});
|
||||
}
|
||||
|
||||
linkSkydiverIdJumps() {
|
||||
const config: JumpListConfig = { type: 'all', filters: {} } as JumpListConfig;
|
||||
config.filters.limit = 200;
|
||||
config.filters.offset = 0;
|
||||
config.filters.dateRangeStart = '2024-01-01 00:00:00';
|
||||
//const year = new Date().getFullYear();
|
||||
//config.filters.dateRangeEnd = `${year}-12-31 23:59:59`;
|
||||
config.filters.dateRangeEnd = '2024-12-31 23:59:59';
|
||||
//console.log(config.filters);
|
||||
const subscription: Subscription = this._jumpsService.getAllByDay(config)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (days) => {
|
||||
this._resetErrors();
|
||||
//console.log('getAllByDay : ', days);
|
||||
days.forEach(day => {
|
||||
this.loadSkydiverIdJumps(day, config);
|
||||
});
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
});
|
||||
this._subscriptions.push(subscription);
|
||||
}
|
||||
|
||||
loadSkydiverIdJumps(day: JumpByDay, config: JumpListConfig) {
|
||||
config.filters.dateRangeStart = `${day.jumps[0].date!.substring(0, 10)} 00:00:00`;
|
||||
config.filters.dateRangeEnd = `${day.jumps[0].date!.substring(0, 10)} 23:59:59`;
|
||||
const subscription: Subscription = this._jumpsService.getAllFromSkydiverIdApi(config)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (data) => {
|
||||
if (data.items.length) {
|
||||
const diff = day.count - data.items.length;
|
||||
if (diff < 0) {
|
||||
console.error(`${diff} ${diff > 1 ? 'données' : 'donnée'} altimètre de trop le ${day.jumps[0].date!.substring(0, 10)}`);
|
||||
} else {
|
||||
if (diff > 0) {
|
||||
console.error(`${diff}/${day.count} ${diff > 1 ? 'données altimètre manquantes' : 'donnée altimètre manquante'} le ${day.jumps[0].date!.substring(0, 10)}`, day.jumps, data.items);
|
||||
}
|
||||
console.log(`${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, day.jumps.sort((a, b) => b.numero! - a.numero!), data.items);
|
||||
data.items.map((jump, index) => {
|
||||
const file: JumpFile = {
|
||||
name: jump.name,
|
||||
path: `/Volumes/Storage/Skydive/X2_Logs/${day.jumps[index].date!.substring(0, 4)}/`,
|
||||
type: JumpFileType.CSV
|
||||
} as JumpFile;
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.saveX2Data(day.jumps[index].slug!, file, jump)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (jump) => {
|
||||
this._resetErrors();
|
||||
console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
})
|
||||
);
|
||||
return jump;
|
||||
});
|
||||
}
|
||||
//console.log(`${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${day.jumps[0].date!.substring(0, 10)}`);
|
||||
//console.log(`${data.items.length} ${data.items.length > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, data);
|
||||
} else {
|
||||
console.info('Aucune données altimètre pour le ', day.jumps[0].date!.substring(0, 10));
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('getAllFromSkydiverIdApi error');
|
||||
this.errors = err;
|
||||
}
|
||||
});
|
||||
this._subscriptions.push(subscription);
|
||||
}
|
||||
|
||||
updateJumps(jumps: Array<Jump>) {
|
||||
jumps.map(jump => {
|
||||
jump.files = [];
|
||||
if (jump.dossier !== "" && jump.video !== "") {
|
||||
const file: JumpFile = {
|
||||
name: jump.video!,
|
||||
path: jump.dossier!,
|
||||
type: JumpFileType.VIDEO
|
||||
} as JumpFile;
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.saveFile(jump.slug, file)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (file) => {
|
||||
this._resetErrors();
|
||||
jump.files.push(file);
|
||||
console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
return jump;
|
||||
});
|
||||
}
|
||||
|
||||
getKmlJumpFiles(jumps: Array<Jump>) {
|
||||
jumps.map(jump => {
|
||||
if (jump.x2data !== null) {
|
||||
//console.log(`Le fichier ${jump.x2data.name} existe pour le saut n°${jump.numero} !`, `https://skydiver.id/api/jump/${jump.x2data.id}/kml`);
|
||||
const file: JumpFile = {
|
||||
name: jump.x2data.name.substring(0, -4) + '.kml',
|
||||
path: `/X2_Kmls/${jump.date.substring(0, 4)}/`,
|
||||
type: JumpFileType.KML
|
||||
} as JumpFile;
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.saveKml(jump.slug, file)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (file) => {
|
||||
this._resetErrors();
|
||||
console.log(`Le fichier ${file.path}${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
})
|
||||
);
|
||||
/*
|
||||
*/
|
||||
}
|
||||
return jump;
|
||||
});
|
||||
}
|
||||
|
||||
deleteJump(slug: string) {
|
||||
this.isDeleting = true;
|
||||
this._jumpsService.destroy(slug).pipe(take(1))
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.destroy(slug)
|
||||
.pipe(take(1))
|
||||
.subscribe(() => {
|
||||
this.router.navigateByUrl('/');
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
openDeleteDialog(jump: Jump): void {
|
||||
@@ -183,12 +327,15 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
width: '60vw',
|
||||
data: jump
|
||||
});
|
||||
dialogRef.afterClosed().pipe(take(1))
|
||||
this._subscriptions.push(
|
||||
dialogRef.afterClosed()
|
||||
.pipe(take(1))
|
||||
.subscribe(result => {
|
||||
if (result != undefined) {
|
||||
this._onDelete(result.slug);
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
openEditDialog(jump: Jump): void {
|
||||
@@ -196,12 +343,15 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
width: '70vw',
|
||||
data: jump
|
||||
});
|
||||
dialogRef.afterClosed().pipe(take(1))
|
||||
.subscribe(result => {
|
||||
if (result != undefined) {
|
||||
this._onEdit(result);
|
||||
}
|
||||
});
|
||||
this._subscriptions.push(
|
||||
dialogRef.afterClosed()
|
||||
.pipe(take(1))
|
||||
.subscribe(result => {
|
||||
if (result != undefined) {
|
||||
this._onEdit(result);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
openViewDialog(jump: Jump): void {
|
||||
@@ -230,20 +380,22 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
private _onDelete(slug: string): void {
|
||||
try {
|
||||
this.isDeleting = true;
|
||||
this._jumpsService.destroy(slug)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this._resetErrors();
|
||||
this._openSnackBar(`Le saut a été supprimé !`, 'OK');
|
||||
this.runQuery();
|
||||
this.tableRefresh = !this.tableRefresh;
|
||||
this._jumpTableService.updateTableRefresh(this.tableRefresh);
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
});
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.destroy(slug)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
this._resetErrors();
|
||||
this._openSnackBar(`Le saut a été supprimé !`, 'OK');
|
||||
this.runQuery();
|
||||
this.tableRefresh = !this.tableRefresh;
|
||||
this._jumpTableService.updateTableRefresh(this.tableRefresh);
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -252,21 +404,23 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
|
||||
private _onEdit(jump: Jump): void {
|
||||
try {
|
||||
this.isSubmitting = true;
|
||||
this._jumpsService.update(jump)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (jump) => {
|
||||
this._resetErrors();
|
||||
this._openSnackBar(`Le saut n°${jump.numero} a été mis à jour !`, 'OK');
|
||||
this.runQuery();
|
||||
this.tableRefresh = !this.tableRefresh;
|
||||
this._jumpTableService.updateTableRefresh(this.tableRefresh);
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
this.isSubmitting = false;
|
||||
}
|
||||
});
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.update(jump)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (jump) => {
|
||||
this._resetErrors();
|
||||
this._openSnackBar(`Le saut n°${jump.numero} a été mis à jour !`, 'OK');
|
||||
this.runQuery();
|
||||
this.tableRefresh = !this.tableRefresh;
|
||||
this._jumpTableService.updateTableRefresh(this.tableRefresh);
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
this.isSubmitting = false;
|
||||
}
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<mat-toolbar class="topbar bg-primary">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="index.html">
|
||||
<a class="navbar-brand" href="/">
|
||||
<span class="logo">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="190px" height="33px" viewBox="0 0 190 33" enable-background="new 0 0 190 33" xml:space="preserve">
|
||||
<path id="Jumper_2" fill="#FFFFFF"
|
||||
@@ -281,8 +281,8 @@
|
||||
<app-layout-footer></app-layout-footer>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content class="page-wrapper">
|
||||
<div class="page-content">
|
||||
<!--<div class="page-content">-->
|
||||
<router-outlet><!--<app-spinner></app-spinner>--></router-outlet>
|
||||
</div>
|
||||
<!--</div>-->
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
|
||||
import { Router, RouterOutlet, RouterModule } from '@angular/router';
|
||||
import { MediaMatcher } from '@angular/cdk/layout';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -13,12 +13,17 @@ const MENUITEMS: Menu[] = [
|
||||
{ state: 'logbook', type: 'link', name: 'Carnet de saut', icon: 'assignment_turned_in', desc: '' },
|
||||
{ state: 'aeronef', type: 'link', name: 'Aéronefs', icon: 'flight', desc: '' },
|
||||
{ state: 'canopy', type: 'link', name: 'Voiles', icon: 'paragliding', desc: '' },
|
||||
{ state: 'dropzone', type: 'link', name: 'Dropzones', icon: 'adjust', desc: '' },
|
||||
{ state: 'jump', type: 'link', name: 'Sauts', icon: 'cloud_download', desc: '' },
|
||||
{ state: 'dropzone', type: 'link', name: 'Drop Zones', icon: 'flight_takeoff', desc: '' },
|
||||
{ state: 'jumps', type: 'link', name: 'Sauts', icon: 'cloud', desc: '' },
|
||||
{ state: 'calculator', type: 'link', name: 'Taille de voile', icon: 'calculate', desc: '' },
|
||||
{ state: 'qcm', type: 'menu', name: 'QCM Brevets', icon: 'rule_folder', 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' },
|
||||
@@ -63,4 +68,7 @@ export class MenuItems {
|
||||
getMenuPanel(): Menu[] {
|
||||
return MENUPANEL;
|
||||
}
|
||||
getMenuJump(): Menu[] {
|
||||
return MENUPJUMP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ export * from './calculator.model';
|
||||
export * from './canopy.model';
|
||||
export * from './canopysize.model';
|
||||
export * from './chart.model';
|
||||
export * from './reducerow.model';
|
||||
export * from './dropzone.model';
|
||||
export * from './errors.model';
|
||||
export * from './jump.model';
|
||||
@@ -14,4 +13,5 @@ export * from './jump-list.model';
|
||||
export * from './jump-list-config.model';
|
||||
export * from './profile.model';
|
||||
export * from './qcm.model';
|
||||
export * from './shared.model';
|
||||
export * from './user.model';
|
||||
|
||||
@@ -33,4 +33,6 @@ export interface JumpListFilters {
|
||||
hauteurRangeEnd?: number;
|
||||
yearRangeStart?: number;
|
||||
yearRangeEnd?: number;
|
||||
dateRangeStart?: string;
|
||||
dateRangeEnd?: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
import { Profile } from './profile.model';
|
||||
import { PageLinks, PageMeta } from './shared.model';
|
||||
|
||||
export interface Jump {
|
||||
slug: string;
|
||||
@@ -23,12 +24,26 @@ export interface Jump {
|
||||
zone?: string;
|
||||
dossier?: string;
|
||||
video?: string;
|
||||
files: Array<JumpFile>;
|
||||
x2data: X2Data;
|
||||
author: Profile;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
//isEdit?: boolean;
|
||||
isSelected?: boolean;
|
||||
}
|
||||
export enum JumpFileType {
|
||||
CSV = "csv",
|
||||
IMAGE = "image",
|
||||
KML = "kml",
|
||||
VIDEO = "video",
|
||||
}
|
||||
export interface JumpFile {
|
||||
slug: string;
|
||||
name: string;
|
||||
path: string;
|
||||
type: JumpFileType;
|
||||
}
|
||||
|
||||
export interface JumpForm {
|
||||
date: FormControl<string>;
|
||||
@@ -69,6 +84,11 @@ export interface JumpByCategorie {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface JumpByDay {
|
||||
jumps: Array<Partial<Jump>>;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface JumpByModule {
|
||||
categorie: string;
|
||||
module: string;
|
||||
@@ -88,12 +108,89 @@ export interface JumpYears {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface SkydiverIdJumps {
|
||||
items: Array<X2Data>;
|
||||
_links: PageLinks;
|
||||
_meta: PageMeta;
|
||||
}
|
||||
|
||||
export interface X2Data {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
landingZone: string;
|
||||
speed: {
|
||||
freeFall: {
|
||||
vertical: {
|
||||
max: number;
|
||||
avg: number;
|
||||
};
|
||||
horizontal: {
|
||||
max: number;
|
||||
avg: number;
|
||||
};
|
||||
};
|
||||
underCanopy: {
|
||||
vertical: {
|
||||
max: number;
|
||||
avg: number;
|
||||
};
|
||||
horizontal: {
|
||||
max: number;
|
||||
avg: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
glideRatio: {
|
||||
freeFall: {
|
||||
max: number;
|
||||
avg: number;
|
||||
};
|
||||
underCanopy: {
|
||||
max: number;
|
||||
avg: number;
|
||||
};
|
||||
};
|
||||
duration: {
|
||||
freeFall: number;
|
||||
underCanopy: number;
|
||||
};
|
||||
distance: {
|
||||
exitFromLandingZone: number;
|
||||
totalFlying: number;
|
||||
totalHorizontal: number;
|
||||
};
|
||||
altitude: {
|
||||
exit: number;
|
||||
deployment: number;
|
||||
};
|
||||
cutaway: string;
|
||||
malfunctions:Array<
|
||||
{
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
>;
|
||||
date: string;
|
||||
createdOn: string;
|
||||
}
|
||||
|
||||
export interface JumpPageData {
|
||||
jump: Jump;
|
||||
prevJump: Jump;
|
||||
nextJump: Jump;
|
||||
_links: PageLinks;
|
||||
_meta: PageMeta;
|
||||
}
|
||||
|
||||
export interface JumpsPageData {
|
||||
jumpsByCategory: Array<JumpByCategorie>;
|
||||
jumpsByDate: Array<JumpByDate>;
|
||||
jumpsByDateByModule: Array<JumpByDateByModule>;
|
||||
jumpsByModule: Array<JumpByModule>;
|
||||
jumpsByYears: Array<JumpYears>;
|
||||
//jumpsFromSkydiverIdApi: SkydiverIdJumps;
|
||||
lastjump: Jump;
|
||||
/*yearsCount: number;
|
||||
jumpsByCategoryCount: number;
|
||||
|
||||
@@ -7,4 +7,5 @@ export interface Menu {
|
||||
icon: string;
|
||||
desc?: string;
|
||||
badge?: Badge[];
|
||||
color?: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
export interface PageLinks {
|
||||
self: {
|
||||
href: string;
|
||||
};
|
||||
first: {
|
||||
href: string;
|
||||
};
|
||||
last: {
|
||||
href: string;
|
||||
};
|
||||
prev: {
|
||||
href: string;
|
||||
};
|
||||
next: {
|
||||
href: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PageMeta {
|
||||
totalCount: number;
|
||||
pageCount: number;
|
||||
currentPage: number;
|
||||
perPage: number;
|
||||
}
|
||||
|
||||
export interface ReduceRow {
|
||||
key: string[];
|
||||
value: number;
|
||||
}
|
||||
@@ -2,10 +2,10 @@ import { inject } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, ResolveFn } from '@angular/router';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { Jump } from 'src/app/core/models';
|
||||
import { JumpPageData } from 'src/app/core/models';
|
||||
import { JumpsService } from 'src/app/core/services';
|
||||
|
||||
export const jumpResolver: ResolveFn<Jump> = (
|
||||
export const jumpResolver: ResolveFn<JumpPageData> = (
|
||||
route: ActivatedRouteSnapshot
|
||||
) => {
|
||||
return inject(JumpsService).get(route.params['slug']).pipe(take(1));
|
||||
|
||||
@@ -23,7 +23,9 @@ export class ApplicationsService {
|
||||
return this.apiService
|
||||
.get(
|
||||
'/applications' + ((config.type === 'feed') ? '/feed' : ''),
|
||||
new HttpParams({ fromObject: <any>params })
|
||||
new HttpParams({ fromObject: <{
|
||||
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
|
||||
}>params })
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ export class CalculatorService {
|
||||
|
||||
private _getTableLine(weight: number): number {
|
||||
let line: number;
|
||||
let min = 60;
|
||||
let max = 110;
|
||||
const min = 60;
|
||||
const max = 110;
|
||||
if (weight < min) {
|
||||
line = min;
|
||||
} else if (weight > max) {
|
||||
@@ -52,14 +52,14 @@ export class CalculatorService {
|
||||
/* const maxRange = 2000; */
|
||||
/* DT48 - 08 février 2024 */
|
||||
const maxRange = 1600;
|
||||
let result: CalculatorResult = {
|
||||
const result: CalculatorResult = {
|
||||
min: 59,
|
||||
min11: 34
|
||||
};
|
||||
if (jumps > maxRange) {
|
||||
return result;
|
||||
}
|
||||
let index: number = (this._getTableLine(weight) - this._getTableLine(0));
|
||||
const index: number = (this._getTableLine(weight) - this._getTableLine(0));
|
||||
result.min = weightSizes[index].ranges[this._getTableColumn(jumps)].value!;
|
||||
result.min11 = this._reduceLimit(result.min, 11);
|
||||
|
||||
@@ -71,27 +71,23 @@ export class CalculatorService {
|
||||
}
|
||||
|
||||
public getCanopySizes(weight: number, reduce = false): number[] {
|
||||
var data: number[] = [];
|
||||
if (weight < 60) {
|
||||
weight = 60;
|
||||
}
|
||||
if (weight > 110) {
|
||||
weight = 110;
|
||||
}
|
||||
if (weight >= 60 && weight <= 110) {
|
||||
const line = (weight - this._getTableLine(0));
|
||||
let data = weightSizes[line];
|
||||
if (reduce) {
|
||||
return data.ranges.map((range: WeightSizeRange): number => {
|
||||
return Math.ceil(range.value! * (100 - 11) / 100);
|
||||
});
|
||||
} else {
|
||||
return data.ranges.map((range: WeightSizeRange): number => {
|
||||
return range.value!;
|
||||
});
|
||||
}
|
||||
const line = (weight - this._getTableLine(0));
|
||||
const data = weightSizes[line];
|
||||
if (reduce) {
|
||||
return data.ranges.map((range: WeightSizeRange): number => {
|
||||
return Math.ceil(range.value! * (100 - 11) / 100);
|
||||
});
|
||||
} else {
|
||||
return data.ranges.map((range: WeightSizeRange): number => {
|
||||
return range.value!;
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public getCharge(canopySize: number, nakedWeight: number, equipementWeight: number): number {
|
||||
@@ -99,7 +95,7 @@ export class CalculatorService {
|
||||
}
|
||||
|
||||
public getRangeNum(jumps: number): number {
|
||||
let data: WeightSizeRange[] = weightSizes[0].ranges;
|
||||
const data: WeightSizeRange[] = weightSizes[0].ranges;
|
||||
let num: number = 1;
|
||||
/* DT48 - 13 mars 2020 */
|
||||
/* const maxRange = 2000; */
|
||||
|
||||
@@ -11,8 +11,8 @@ export class JumpTableService {
|
||||
_jumpRefreshBS = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor() {
|
||||
this.tableRefresh = false;;
|
||||
this.jumpRefresh = false;;
|
||||
this.tableRefresh = false;
|
||||
this.jumpRefresh = false;
|
||||
|
||||
//this._tableRefreshBS.next(this.tableRefresh);
|
||||
//this._jumpRefreshBS.next(this.jumpRefresh);
|
||||
|
||||
@@ -4,7 +4,11 @@ import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ApiService } from './api.service';
|
||||
import { Jump, JumpByCategorie, JumpByDate, JumpByModule, JumpList, JumpListConfig, JumpListFilters } from 'src/app/core/models';
|
||||
import {
|
||||
Jump, JumpByCategorie, JumpByDate, JumpByDay, JumpByModule, JumpFile,
|
||||
JumpList, JumpListConfig, JumpListFilters, JumpPageData,
|
||||
X2Data, SkydiverIdJumps
|
||||
} from 'src/app/core/models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class JumpsService {
|
||||
@@ -44,52 +48,67 @@ export class JumpsService {
|
||||
*/
|
||||
const params: JumpListFilters = {} as JumpListFilters;
|
||||
Object.assign(params, config.filters);
|
||||
|
||||
return this.apiService
|
||||
.get(
|
||||
'/jumps' + ((config.type === 'feed') ? '/feed' : ''),
|
||||
new HttpParams({ fromObject: <any>params })
|
||||
new HttpParams({ fromObject: <{
|
||||
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
|
||||
}>params })
|
||||
);
|
||||
}
|
||||
|
||||
get(slug: string): Observable<Jump> {
|
||||
return this.apiService.get(`/jumps/${slug}`)
|
||||
.pipe(map(data => data.jump));
|
||||
get(slug: string): Observable<JumpPageData> {
|
||||
/* return this.apiService.get(`/jumps/${slug}`)
|
||||
.pipe(map(data => {
|
||||
return { jump: data.jump, prevJump: data.prevJump, nextJump: data.nextJump };
|
||||
})); */
|
||||
return this.apiService.get(`/jumps/${slug}`);
|
||||
}
|
||||
|
||||
getAll(): Observable<[Jump]> {
|
||||
return this.apiService.get('/jumps')
|
||||
.pipe(map(data => data.jumps));
|
||||
getAll(): Observable<Array<Jump>> {
|
||||
return this.apiService.get('/jumps').pipe(map(data => data.jumps));
|
||||
}
|
||||
|
||||
getAllByDate(): Observable<Array<JumpByDate>> {
|
||||
return this.apiService.get('/jumps/allByDate')
|
||||
.pipe(map(data => data.jumps));
|
||||
return this.apiService.get('/jumps/allByDate').pipe(map(data => data.jumps));
|
||||
}
|
||||
|
||||
getAllByCategorie(): Observable<Array<JumpByCategorie>> {
|
||||
return this.apiService.get('/jumps/allByCategorie')
|
||||
.pipe(map(data => data.jumps));
|
||||
return this.apiService.get('/jumps/allByCategorie').pipe(map(data => data.jumps));
|
||||
}
|
||||
|
||||
getAllByDay(config: JumpListConfig): Observable<Array<JumpByDay>> {
|
||||
const params: JumpListFilters = {} as JumpListFilters;
|
||||
Object.assign(params, config.filters);
|
||||
return this.apiService.get('/jumps/allByDay', new HttpParams({ fromObject: <{
|
||||
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
|
||||
}>params }))
|
||||
.pipe(map(data => data.days));
|
||||
}
|
||||
|
||||
getAllByModule(): Observable<Array<JumpByModule>> {
|
||||
return this.apiService.get('/jumps/allByModule')
|
||||
return this.apiService.get('/jumps/allByModule').pipe(map(data => data.jumps));
|
||||
}
|
||||
|
||||
getAllFromSkydiverIdApi(config: JumpListConfig): Observable<SkydiverIdJumps> {
|
||||
const params: JumpListFilters = {} as JumpListFilters;
|
||||
Object.assign(params, config.filters);
|
||||
return this.apiService.get('/jumps/allFromSkydiverIdApi', new HttpParams({ fromObject: <{
|
||||
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
|
||||
}>params }))
|
||||
.pipe(map(data => data.jumps));
|
||||
}
|
||||
|
||||
getLastJump(): Observable<Jump> {
|
||||
return this.apiService.get('/jumps/last')
|
||||
.pipe(map(data => data.jump));
|
||||
return this.apiService.get('/jumps/last').pipe(map(data => data.jump));
|
||||
}
|
||||
|
||||
create(jump: Jump): Observable<Jump> {
|
||||
return this.apiService.post('/jumps/', { jump: jump })
|
||||
.pipe(map(data => data.jump));
|
||||
return this.apiService.post('/jumps/', { jump: jump }).pipe(map(data => data.jump));
|
||||
}
|
||||
|
||||
update(jump: Jump): Observable<Jump> {
|
||||
return this.apiService.put(`/jumps/${jump.slug}`, { jump: jump })
|
||||
.pipe(map(data => data.jump));
|
||||
return this.apiService.put(`/jumps/${jump.slug}`, { jump: jump }).pipe(map(data => data.jump));
|
||||
}
|
||||
|
||||
destroy(slug: string) {
|
||||
@@ -99,16 +118,29 @@ export class JumpsService {
|
||||
save(jump: Jump): Observable<Jump> {
|
||||
// If we're updating an existing jump
|
||||
if (jump.slug) {
|
||||
return this.apiService.put('/jumps/' + jump.slug, { jump: jump })
|
||||
.pipe(map(data => data.jump));
|
||||
return this.apiService.put(`/jumps/${jump.slug}`, { jump: jump }).pipe(map(data => data.jump));
|
||||
|
||||
// Otherwise, create a new jump
|
||||
} else {
|
||||
return this.apiService.post('/jumps/', { jump: jump })
|
||||
.pipe(map(data => data.jump));
|
||||
return this.apiService.post('/jumps/', { jump: jump }).pipe(map(data => data.jump));
|
||||
}
|
||||
}
|
||||
|
||||
saveX2Data(slug: string, file: JumpFile, data: X2Data): Observable<Jump> {
|
||||
return this.apiService.post(`/jumps/data/${slug}`, { file: file, data: data })
|
||||
.pipe(map(data => data.jump));
|
||||
}
|
||||
|
||||
saveFile(slug: string, file: JumpFile): Observable<JumpFile> {
|
||||
return this.apiService.post(`/jumps/file/${slug}`, { file: file })
|
||||
.pipe(map(data => data.file));
|
||||
}
|
||||
|
||||
saveKml(slug: string, file: JumpFile): Observable<JumpFile> {
|
||||
return this.apiService.post(`/jumps/kml/${slug}`, { file: file })
|
||||
.pipe(map(data => data.file));
|
||||
}
|
||||
|
||||
getAeronefs() {
|
||||
/*
|
||||
[
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BubbleDataPoint, Chart, ChartTypeRegistry, Point } from 'chart.js';
|
||||
import { Chart } from 'chart.js';
|
||||
import { Configuration } from 'ng-chartist';
|
||||
|
||||
import { BarConfig, CircleConfig, DoughnutConfig, LineConfig } from 'src/app/core/models';
|
||||
|
||||
@@ -2,13 +2,13 @@ import { Routes } from '@angular/router';
|
||||
|
||||
import {
|
||||
AeronefComponent, CalculatorComponent, CanopyComponent, CredentialsComponent,
|
||||
DashboardComponent, DropzoneComponent, JumpComponent, LogbookComponent,
|
||||
ProfileComponent, QcmComponent, SettingsComponent
|
||||
DashboardComponent, DropzoneComponent, JumpComponent, JumpsComponent,
|
||||
LogbookComponent, ProfileComponent, QcmComponent, SettingsComponent
|
||||
} from 'src/app/components';
|
||||
import { authGuard } from 'src/app/core/guards';
|
||||
import {
|
||||
aeronefsPageResolver, authResolver, canopiesPageResolver, dropZonesPageResolver,
|
||||
jumpsPageResolver, lastjumpResolver, profileResolver, qcmResolver
|
||||
jumpResolver, jumpsPageResolver, lastjumpResolver, profileResolver, qcmResolver
|
||||
} from 'src/app/core/resolvers';
|
||||
|
||||
export const AUTH_ROUTES: Routes = [
|
||||
@@ -53,19 +53,20 @@ export const AUTH_ROUTES: Routes = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'jump',
|
||||
path: 'jumps',
|
||||
component: JumpsComponent,
|
||||
canActivate: [ authGuard ],
|
||||
resolve: {
|
||||
pageData: jumpsPageResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'jump/:slug',
|
||||
component: JumpComponent,
|
||||
canActivate: [ authGuard ],
|
||||
resolve: {
|
||||
jumpsPageData: jumpsPageResolver
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':slug',
|
||||
component: JumpComponent,
|
||||
canActivate: [ authGuard ]
|
||||
}
|
||||
]
|
||||
pageData: jumpResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'logbook',
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 982 241" style="width: max(calc(700 * min(100vw, 2560px) / 1470), 350px); height: auto;">
|
||||
<g clip-path="url(#wordmark_a)">
|
||||
<path fill="url(#wordmark_b)" d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.603 64.311h-44.432Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.038-7.037 7.038 0 12.847 1.145 17.348 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.746 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.619 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.01 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.091 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.815 58.815 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"></path>
|
||||
<path fill="url(#wordmark_c)" d="M388.676 191.625h30.849L363.31 31.828h-35.758l-56.215 159.797h30.848l13.174-39.356h60.061l13.256 39.356Zm-65.461-62.675 21.602-64.311h1.227l21.603 64.311h-44.432Zm126.831-7.527v70.202h-28.23V71.839h27.002v20.374h1.392c2.782-6.71 7.2-12.028 13.255-15.956 6.056-3.927 13.584-5.89 22.503-5.89 8.264 0 15.465 1.8 21.684 5.318 6.137 3.518 10.964 8.673 14.319 15.382 3.437 6.71 5.074 14.81 4.992 24.383v76.175h-28.23v-71.92c0-8.019-2.046-14.237-6.219-18.819-4.173-4.5-9.819-6.791-17.102-6.791-4.91 0-9.328 1.063-13.174 3.272-3.846 2.128-6.792 5.237-9.001 9.328-2.046 4.009-3.191 8.918-3.191 14.728ZM589.233 239c-10.147 0-18.82-1.391-26.103-4.091-7.282-2.7-13.092-6.382-17.511-10.964-4.418-4.582-7.528-9.655-9.164-15.219l25.448-6.136c1.145 2.372 2.782 4.663 4.991 6.954 2.209 2.291 5.155 4.255 8.837 5.81 3.683 1.554 8.428 2.291 14.074 2.291 8.019 0 14.647-1.964 19.884-5.81 5.237-3.845 7.856-10.227 7.856-19.064v-22.665h-1.391c-1.473 2.946-3.601 5.892-6.383 9.001-2.782 3.109-6.464 5.645-10.965 7.691-4.582 2.046-10.228 3.109-17.101 3.109-9.165 0-17.511-2.209-25.039-6.545-7.446-4.337-13.42-10.883-17.757-19.474-4.418-8.673-6.628-19.473-6.628-32.565 0-13.091 2.21-24.301 6.628-33.383 4.419-9.082 10.311-15.955 17.839-20.7 7.528-4.746 15.874-7.037 25.038-7.037 7.038 0 12.847 1.145 17.348 3.518 4.582 2.373 8.182 5.236 10.883 8.51 2.7 3.272 4.746 6.382 6.137 9.327h1.554v-19.8h27.821v121.749c0 10.228-2.454 18.737-7.364 25.447-4.91 6.709-11.538 11.7-20.048 15.055-8.509 3.355-18.165 4.991-28.884 4.991Zm.245-71.266c5.974 0 11.047-1.473 15.302-4.337 4.173-2.945 7.446-7.118 9.573-12.519 2.21-5.482 3.274-12.027 3.274-19.637 0-7.609-1.064-14.155-3.274-19.8-2.127-5.646-5.318-10.064-9.491-13.255-4.174-3.11-9.329-4.746-15.384-4.746s-11.537 1.636-15.792 4.91c-4.173 3.272-7.365 7.772-9.492 13.418-2.128 5.727-3.191 12.191-3.191 19.392 0 7.2 1.063 13.746 3.273 19.228 2.127 5.482 5.318 9.736 9.573 12.764 4.174 3.027 9.41 4.582 15.629 4.582Zm141.56-26.51V71.839h28.23v119.786h-27.412v-21.273h-1.227c-2.7 6.709-7.119 12.191-13.338 16.446-6.137 4.255-13.747 6.382-22.748 6.382-7.855 0-14.81-1.718-20.783-5.237-5.974-3.518-10.72-8.591-14.075-15.382-3.355-6.709-5.073-14.891-5.073-24.464V71.839h28.312v71.921c0 7.609 2.046 13.664 6.219 18.083 4.173 4.5 9.655 6.709 16.365 6.709 4.173 0 8.183-.982 12.111-3.028 3.927-2.045 7.118-5.072 9.655-9.082 2.537-4.091 3.764-9.164 3.764-15.218Zm65.707-109.395v159.796h-28.23V31.828h28.23Zm44.841 162.169c-7.61 0-14.402-1.391-20.457-4.091-6.055-2.7-10.883-6.791-14.32-12.109-3.518-5.319-5.237-11.946-5.237-19.801 0-6.791 1.228-12.355 3.765-16.773 2.536-4.419 5.891-7.937 10.228-10.637 4.337-2.619 9.164-4.664 14.647-6.055 5.4-1.391 11.046-2.373 16.856-3.027 7.037-.737 12.683-1.391 17.102-1.964 4.337-.573 7.528-1.555 9.574-2.782 1.963-1.309 3.027-3.273 3.027-5.973v-.491c0-5.891-1.718-10.391-5.237-13.664-3.518-3.191-8.51-4.828-15.056-4.828-6.955 0-12.356 1.473-16.447 4.5-4.01 3.028-6.71 6.546-8.183 10.719l-26.348-3.764c2.046-7.282 5.483-13.336 10.31-18.328 4.746-4.909 10.638-8.59 17.511-11.045 6.955-2.455 14.565-3.682 22.912-3.682 5.809 0 11.537.654 17.265 2.045s10.965 3.6 15.711 6.71c4.746 3.109 8.51 7.282 11.455 12.6 2.864 5.318 4.337 11.946 4.337 19.883v80.184h-27.166v-16.446h-.9c-1.719 3.355-4.091 6.464-7.201 9.328-3.109 2.864-6.955 5.237-11.619 6.955-4.828 1.718-10.229 2.536-16.529 2.536Zm7.364-20.701c5.646 0 10.556-1.145 14.729-3.354 4.173-2.291 7.364-5.237 9.655-9.001 2.292-3.763 3.355-7.854 3.355-12.273v-14.155c-.9.737-2.373 1.391-4.5 2.046-2.128.654-4.419 1.145-7.037 1.636-2.619.491-5.155.9-7.692 1.227-2.537.328-4.746.655-6.628.901-4.173.572-8.019 1.472-11.292 2.781-3.355 1.31-5.973 3.11-7.855 5.401-1.964 2.291-2.864 5.318-2.864 8.918 0 5.237 1.882 9.164 5.728 11.782 3.682 2.782 8.51 4.091 14.401 4.091Zm64.643 18.328V71.839h27.412v19.965h1.227c2.21-6.955 5.974-12.274 11.292-16.038 5.319-3.763 11.456-5.645 18.329-5.645 1.555 0 3.355.082 5.237.163 1.964.164 3.601.328 4.91.573v25.938c-1.227-.41-3.109-.819-5.646-1.146a58.815 58.815 0 0 0-7.446-.49c-5.155 0-9.738 1.145-13.829 3.354-4.091 2.209-7.282 5.236-9.655 9.164-2.373 3.927-3.519 8.427-3.519 13.5v70.448h-28.312ZM222.077 39.192l-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"></path>
|
||||
</g>
|
||||
<defs _ngcontent-ng-c433524483="">
|
||||
<radialGradient id="wordmark_c" cx="0" cy="0" r="1" gradientTransform="rotate(118.122 171.182 60.81) scale(205.794)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF41F8"></stop>
|
||||
<stop offset=".707" stop-color="#FF41F8" stop-opacity=".5"></stop>
|
||||
<stop offset="1" stop-color="#FF41F8" stop-opacity="0"></stop>
|
||||
</radialGradient>
|
||||
<linearGradient id="wordmark_b" x1="0" x2="982" y1="192" y2="192" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0060B"></stop>
|
||||
<stop offset="0" stop-color="#F0070C"></stop>
|
||||
<stop offset=".526" stop-color="#CC26D5"></stop>
|
||||
<stop offset="1" stop-color="#7702FF"></stop>
|
||||
</linearGradient>
|
||||
<clipPath id="wordmark_a">
|
||||
<path fill="#fff" d="M0 0h982v239H0z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
+1
-6
@@ -13,12 +13,7 @@ html {
|
||||
background: $white;
|
||||
.page-wrapper{
|
||||
height:calc(100vh - 64px);
|
||||
.page-content{
|
||||
padding-bottom: 30px;
|
||||
.content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
body {
|
||||
|
||||
Reference in New Issue
Block a user