#14 Update recipe explorer
This commit is contained in:
parent
521db72f5e
commit
a96062a91f
|
@ -4,7 +4,7 @@
|
|||
</mat-card-header>
|
||||
<mat-card-content [class.no-action]="!editionMode">
|
||||
<div class="d-flex flex-row justify-content-around flex-wrap">
|
||||
<p *ngIf="imagesUrls.length <= 0" class="light-text text-center">Aucune image n'est associée à cette couleur</p>
|
||||
<p *ngIf="imagesUrls.length <= 0" class="light-text text-center mb-0">Aucune image n'est associée à cette couleur</p>
|
||||
|
||||
<div *ngFor="let imageUrl of imagesUrls" class="d-flex flex-column align-self-center m-3">
|
||||
<div class="image-wrapper">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<mat-card-title>Étapes</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="no-action">
|
||||
<mat-list>
|
||||
<mat-list *ngIf="steps.length > 0">
|
||||
<mat-list-item *ngFor="let step of steps">
|
||||
{{step.position}}.<span class="space"></span>{{step.message}}
|
||||
</mat-list-item>
|
||||
|
|
|
@ -1,60 +1,26 @@
|
|||
<div *ngIf="recipe">
|
||||
<cre-recipe-info [recipe]="recipe" [hasModifications]="hasModifications"></cre-recipe-info>
|
||||
|
||||
<div class="action-bar backward d-flex flex-row">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="mt-1 pb-2">
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
routerLink="/color/list">
|
||||
Retour
|
||||
</button>
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
disabled
|
||||
title="WIP">
|
||||
Version Excel
|
||||
</button>
|
||||
<button
|
||||
*ngIf="canEditRecipesPublicData"
|
||||
mat-raised-button
|
||||
color="accent"
|
||||
(click)="saveModifications()"
|
||||
[disabled]="!hasModifications">
|
||||
Enregistrer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<cre-action-bar>
|
||||
<cre-action-group>
|
||||
<cre-action-group>
|
||||
<cre-primary-button routerLink="/color/list">Retour</cre-primary-button>
|
||||
<cre-unit-selector (unitChange)="changeUnits($event)"></cre-unit-selector>
|
||||
<mat-form-field class="ml-3">
|
||||
<mat-label>Groupe</mat-label>
|
||||
<mat-select [(ngModel)]="selectedGroupId">
|
||||
<mat-option *ngFor="let group of (groups$ | async)" [value]="group.id">
|
||||
{{group.name}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<cre-select [control]="groupControl" label="Group" [entries]="groupEntries$"></cre-select>
|
||||
</cre-action-group>
|
||||
<cre-action-group>
|
||||
<cre-textarea [control]="noteControl" [cols]="50" [rows]="canEditRecipesPublicData ? 2 : 1"></cre-textarea>
|
||||
</cre-action-group>
|
||||
</cre-action-group>
|
||||
<cre-action-group>
|
||||
<cre-primary-button disabled title="WIP">Version Excel</cre-primary-button>
|
||||
<cre-accent-button *ngIf="canEditRecipesPublicData" [disabled]="!hasModifications" (click)="saveModifications()">
|
||||
Enregistrer
|
||||
</cre-accent-button>
|
||||
</cre-action-group>
|
||||
</cre-action-bar>
|
||||
|
||||
<div class="flex-grow-1"></div>
|
||||
|
||||
<mat-form-field *ngIf="canEditRecipesPublicData" class="w-auto">
|
||||
<mat-label>Note</mat-label>
|
||||
<textarea
|
||||
matInput
|
||||
cols="40" rows="3"
|
||||
[(ngModel)]="selectedGroupNote"
|
||||
(keyup)="hasModifications = true">
|
||||
</textarea>
|
||||
</mat-form-field>
|
||||
<p *ngIf="!canEditRecipesPublicData">{{selectedGroupNote}}</p>
|
||||
</div>
|
||||
|
||||
<div class="recipe-content d-flex flex-row justify-content-around align-items-start flex-wrap mt-5">
|
||||
<div class="recipe-content d-flex flex-row justify-content-around align-items-start flex-wrap">
|
||||
<!-- Mixes -->
|
||||
<div *ngIf="recipe.mixes.length > 0">
|
||||
<cre-mixes-card
|
||||
|
|
|
@ -2,7 +2,13 @@ import {Component} from '@angular/core'
|
|||
import {RecipeService} from '../../services/recipe.service'
|
||||
import {ActivatedRoute, Router} from '@angular/router'
|
||||
import {ErrorHandlingComponent} from '../../../shared/components/subscribing.component'
|
||||
import {MixMaterialDto, Recipe, recipeMixCount, recipeNoteForGroupId, recipeStepCount} from '../../../shared/model/recipe.model'
|
||||
import {
|
||||
MixMaterialDto,
|
||||
Recipe,
|
||||
recipeMixCount,
|
||||
recipeNoteForGroupId,
|
||||
recipeStepCount
|
||||
} 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'
|
||||
|
@ -13,6 +19,9 @@ 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';
|
||||
|
||||
@Component({
|
||||
selector: 'cre-explore',
|
||||
|
@ -20,8 +29,6 @@ import {Permission} from '../../../shared/model/user'
|
|||
styleUrls: ['./explore.component.sass']
|
||||
})
|
||||
export class ExploreComponent extends ErrorHandlingComponent {
|
||||
recipe: Recipe | null
|
||||
groups$ = this.groupService.all
|
||||
deductErrorBody = {}
|
||||
units$ = new Subject<string>()
|
||||
selectedGroupId: number | null
|
||||
|
@ -33,6 +40,12 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
|
||||
deductedMixId: number | null
|
||||
|
||||
groupControl: FormControl
|
||||
noteControl: FormControl
|
||||
groupEntries$ = this.groupService.all.pipe(map(groups => {
|
||||
return groups.map(group => new CreInputEntry(group.id, group.name))
|
||||
}))
|
||||
|
||||
errorHandlers: ErrorHandler[] = [{
|
||||
filter: error => error.type === 'notfound-recipe-id',
|
||||
consumer: error => this.urlUtils.navigateTo('/color/list')
|
||||
|
@ -42,6 +55,9 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
messageProducer: () => 'Certains produit ne sont pas en quantité suffisante dans l\'inventaire'
|
||||
}]
|
||||
|
||||
private _recipe: Recipe | null
|
||||
private _notePlaceholder = !this.canEditRecipesPublicData ? 'N/A' : ''
|
||||
|
||||
constructor(
|
||||
private recipeService: RecipeService,
|
||||
private inventoryService: InventoryService,
|
||||
|
@ -62,18 +78,30 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
|
||||
this.selectedGroupId = this.loggedInUserGroupId
|
||||
|
||||
const id = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
|
||||
this.fetchRecipe()
|
||||
|
||||
this.groupControl = new FormControl(this.selectedGroupId)
|
||||
this.subscribe(
|
||||
this.groupControl.valueChanges,
|
||||
groupId => {
|
||||
this.selectedGroupId = groupId
|
||||
this.noteControl.setValue(this.selectedGroupNote, {emitEvent: false})
|
||||
}
|
||||
)
|
||||
|
||||
this.noteControl = new FormControl({value: this._notePlaceholder, disabled: !this.canEditRecipesPublicData})
|
||||
this.subscribe(
|
||||
this.noteControl.valueChanges,
|
||||
_ => this.hasModifications = true
|
||||
)
|
||||
}
|
||||
|
||||
fetchRecipe() {
|
||||
const recipeId = parseInt(this.activatedRoute.snapshot.paramMap.get('id'))
|
||||
this.subscribeEntityById(
|
||||
this.recipeService,
|
||||
id,
|
||||
r => {
|
||||
this.recipe = r
|
||||
this.appState.title = r.name
|
||||
|
||||
if (recipeMixCount(this.recipe) <= 0 || recipeStepCount(this.recipe) <= 0) {
|
||||
this.alertService.pushWarning('Cette recette n\'est pas complète')
|
||||
}
|
||||
}
|
||||
recipeId,
|
||||
recipe => this.recipe = recipe
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -128,11 +156,24 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
subscribeDeductMix(observable: Observable<any>) {
|
||||
this.subscribe(
|
||||
observable,
|
||||
() => this.alertService.pushSuccess('Les quantités quantités ont été déduites de l\'inventaire'),
|
||||
() => this.alertService.pushSuccess('Les quantités ont été déduites de l\'inventaire'),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
get recipe(): Recipe {
|
||||
return this._recipe
|
||||
}
|
||||
|
||||
set recipe(recipe: Recipe) {
|
||||
this._recipe = recipe
|
||||
this.appState.title = recipe.name
|
||||
|
||||
if (recipeMixCount(recipe) <= 0 || recipeStepCount(recipe) <= 0) {
|
||||
this.alertService.pushWarning('Cette recette n\'est pas complète')
|
||||
}
|
||||
}
|
||||
|
||||
get loggedInUserGroupId(): number {
|
||||
return this.appState.authenticatedUser.group?.id
|
||||
}
|
||||
|
@ -141,11 +182,7 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
if (!this.groupsNote.has(this.selectedGroupId)) {
|
||||
this.groupsNote.set(this.selectedGroupId, recipeNoteForGroupId(this.recipe, this.selectedGroupId))
|
||||
}
|
||||
return this.groupsNote.get(this.selectedGroupId)
|
||||
}
|
||||
|
||||
set selectedGroupNote(value: string) {
|
||||
this.groupsNote.set(this.selectedGroupId, value)
|
||||
return this.groupsNote.get(this.selectedGroupId) ?? this._notePlaceholder
|
||||
}
|
||||
|
||||
get canEditRecipesPublicData(): boolean {
|
||||
|
@ -160,7 +197,9 @@ export class ExploreComponent extends ErrorHandlingComponent {
|
|||
})
|
||||
|
||||
this.groupsNote.forEach((content, groupId) => {
|
||||
updatedNotes.set(groupId, content)
|
||||
if (content) {
|
||||
updatedNotes.set(groupId, content)
|
||||
}
|
||||
})
|
||||
|
||||
return updatedNotes
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {ErrorHandlingComponent, SubscribingComponent} from '../shared/components/subscribing.component';
|
||||
import {Observable, Subject} from 'rxjs';
|
||||
import {ComboBoxEntry} from '../shared/components/inputs/inputs';
|
||||
import {CreInputEntry} from '../shared/components/inputs/inputs';
|
||||
import {map, tap} from 'rxjs/operators';
|
||||
import {RecipeService} from './services/recipe.service';
|
||||
import {CompanyService} from '../company/service/company.service';
|
||||
|
@ -29,7 +29,7 @@ export class RecipeForm extends SubscribingComponent {
|
|||
@Output() submitForm = new EventEmitter<Recipe>();
|
||||
|
||||
controls: any
|
||||
companyEntries$: Observable<ComboBoxEntry[]>
|
||||
companyEntries$: Observable<CreInputEntry[]>
|
||||
hasCompanies = true
|
||||
|
||||
constructor(
|
||||
|
@ -62,7 +62,7 @@ export class RecipeForm extends SubscribingComponent {
|
|||
private fetchCompanies() {
|
||||
this.companyEntries$ = this.companyService.all.pipe(
|
||||
tap(companies => this.hasCompanies = companies.length > 0),
|
||||
map(companies => companies.map(c => new ComboBoxEntry(c.id, c.name))),
|
||||
map(companies => companies.map(c => new CreInputEntry(c.id, c.name))),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import {ApiService} from '../../shared/service/api.service'
|
|||
import {Observable} from 'rxjs'
|
||||
import {Recipe, RecipeStep} from '../../shared/model/recipe.model'
|
||||
import {map} from 'rxjs/operators'
|
||||
import {Company} from '../../shared/model/company.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
<div>
|
||||
<ng-content></ng-content>
|
||||
<div class="d-flex flex-column">
|
||||
<div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
<ng-content select="cre-action-group"></ng-content>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
CreChipComboBoxComponent,
|
||||
CreChipInputComponent,
|
||||
CreComboBoxComponent, CreFileInputComponent,
|
||||
CreInputComponent, CrePeriodInputComponent, CreSliderInputComponent
|
||||
CreInputComponent, CrePeriodInputComponent, CreSelectComponent, CreSliderInputComponent, CreTextareaComponent
|
||||
} from './inputs'
|
||||
import {MatInputModule} from '@angular/material/input'
|
||||
import {MatIconModule} from '@angular/material/icon'
|
||||
|
@ -29,7 +29,9 @@ import {MatSliderModule} from '@angular/material/slider';
|
|||
CreFileInputComponent,
|
||||
CreCheckboxInputComponent,
|
||||
CrePeriodInputComponent,
|
||||
CreSliderInputComponent
|
||||
CreSliderInputComponent,
|
||||
CreTextareaComponent,
|
||||
CreSelectComponent
|
||||
],
|
||||
imports: [
|
||||
MatInputModule,
|
||||
|
@ -55,7 +57,9 @@ import {MatSliderModule} from '@angular/material/slider';
|
|||
CreFileInputComponent,
|
||||
CreCheckboxInputComponent,
|
||||
CrePeriodInputComponent,
|
||||
CreSliderInputComponent
|
||||
CreSliderInputComponent,
|
||||
CreTextareaComponent,
|
||||
CreSelectComponent
|
||||
]
|
||||
})
|
||||
export class CreInputsModule {
|
||||
|
|
|
@ -63,6 +63,19 @@ export class CreInputComponent extends _CreInputBase implements AfterViewInit {
|
|||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cre-textarea',
|
||||
templateUrl: 'textarea.html',
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class CreTextareaComponent {
|
||||
@Input() label: string
|
||||
@Input() control: FormControl
|
||||
@Input() cols = 40
|
||||
@Input() rows = 3
|
||||
@Input() placeholder: string | null
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cre-autocomplete-input',
|
||||
templateUrl: 'autocomplete.html',
|
||||
|
@ -140,7 +153,7 @@ export class CreComboBoxComponent implements OnInit {
|
|||
@Input() label: string
|
||||
@Input() icon: string
|
||||
@Input() required = true
|
||||
@Input() entries: Observable<ComboBoxEntry[]>
|
||||
@Input() entries: Observable<CreInputEntry[]>
|
||||
|
||||
@ContentChild(TemplateRef) errors: TemplateRef<any>
|
||||
|
||||
|
@ -148,7 +161,7 @@ export class CreComboBoxComponent implements OnInit {
|
|||
validValue = false
|
||||
|
||||
private _destroy$ = new Subject<boolean>();
|
||||
private _entries: ComboBoxEntry[]
|
||||
private _entries: CreInputEntry[]
|
||||
|
||||
ngOnInit() {
|
||||
this.entries.pipe(takeUntil(this._destroy$))
|
||||
|
@ -160,7 +173,7 @@ export class CreComboBoxComponent implements OnInit {
|
|||
this.internalControl.setValue(this.findEntryByKey(this.control.value)?.value)
|
||||
}
|
||||
|
||||
if (this.internalControl.disabled) {
|
||||
if (this.control.disabled) {
|
||||
this.internalControl.disable()
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +181,7 @@ export class CreComboBoxComponent implements OnInit {
|
|||
|
||||
this.internalControl = new FormControl({
|
||||
value: null,
|
||||
disabled: true
|
||||
disabled: false
|
||||
}, Validators.compose([this.control.validator, this.valueValidator()]))
|
||||
this.internalControl.valueChanges.pipe(takeUntil(this._destroy$))
|
||||
.subscribe({
|
||||
|
@ -182,7 +195,7 @@ export class CreComboBoxComponent implements OnInit {
|
|||
})
|
||||
}
|
||||
|
||||
private findEntryByKey(key: any): ComboBoxEntry | null {
|
||||
private findEntryByKey(key: any): CreInputEntry | null {
|
||||
const found = this._entries.filter(e => e.key === key)
|
||||
if (found.length <= 0) {
|
||||
return null
|
||||
|
@ -190,7 +203,7 @@ export class CreComboBoxComponent implements OnInit {
|
|||
return found[0]
|
||||
}
|
||||
|
||||
private findEntryByValue(value: any): ComboBoxEntry | null {
|
||||
private findEntryByValue(value: any): CreInputEntry | null {
|
||||
const found = this._entries.filter(e => e.value === value)
|
||||
if (found.length <= 0) {
|
||||
return null
|
||||
|
@ -216,15 +229,15 @@ export class CreComboBoxComponent implements OnInit {
|
|||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class CreChipComboBoxComponent extends CreChipInputComponent implements OnDestroy {
|
||||
@Input() entries: Observable<ComboBoxEntry[]>
|
||||
@Input() entries: Observable<CreInputEntry[]>
|
||||
|
||||
@ContentChild(TemplateRef) errors: TemplateRef<any>
|
||||
@ViewChild('chipInput') chipInput: ElementRef<HTMLInputElement>
|
||||
@ViewChild('auto') matAutocomplete: MatAutocomplete
|
||||
|
||||
filteredEntries: Observable<ComboBoxEntry[]>
|
||||
filteredEntries: Observable<CreInputEntry[]>
|
||||
|
||||
private _entries: ComboBoxEntry[]
|
||||
private _entries: CreInputEntry[]
|
||||
private _destroy$ = new Subject()
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -255,7 +268,7 @@ export class CreChipComboBoxComponent extends CreChipInputComponent implements O
|
|||
return this.selectedValues.length <= 0
|
||||
}
|
||||
|
||||
private _filter(query: string): ComboBoxEntry[] {
|
||||
private _filter(query: string): CreInputEntry[] {
|
||||
const filterValue = query.toString().toLowerCase()
|
||||
return this._entries.filter(e => e.value.toString().toLowerCase().indexOf(filterValue) === 0)
|
||||
}
|
||||
|
@ -382,7 +395,15 @@ export class CreSliderInputComponent {
|
|||
}
|
||||
}
|
||||
|
||||
export class ComboBoxEntry {
|
||||
@Component({
|
||||
selector: 'cre-select',
|
||||
templateUrl: 'select.html'
|
||||
})
|
||||
export class CreSelectComponent extends _CreInputBase {
|
||||
@Input() entries: Observable<CreInputEntry[]>
|
||||
}
|
||||
|
||||
export class CreInputEntry {
|
||||
constructor(
|
||||
public key: any,
|
||||
public value: any,
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<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>
|
||||
</mat-form-field>
|
|
@ -0,0 +1,9 @@
|
|||
<mat-form-field class="w-auto">
|
||||
<mat-label>{{label}}</mat-label>
|
||||
<textarea
|
||||
matInput
|
||||
[cols]="cols" [rows]="rows"
|
||||
[formControl]="control">
|
||||
{{placeholder}}
|
||||
</textarea>
|
||||
</mat-form-field>
|
|
@ -1,5 +1,5 @@
|
|||
import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core'
|
||||
import {chipListRequired, ComboBoxEntry, CreChipComboBoxComponent} from '../../shared/components/inputs/inputs'
|
||||
import {chipListRequired, CreInputEntry, CreChipComboBoxComponent} from '../../shared/components/inputs/inputs'
|
||||
import {CreFormComponent} from '../../shared/components/forms/forms'
|
||||
import {TouchUpKitProductEditor} from './product-editor'
|
||||
import {FormControl, Validators} from '@angular/forms'
|
||||
|
@ -25,7 +25,7 @@ export class TouchUpKitForm extends SubscribingComponent {
|
|||
|
||||
controls: any
|
||||
finish$ = this.recipeService.all.pipe(
|
||||
map(recipes => recipes.map(recipe => new ComboBoxEntry(recipe.id, recipe.name, `${recipe.name} - ${recipe.company.name}`)))
|
||||
map(recipes => recipes.map(recipe => new CreInputEntry(recipe.id, recipe.name, `${recipe.name} - ${recipe.company.name}`)))
|
||||
)
|
||||
companies$ = this.companyService.all.pipe(
|
||||
map(companies => companies.map(company => company.name))
|
||||
|
|
Loading…
Reference in New Issue