develop #5

Merged
william merged 42 commits from develop into master 2021-12-15 00:25:11 -05:00
26 changed files with 383 additions and 296 deletions
Showing only changes of commit c21dd8d0f9 - Show all commits

View File

@ -8,12 +8,12 @@ services:
MYSQL_ROOT_PASSWORD: "pass"
MYSQL_DATABASE: "cre"
ports:
- "3306:3306"
- "3307:3306"
backend:
image: registry.fyloz.dev:5443/colorrecipesexplorer/backend:latest
environment:
spring_profiles_active: "mysql,debug"
cre_database_url: "mysql://database:3306/cre"
cre_database_url: "mysql://database:3307/cre"
cre_database_username: "root"
cre_database_password: "pass"
CRE_ENABLE_DB_UPDATE: 1

View File

@ -1 +1,10 @@
<recipe-form (submitForm)="submit($event)"></recipe-form>
<cre-action-bar>
<cre-action-group>
<cre-primary-button routerLink="/color/list">Retour</cre-primary-button>
</cre-action-group>
<cre-action-group>
<cre-submit-button [form]="recipeForm.creForm" (submit)="recipeForm.submit()"></cre-submit-button>
</cre-action-group>
</cre-action-bar>
<recipe-form #recipeForm (submitForm)="submit($event)"></recipe-form>

View File

@ -1,132 +1,122 @@
<mat-card *ngIf="recipe && (!editionMode || mix)" class="x-centered mt-5">
<mat-card-header>
<mat-card-title *ngIf="!editionMode">Création d'un mélange pour la recette {{recipe.company.name}}
- {{recipe.name}}</mat-card-title>
<mat-card-title *ngIf="editionMode">Modification du mélange {{mix.mixType.name}} de la
recette {{recipe.company.name}} - {{recipe.name}}</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-form-field>
<mat-label>Nom</mat-label>
<input matInput type="text" [formControl]="nameControl"/>
<mat-icon svgIcon="form-textbox" matSuffix></mat-icon>
</mat-form-field>
<mat-form-field>
<mat-label>Type de produit</mat-label>
<mat-select [formControl]="materialTypeControl">
<mat-option
*ngFor="let materialType of (materialTypes$ | async)"
[value]="materialType.id">
{{materialType.name}}
</mat-option>
</mat-select>
</mat-form-field>
<ng-container *ngIf="recipe && materials && (!editionMode || mix)">
<cre-action-bar>
<cre-action-group>
<cre-primary-button routerLink="/color/edit/{{recipeId}}">Retour</cre-primary-button>
</cre-action-group>
<cre-action-group>
<cre-warn-button *ngIf="editionMode" (click)="deleteConfirmBox.show()">Supprimer</cre-warn-button>
<cre-submit-button [form]="creForm" (submit)="submit()">Enregistrer</cre-submit-button>
</cre-action-group>
</cre-action-bar>
<div class="mix-materials-wrapper">
<ng-container *ngTemplateOutlet="mixEditor"></ng-container>
</div>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="primary" routerLink="/color/edit/{{recipeId}}">Retour</button>
<button *ngIf="editionMode" mat-raised-button color="warn" (click)="deleteConfirmBox.show()">
Supprimer
</button>
<button mat-raised-button color="accent" [disabled]="!form.valid" (click)="submit()">Enregistrer</button>
</mat-card-actions>
</mat-card>
<cre-form #creForm [formControls]="controls" class="mx-auto">
<cre-form-title *ngIf="!editionMode">Ajouter un mélange à la couleur {{recipe.company.name}}
- {{recipe.name}}</cre-form-title>
<cre-form-title *ngIf="editionMode">Modification du mélange {{mix.mixType.name}} de la
couleur {{recipe.company.name}}
- {{recipe.name}}</cre-form-title>
<ng-template #mixEditor>
<table #matTable mat-table [dataSource]="mixMaterials">
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef>Position</th>
<td mat-cell *matCellDef="let mixMaterial">
{{mixMaterial.position}}
</td>
</ng-container>
<cre-form-content>
<cre-input [control]="controls.name" label="name" icon="form-textbox"></cre-input>
<cre-select [control]="controls.materialType" label="Type de produit"
[entries]="materialTypeEntries$"></cre-select>
</cre-form-content>
</cre-form>
</ng-container>
<ng-container matColumnDef="buttonsPosition">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let mixMaterial; let i = index">
<ng-container *ngIf="(!hoveredMixMaterial && i === 0) || hoveredMixMaterial === mixMaterial">
<button
mat-mini-fab
color="primary"
class="mr-1"
[disabled]="mixMaterial.position <= 1"
(click)="decreasePosition(mixMaterial, matTable)">
<mat-icon svgIcon="arrow-up"></mat-icon>
</button>
<button
mat-mini-fab
color="primary"
[disabled]="mixMaterial.position >= mixMaterials.length"
(click)="increasePosition(mixMaterial, matTable)">
<mat-icon svgIcon="arrow-down"></mat-icon>
</button>
<table #matTable mat-table [dataSource]="mixMaterials" class="mx-auto mt-5">
<ng-container matColumnDef="position">
<th mat-header-cell *matHeaderCellDef>Position</th>
<td mat-cell *matCellDef="let mixMaterial">
{{mixMaterial.position}}
</td>
</ng-container>
<ng-container matColumnDef="buttonsPosition">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let mixMaterial; let i = index">
<ng-container *ngIf="(!hoveredMixMaterial && i === 0) || hoveredMixMaterial === mixMaterial">
<button
mat-mini-fab
color="primary"
class="mr-1"
[disabled]="mixMaterial.position <= 1"
(click)="decreasePosition(mixMaterial, matTable)">
<mat-icon svgIcon="arrow-up"></mat-icon>
</button>
<button
mat-mini-fab
color="primary"
[disabled]="mixMaterial.position >= mixMaterials.length"
(click)="increasePosition(mixMaterial, matTable)">
<mat-icon svgIcon="arrow-down"></mat-icon>
</button>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="material">
<th mat-header-cell *matHeaderCellDef>Produit</th>
<td mat-cell *matCellDef="let mixMaterial">
<!-- <mat-form-field *ngIf="materials">-->
<!-- <mat-select-->
<!-- [value]="mixMaterial.materialId"-->
<!-- (valueChange)="setMixMaterialMaterial(mixMaterial, $event)">-->
<!-- <mat-option-->
<!-- *ngFor="let material of sortedMaterials(getAvailableMaterials(mixMaterial))"-->
<!-- [value]="material.id">-->
<!-- {{materialDisplayName(material)}}-->
<!-- </mat-option>-->
<!-- </mat-select>-->
<!-- </mat-form-field>-->
<cre-select [entries]="getAvailableMaterials(mixMaterial)"></cre-select>
</td>
</ng-container>
<ng-container matColumnDef="quantity">
<th mat-header-cell *matHeaderCellDef>Quantité</th>
<td mat-cell *matCellDef="let mixMaterial">
<mat-form-field>
<input matInput type="number" step="0.001" [(ngModel)]="mixMaterial.quantity"/>
</mat-form-field>
</td>
</ng-container>
<ng-container matColumnDef="units">
<th mat-header-cell *matHeaderCellDef>Unités</th>
<td mat-cell *matCellDef="let mixMaterial" class="units-wrapper">
<ng-container *ngIf="materials">
<ng-container *ngIf="mixMaterial.isPercents">
<p>%</p>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="material">
<th mat-header-cell *matHeaderCellDef>Produit</th>
<td mat-cell *matCellDef="let mixMaterial">
<mat-form-field *ngIf="materials">
<mat-select
[value]="mixMaterial.materialId"
(valueChange)="setMixMaterialMaterial(mixMaterial, $event)">
<mat-option
*ngFor="let material of sortedMaterials(getAvailableMaterials(mixMaterial))"
[value]="material.id">
{{materialDisplayName(material)}}
</mat-option>
</mat-select>
</mat-form-field>
</td>
</ng-container>
<ng-container matColumnDef="quantity">
<th mat-header-cell *matHeaderCellDef>Quantité</th>
<td mat-cell *matCellDef="let mixMaterial">
<mat-form-field>
<input matInput type="number" step="0.001" [(ngModel)]="mixMaterial.quantity"/>
</mat-form-field>
</td>
</ng-container>
<ng-container matColumnDef="units">
<th mat-header-cell *matHeaderCellDef>Unités</th>
<td mat-cell *matCellDef="let mixMaterial" class="units-wrapper">
<ng-container *ngIf="materials">
<ng-container *ngIf="mixMaterial.isPercents">
<p>%</p>
<ng-container *ngIf="!mixMaterial.isPercents">
<ng-container *ngIf="!hoveredMixMaterial || hoveredMixMaterial != mixMaterial">
<span>{{units}}</span>
</ng-container>
<ng-container *ngIf="!mixMaterial.isPercents">
<ng-container *ngIf="!hoveredMixMaterial || hoveredMixMaterial != mixMaterial">
<span>{{units}}</span>
</ng-container>
<ng-container *ngIf="hoveredMixMaterial && hoveredMixMaterial == mixMaterial">
<cre-unit-selector [(unit)]="units" [showLabel]="false" [short]="true"></cre-unit-selector>
</ng-container>
<ng-container *ngIf="hoveredMixMaterial && hoveredMixMaterial == mixMaterial">
<cre-unit-selector [(unit)]="units" [showLabel]="false" [short]="true"></cre-unit-selector>
</ng-container>
</ng-container>
</td>
</ng-container>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="buttonRemove">
<th mat-header-cell *matHeaderCellDef>
<button mat-raised-button color="accent" (click)="addRow()">Ajouter</button>
</th>
<td mat-cell *matCellDef="let mixMaterial; let i = index">
<ng-container *ngIf="hoveredMixMaterial && hoveredMixMaterial == mixMaterial">
<button mat-raised-button color="warn" (click)="removeRow(i)">Retirer</button>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="buttonRemove">
<th mat-header-cell *matHeaderCellDef>
<button mat-raised-button color="accent" (click)="addRow()">Ajouter</button>
</th>
<td mat-cell *matCellDef="let mixMaterial; let i = index">
<ng-container *ngIf="hoveredMixMaterial && hoveredMixMaterial == mixMaterial">
<button mat-raised-button color="warn" (click)="removeRow(i)">Retirer</button>
</ng-container>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columns"></tr>
<tr mat-row *matRowDef="let mixMaterial; columns: columns" (mouseover)="hoveredMixMaterial = mixMaterial"></tr>
</table>
</ng-template>
<tr mat-header-row *matHeaderRowDef="tableColumns"></tr>
<tr mat-row *matRowDef="let mixMaterial; columns: tableColumns" (mouseover)="hoveredMixMaterial = mixMaterial"></tr>
</table>
<cre-confirm-box
*ngIf="editionMode && mix"

View File

@ -10,16 +10,19 @@ import {
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {MixService} from '../../services/mix.service'
import {RecipeService} from '../../services/recipe.service'
import {Material} from '../../../shared/model/material.model'
import {Material, materialComparator} from '../../../shared/model/material.model'
import {MaterialService} from '../../../material/service/material.service'
import {MaterialTypeService} from '../../../material-type/service/material-type.service'
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'
import {FormBuilder, FormControl, Validators} from '@angular/forms'
import {UNIT_MILLILITER} from '../../../shared/units'
import {MatTable} from '@angular/material/table'
import {ActivatedRoute, Router} from '@angular/router'
import {ConfirmBoxComponent} from '../../../shared/components/confirm-box/confirm-box.component'
import {AccountService} from '../../../accounts/services/account.service'
import {ErrorService} from '../../../shared/service/error.service'
import {map, tap} from 'rxjs/operators';
import {CreInputEntry} from '../../../shared/components/inputs/inputs';
import {CreForm, ICreForm} from '../../../shared/components/forms/forms';
@Component({
selector: 'cre-mix-editor',
@ -29,6 +32,7 @@ import {ErrorService} from '../../../shared/service/error.service'
export class MixEditorComponent extends ErrorHandlingComponent {
@ViewChild('matTable') mixTable: MatTable<MixMaterial>
@ViewChild('deleteConfirmBox') deleteConfirmBox: ConfirmBoxComponent
@ViewChild(CreForm) creForm: ICreForm
@Input() mixId: number | null
@Input() recipeId: number | null
@ -36,35 +40,36 @@ export class MixEditorComponent extends ErrorHandlingComponent {
@Output() save = new EventEmitter<any>()
recipe: Recipe
mix: Mix | null
recipe: Recipe | null
materialTypes$ = this.materialTypeService.all
form: FormGroup
nameControl: FormControl
materialTypeControl: FormControl
mixMaterials: MixMaterialDto[] = []
editionMode = false
units = UNIT_MILLILITER
hoveredMixMaterial: MixMaterial | null
columns = ['position', 'buttonsPosition', 'material', 'quantity', 'units', 'buttonRemove']
tableColumns = ['position', 'buttonsPosition', 'material', 'quantity', 'units', 'buttonRemove']
deleting = false
errorHandlers = [{
filter: error => error.type === 'notfound-mix-id',
consumer: error => this.urlUtils.navigateTo('/color/list')
consumer: _ => this.urlUtils.navigateTo('/color/list')
}, {
filter: error => error.type === 'exists-material-name',
messageProducer: error => `Un produit avec le nom '${error.name}' existe déjà`
}, {
filter: error => error.type === 'cannotdelete-mix',
messageProducer: error => 'Ce mélange est utilisé par un ou plusieurs autres mélanges'
messageProducer: _ => 'Ce mélange est utilisé par un ou plusieurs autres mélanges'
}, {
filter: error => error.type === 'invalid-mixmaterial-first',
messageProducer: error => 'La quantité du premier ingrédient du mélange ne peut pas être exprimée en pourcentage'
messageProducer: _ => 'La quantité du premier ingrédient du mélange ne peut pas être exprimée en pourcentage'
}]
controls: any
materialTypeEntries$ = this.materialTypeService.all.pipe(
map(materialTypes => materialTypes.map(materialType => new CreInputEntry(materialType.id, materialType.name))),
)
private _mixMaterials: MixMaterialDto[] = []
constructor(
private mixService: MixService,
private recipeService: RecipeService,
@ -87,22 +92,42 @@ export class MixEditorComponent extends ErrorHandlingComponent {
this.editionMode = true
}
this.subscribeEntityById(
this.recipeService,
this.recipeId,
r => {
this.recipe = r
this.fetchRecipe()
this.fetchMaterials()
}
private fetchRecipe() {
this.subscribe(
this.recipeService.getById(this.recipeId),
recipe => {
this.recipe = recipe
if (this.editionMode) {
this.mix = this.recipe.mixes.find(m => m.id === this.mixId)
this.mix = this.recipe.mixes.find(mix => mix.id === this.mixId)
this.mixMaterials = mixMaterialsAsMixMaterialsDto(this.mix)
} else {
this.addBlankMixMaterial()
}
this.generateForm()
this.createControls()
}
)
}
private fetchMaterials() {
this.subscribe(
this.materialService.all,
materials => this.materials = materials
)
}
private createControls() {
this.controls = {
name: new FormControl(this.mix?.mixType.name, Validators.required),
materialType: new FormControl(this.mix?.mixType.material.materialType.id, Validators.required)
}
}
addRow() {
this.addBlankMixMaterial()
this.mixTable.renderRows()
@ -141,9 +166,9 @@ export class MixEditorComponent extends ErrorHandlingComponent {
submit() {
this.save.emit({
name: this.nameControl.value,
name: this.controls.name.value,
recipeId: this.recipeId,
materialTypeId: this.materialTypeControl.value,
materialTypeId: this.controls.materialType.value,
mixMaterials: this.mixMaterials,
units: this.units
})
@ -154,8 +179,21 @@ export class MixEditorComponent extends ErrorHandlingComponent {
this.subscribeAndNavigate(this.mixService.delete(this.mixId), `/color/edit/${this.recipeId}`)
}
getAvailableMaterials(mixMaterial: MixMaterialDto): Material[] {
return this.materials.filter(m => mixMaterial.materialId === m.id || this.mixMaterials.filter(mm => mm.materialId === m.id).length === 0)
getAvailableMaterials(mixMaterial: MixMaterialDto): CreInputEntry[] {
// return this.materialService.all.pipe(
// map(materials => materials.filter(material => {
// return mixMaterial.materialId === material.id || this.mixMaterials.filter(mm => mm.materialId === material.id).length === 0
// })),
// map(materials => this.sortedMaterials(materials)),
// map(materials => materials.map(material => new CreInputEntry(material.id, material.name)))
// )
console.log(this.materials.map(material => new CreInputEntry(material.id, material.name)))
return []
// return this.materials.map(material => new CreInputEntry(material.id, material.name))
// return this.materials
// .filter(m => mixMaterial.materialId === m.id || this.mixMaterials.filter(mm => mm.materialId === m.id).length === 0)
// .sort(materialComparator)
// .map(material => new CreInputEntry(material.id, material.name))
}
materialDisplayName(material: Material): string {
@ -165,43 +203,18 @@ export class MixEditorComponent extends ErrorHandlingComponent {
return material.name
}
sortedMaterials(materials: Material[]): Material[] {
return materials.sort((a, b) => {
const aPrefixName = a.materialType.prefix.toLowerCase()
const bPrefixName = b.materialType.prefix.toLowerCase()
if (aPrefixName < bPrefixName) {
return -1
} else if (aPrefixName > bPrefixName) {
return 1
} else {
const aName = a.name.toLowerCase()
const bName = b.name.toLowerCase()
if (aName < bName) {
return -1
} else if (aName > bName) {
return 1
} else {
return 0
}
}
})
get mixMaterials(): MixMaterialDto[] {
return this._mixMaterials
}
private generateForm() {
this.nameControl = new FormControl(this.mix ? this.mix.mixType.name : null, Validators.required)
this.materialTypeControl = new FormControl(this.mix ? this.mix.mixType.material.materialType.id : null, Validators.required)
this.form = this.formBuilder.group({
name: this.nameControl,
materialType: this.materialTypeControl
})
set mixMaterials(mixMaterials: MixMaterialDto[]) {
this._mixMaterials = mixMaterials
this.mixTable.renderRows()
}
private addBlankMixMaterial() {
this.mixMaterials.push(
new MixMaterialDto(null, 0, false, this.mixMaterials.length + 1)
)
const mixMaterial = new MixMaterialDto(null, 0, false, this.mixMaterials.length + 1)
this.mixMaterials = [...this.mixMaterials, mixMaterial]
}
private updateMixMaterialPosition(mixMaterial: MixMaterialDto, updatedPosition: number) {

View File

@ -1,34 +1,34 @@
import {Component} from '@angular/core'
import {RecipeService} from '../../services/recipe.service'
import {RecipeService} from './services/recipe.service'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {ErrorHandlingComponent} from '../shared/components/subscribing.component'
import {
MixMaterialDto,
Recipe,
recipeMixCount,
recipeNoteForGroupId,
recipeStepCount
} from '../../../shared/model/recipe.model'
} from '../shared/model/recipe.model'
import {Observable, Subject} from 'rxjs'
import {ErrorHandler, ErrorService} from '../../../shared/service/error.service'
import {AlertService} from '../../../shared/service/alert.service'
import {GlobalAlertHandlerComponent} from '../../../shared/components/global-alert-handler/global-alert-handler.component'
import {InventoryService} from '../../../material/service/inventory.service'
import {ConfirmBoxComponent} from '../../../shared/components/confirm-box/confirm-box.component'
import {GroupService} from '../../../groups/services/group.service'
import {AppState} from '../../../shared/app-state'
import {AccountService} from '../../../accounts/services/account.service'
import {Permission} from '../../../shared/model/user'
import {ErrorHandler, ErrorService} from '../shared/service/error.service'
import {AlertService} from '../shared/service/alert.service'
import {GlobalAlertHandlerComponent} from '../shared/components/global-alert-handler/global-alert-handler.component'
import {InventoryService} from '../material/service/inventory.service'
import {ConfirmBoxComponent} from '../shared/components/confirm-box/confirm-box.component'
import {GroupService} from '../groups/services/group.service'
import {AppState} from '../shared/app-state'
import {AccountService} from '../accounts/services/account.service'
import {Permission} from '../shared/model/user'
import {FormControl} from '@angular/forms';
import {map, tap} from 'rxjs/operators';
import {CreInputEntry} from '../../../shared/components/inputs/inputs';
import {map} from 'rxjs/operators';
import {CreInputEntry} from '../shared/components/inputs/inputs';
@Component({
selector: 'cre-explore',
templateUrl: './explore.component.html',
styleUrls: ['./explore.component.sass']
selector: 'cre-recipe-explore',
templateUrl: './explore.html',
styleUrls: ['./recipes.sass']
})
export class ExploreComponent extends ErrorHandlingComponent {
export class CreRecipeExplore extends ErrorHandlingComponent {
deductErrorBody = {}
units$ = new Subject<string>()
selectedGroupId: number | null

View File

@ -5,7 +5,7 @@
</cre-warning-alert>
</div>
<cre-form *ngIf="hasCompanies" #form [formControls]="controls" class="mx-auto">
<cre-form *ngIf="hasCompanies" [formControls]="controls" class="mx-auto">
<cre-form-title *ngIf="!recipe">Ajouter une couleur</cre-form-title>
<cre-form-title *ngIf="recipe">Modifier la couleur {{recipe.name}}</cre-form-title>
<cre-form-content>
@ -18,8 +18,4 @@
<cre-input [control]="controls.remark" label="Remarque" icon="text"></cre-input>
<cre-combo-box [control]="controls.company" label="Bannière" [entries]="companyEntries$"></cre-combo-box>
</cre-form-content>
<cre-form-actions *ngIf="!recipe">
<cre-primary-button routerLink="/color/list">Retour</cre-primary-button>
<cre-accent-button [disabled]="form.invalid" (click)="submit()">Enregistrer</cre-accent-button>
</cre-form-actions>
</cre-form>

View File

@ -1,31 +1,19 @@
<div class="action-bar">
<mat-form-field>
<mat-label>Recherche</mat-label>
<input
matInput
type="text"
[(ngModel)]="searchQuery"
(keyup)="searchRecipes()"/>
<button
mat-button
*ngIf="searchQuery"
matSuffix
mat-icon-button
(click)="searchQuery=''">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<div class="button-add">
<button *ngIf="hasEditPermission" mat-raised-button color="accent" routerLink="/color/add">Ajouter</button>
</div>
</div>
<cre-action-bar>
<cre-action-group>
<cre-input label="Recherche" [control]="searchControl"></cre-input>
</cre-action-group>
<cre-action-group>
<cre-accent-button *ngIf="hasEditPermission" routerLink="/color/add">Ajouter</cre-accent-button>
</cre-action-group>
</cre-action-bar>
<div *ngIf="!loading">
<cre-warning-alert *ngIf="companies.length === 0">
<p>Il n'y a actuellement aucune bannière enregistrée dans le système.</p>
<p *ngIf="hasCompanyEditPermission">Vous pouvez en créer une <b><a routerLink="/catalog/company/add">ici</a></b>.</p>
<p *ngIf="hasCompanyEditPermission">Vous pouvez en créer une <b><a routerLink="/catalog/company/add">ici</a></b>.
</p>
</cre-warning-alert>
<cre-warning-alert *ngIf="recipes.size === 0">
<cre-warning-alert *ngIf="companies.length > 0 && recipes.size === 0">
<p>Il n'y a actuellement aucune recette enregistrée dans le système.</p>
<p *ngIf="hasEditPermission">Vous pouvez en créer une <b><a routerLink="/color/add">ici</a></b>.</p>
</cre-warning-alert>

View File

@ -1,23 +1,24 @@
import {ChangeDetectorRef, Component} from '@angular/core'
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
import {RecipeService} from '../../services/recipe.service'
import {Permission} from '../../../shared/model/user'
import {AccountService} from '../../../accounts/services/account.service'
import {getRecipeLuma, Recipe} from '../../../shared/model/recipe.model'
import {ActivatedRoute, Router} from '@angular/router'
import {ErrorService} from '../../../shared/service/error.service'
import {AppState} from '../../../shared/app-state'
import {ConfigService} from '../../../shared/service/config.service'
import {Company} from '../../../shared/model/company.model';
import {CompanyService} from '../../../company/service/company.service';
import {Config} from '../../../shared/model/config.model';
import {ChangeDetectorRef, Component} from '@angular/core';
import {ErrorHandlingComponent} from '../shared/components/subscribing.component';
import {Company} from '../shared/model/company.model';
import {getRecipeLuma, Recipe} from '../shared/model/recipe.model';
import {CompanyService} from '../company/service/company.service';
import {RecipeService} from './services/recipe.service';
import {AccountService} from '../accounts/services/account.service';
import {ConfigService} from '../shared/service/config.service';
import {AppState} from '../shared/app-state';
import {ErrorService} from '../shared/service/error.service';
import {ActivatedRoute, Router} from '@angular/router';
import {Config} from '../shared/model/config.model';
import {Permission} from '../shared/model/user';
import {FormControl} from '@angular/forms';
@Component({
selector: 'cre-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.sass']
selector: 'cre-recipe-list',
templateUrl: 'list.html',
styleUrls: ['recipes.sass']
})
export class ListComponent extends ErrorHandlingComponent {
export class RecipeList extends ErrorHandlingComponent {
companies: Company[] = []
recipes: Map<number, Recipe[]> = new Map<number, Recipe[]>()
tableCols = ['name', 'description', 'color', 'sample', 'iconNotApproved', 'buttonView', 'buttonEdit']
@ -25,6 +26,8 @@ export class ListComponent extends ErrorHandlingComponent {
panelForcedExpanded = false
hiddenRecipes = []
searchControl: FormControl
constructor(
private companyService: CompanyService,
private recipeService: RecipeService,
@ -47,7 +50,7 @@ export class ListComponent extends ErrorHandlingComponent {
this.subscribe(
this.configService.get(Config.EMERGENCY_MODE),
config => {
if (config.content == "true") {
if (config.content == 'true') {
this.urlUtils.navigateTo('/admin/config/')
}
}
@ -55,6 +58,12 @@ export class ListComponent extends ErrorHandlingComponent {
this.fetchCompanies()
this.fetchRecipes()
this.searchControl = new FormControl('')
this.subscribe(
this.searchControl.valueChanges,
value => this.searchRecipes(value)
)
}
private fetchCompanies() {
@ -72,7 +81,8 @@ export class ListComponent extends ErrorHandlingComponent {
)
}
searchRecipes() {
searchRecipes(searchQuery) {
this.searchQuery = searchQuery
if (this.searchQuery.length > 0 && !this.panelForcedExpanded) {
this.panelForcedExpanded = true
this.cdRef.detectChanges()

View File

@ -1,2 +0,0 @@
.recipe-content > div
margin: 0 3rem 3rem

View File

@ -1,9 +0,0 @@
mat-expansion-panel
width: 60rem
margin: 20px auto
.button-add
margin-top: .8rem
.recipe-color-circle
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12)

View File

@ -1,14 +1,14 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {ListComponent} from './pages/list/list.component';
import {ExploreComponent} from './pages/explore/explore.component';
import {CreRecipeExplore} from './explore';
import {MixEditComponent} from './pages/mix/mix-edit/mix-edit.component';
import {MixAddComponent} from './pages/mix/mix-add/mix-add.component';
import {RecipeAdd, RecipeEdit} from './recipes';
import {RecipeList} from './list';
const routes: Routes = [{
path: 'list',
component: ListComponent
component: RecipeList
}, {
path: 'add',
component: RecipeAdd
@ -23,7 +23,7 @@ const routes: Routes = [{
component: MixEditComponent
}, {
path: 'explore/:id',
component: ExploreComponent
component: CreRecipeExplore
}, {
path: '',
pathMatch: 'full',

View File

@ -2,10 +2,9 @@ import {NgModule} from '@angular/core'
import {RecipesRoutingModule} from './recipes-routing.module'
import {SharedModule} from '../shared/shared.module'
import {ListComponent} from './pages/list/list.component'
import {MatExpansionModule} from '@angular/material/expansion'
import {FormsModule} from '@angular/forms'
import {ExploreComponent} from './pages/explore/explore.component'
import {CreRecipeExplore} from './explore'
import {RecipeInfoComponent} from './components/recipe-info/recipe-info.component'
import {MixTableComponent} from './components/mix-table/mix-table.component'
import {StepListComponent} from './components/step-list/step-list.component'
@ -21,12 +20,11 @@ import {CreInputsModule} from '../shared/components/inputs/inputs.module';
import {CreButtonsModule} from '../shared/components/buttons/buttons.module';
import {RecipeAdd, RecipeEdit, RecipeForm} from './recipes';
import {CreActionBarModule} from '../shared/components/action-bar/action-bar.module';
import {RecipeList} from './list';
@NgModule({
declarations: [
ListComponent,
ExploreComponent,
CreRecipeExplore,
RecipeInfoComponent,
MixTableComponent,
StepListComponent,
@ -39,7 +37,8 @@ import {CreActionBarModule} from '../shared/components/action-bar/action-bar.mod
MixesCardComponent,
RecipeForm,
RecipeAdd,
RecipeEdit
RecipeEdit,
RecipeList
],
exports: [
UnitSelectorComponent

View File

@ -4,3 +4,15 @@
cre-form
margin-top: 0 !important
mat-expansion-panel
width: 60rem
margin: 20px auto
.button-add
margin-top: .8rem
.recipe-color-circle
box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12)
.recipe-content > div
margin: 0 3rem 3rem

View File

@ -16,6 +16,7 @@ import {AlertService} from '../shared/service/alert.service';
import {GroupService} from '../groups/services/group.service';
import {StepTableComponent} from './components/step-table/step-table.component';
import {anyMap} from '../shared/utils/map.utils';
import {CreForm, ICreForm} from '../shared/components/forms/forms';
@Component({
selector: 'recipe-form',
@ -26,6 +27,8 @@ import {anyMap} from '../shared/utils/map.utils';
export class RecipeForm extends SubscribingComponent {
@Input() recipe: Recipe | null
@ViewChild(CreForm) creForm: ICreForm
@Output() submitForm = new EventEmitter<Recipe>();
controls: any

View File

@ -1,3 +1,3 @@
<div class="d-flex flex-row justify-content-between px-5 py-4">
<div class="d-flex flex-row justify-content-between px-5 py-4" [class.flex-row-reverse]="reverse">
<ng-content select="cre-action-group"></ng-content>
</div>

View File

@ -1,4 +1,4 @@
import {Component} from '@angular/core'
import {Component, Input} from '@angular/core'
@Component({
selector: 'cre-action-group',
@ -11,4 +11,6 @@ export class CreActionGroup {}
selector: 'cre-action-bar',
templateUrl: 'action-bar.html'
})
export class CreActionBar {}
export class CreActionBar {
@Input() reverse = false
}

View File

@ -0,0 +1,17 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {ICreForm} from './forms';
@Component({
selector: 'cre-submit-button',
templateUrl: 'submit-button.html'
})
export class CreSubmitButton {
@Input() form: ICreForm
@Input() valid: boolean | null
@Output() submit = new EventEmitter<void>()
get disableButton(): boolean {
return !this.form || !(this.valid ?? this.form.valid)
}
}

View File

@ -1,28 +1,34 @@
import {NgModule} from '@angular/core'
import {CreFormActions, CreFormComponent, CreFormContent, CreFormTitle} from './forms'
import {CreFormActions, CreForm, CreFormContent, CreFormTitle} from './forms'
import {MatCardModule} from '@angular/material/card'
import {CommonModule} from '@angular/common'
import {MatButtonModule} from '@angular/material/button'
import {ReactiveFormsModule} from '@angular/forms'
import {CreSubmitButton} from './buttons';
import {CreButtonsModule} from '../buttons/buttons.module';
@NgModule({
declarations: [
CreFormComponent,
CreForm,
CreFormTitle,
CreFormContent,
CreFormActions
CreFormActions,
CreSubmitButton
],
exports: [
CreFormComponent,
CreForm,
CreFormTitle,
CreFormContent,
CreFormActions
CreFormActions,
CreSubmitButton
],
imports: [
MatCardModule,
CommonModule,
MatButtonModule,
ReactiveFormsModule
]
imports: [
MatCardModule,
CommonModule,
MatButtonModule,
ReactiveFormsModule,
CreButtonsModule
]
})
export class CreFormsModule {}
export class CreFormsModule {
}

View File

@ -1,6 +1,12 @@
import {Component, ContentChild, Directive, Input, OnInit, ViewEncapsulation} from '@angular/core'
import {FormBuilder, FormGroup} from '@angular/forms'
export interface ICreForm {
form: FormGroup
valid: boolean
invalid: boolean
}
@Directive({
selector: 'cre-form-title'
})
@ -17,7 +23,6 @@ export class CreFormContent {
selector: 'cre-form-actions'
})
export class CreFormActions {
}
@Component({
@ -26,7 +31,7 @@ export class CreFormActions {
styleUrls: ['forms.sass'],
encapsulation: ViewEncapsulation.None
})
export class CreFormComponent implements OnInit {
export class CreForm implements ICreForm, OnInit {
@ContentChild(CreFormActions) formActions: CreFormActions
@Input() formControls: { [key: string]: any }
@ -45,6 +50,10 @@ export class CreFormComponent implements OnInit {
return !!this.formActions
}
get valid(): boolean {
return this.form && this.form.valid
}
get invalid(): boolean {
return !this.form || this.form.invalid
}

View File

@ -0,0 +1 @@
<cre-accent-button [disabled]="disableButton" (click)="submit.emit()">Enregistrer</cre-accent-button>

View File

@ -5,7 +5,7 @@
<input
#input
matInput
[required]="isFieldRequired"
[required]="fieldRequired"
[disabled]="disabled"
[(ngModel)]="value"
(change)="valueChange.emit(value)"/>
@ -14,7 +14,7 @@
<input
#input
matInput
[required]="isFieldRequired"
[required]="fieldRequired"
[formControl]="control">
</ng-container>

View File

@ -15,7 +15,7 @@ import {
} from '@angular/core'
import {AbstractControl, FormControl, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'
import {COMMA, ENTER} from '@angular/cdk/keycodes'
import {Observable, Subject} from 'rxjs'
import {isObservable, Observable, Subject} from 'rxjs'
import {map, takeUntil} from 'rxjs/operators'
import {MatChipInputEvent} from '@angular/material/chips'
import {MatAutocomplete, MatAutocompleteSelectedEvent} from '@angular/material/autocomplete'
@ -50,16 +50,16 @@ export class CreInputComponent extends _CreInputBase implements AfterViewInit {
@ViewChild('input') input: any
@ContentChild(TemplateRef) errors: TemplateRef<any>
fieldRequired = true
ngAfterViewInit() {
const element = this.input.nativeElement
element.type = this.type
element.step = this.step.toString()
element.placeholder = this.placeholder
element.autocomplete = this.autocomplete ? 'on' : 'off'
}
get isFieldRequired(): boolean {
return this.control ? this.control.validator && this.control.validator({} as AbstractControl)?.required : this.required
this.fieldRequired = this.control ? this.control.validator && this.control.validator({} as AbstractControl)?.required : this.required
}
}
@ -400,7 +400,19 @@ export class CreSliderInputComponent {
templateUrl: 'select.html'
})
export class CreSelectComponent extends _CreInputBase {
@Input() entries: Observable<CreInputEntry[]>
@Input() entries: Observable<CreInputEntry[]> | CreInputEntry[]
get entriesAreObservable(): boolean {
return isObservable(this.entries)
}
get arrayEntries(): CreInputEntry[] {
return this.entries as CreInputEntry[]
}
get observableEntries(): Observable<CreInputEntry[]> {
return this.entries as Observable<CreInputEntry[]>
}
}
export class CreInputEntry {

View File

@ -1,8 +1,17 @@
<mat-form-field>
<mat-label>{{label}}</mat-label>
<mat-select [formControl]="control">
<mat-option *ngFor="let entry of entries | async" [value]="entry.key">
{{entry.display || entry.value}}
</mat-option>
<mat-select [attr.formControl]="control ? control : null">
<ng-container *ngIf="entriesAreObservable">
<ng-container
*ngFor="let entry of (observableEntries | async)">
<mat-option [value]="entry.key">{{entry.display || entry.value}}</mat-option>
</ng-container>
</ng-container>
<ng-container
*ngIf="!entriesAreObservable">
<mat-option *ngFor="let entry of arrayEntries" [value]="entry.key">
{{entry.display || entry.value}}
</mat-option>
</ng-container>
</mat-select>
</mat-form-field>

View File

@ -1,4 +1,4 @@
import {MaterialType} from "./materialtype.model";
import {MaterialType} from './materialtype.model';
import {openPdf} from '../utils/utils'
export class Material {
@ -15,3 +15,25 @@ export class Material {
export function openSimdut(material: Material) {
openPdf(material.simdutUrl)
}
export const materialComparator = (a: Material, b: Material): number => {
const aPrefixName = a.materialType.prefix.toLowerCase()
const bPrefixName = b.materialType.prefix.toLowerCase()
if (aPrefixName < bPrefixName) {
return -1
} else if (aPrefixName > bPrefixName) {
return 1
} else {
const aName = a.name.toLowerCase()
const bName = b.name.toLowerCase()
if (aName < bName) {
return -1
} else if (aName > bName) {
return 1
} else {
return 0
}
}
}

View File

@ -1,6 +1,6 @@
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'
import {chipListRequired, CreInputEntry, CreChipComboBoxComponent} from '../../shared/components/inputs/inputs'
import {CreFormComponent} from '../../shared/components/forms/forms'
import {CreForm} from '../../shared/components/forms/forms'
import {TouchUpKitProductEditor} from './product-editor'
import {FormControl, Validators} from '@angular/forms'
import {RecipeService} from '../../recipes/services/recipe.service'
@ -18,7 +18,7 @@ import {map} from 'rxjs/operators'
export class TouchUpKitForm extends SubscribingComponent {
@ViewChild('finishInput') finishInput: CreChipComboBoxComponent
@ViewChild('materialInput') materialInput: CreChipComboBoxComponent
@ViewChild(CreFormComponent) form: CreFormComponent
@ViewChild(CreForm) form: CreForm
@ViewChild(TouchUpKitProductEditor) contentEditor: TouchUpKitProductEditor
@Input() touchUpKit: TouchUpKit | null