generated from bastian/boilerplate
guide
This commit is contained in:
@@ -1120,8 +1120,8 @@ export class FurnitureService {
|
|||||||
}
|
}
|
||||||
private optionValues(dto: CreateFurnitureOptionDto) {
|
private optionValues(dto: CreateFurnitureOptionDto) {
|
||||||
const text = (value?: string) => value?.trim() || null;
|
const text = (value?: string) => value?.trim() || null;
|
||||||
const decimal = (value?: number) =>
|
const decimal = (value?: number | null) =>
|
||||||
value === undefined ? null : value.toFixed(2);
|
value === undefined || value === null ? null : value.toFixed(2);
|
||||||
return {
|
return {
|
||||||
name: dto.name?.trim() || 'Unbenannter Möbelvorschlag',
|
name: dto.name?.trim() || 'Unbenannter Möbelvorschlag',
|
||||||
manufacturer: text(dto.manufacturer),
|
manufacturer: text(dto.manufacturer),
|
||||||
@@ -1130,13 +1130,13 @@ export class FurnitureService {
|
|||||||
retailer: text(dto.retailer),
|
retailer: text(dto.retailer),
|
||||||
productUrl: text(dto.productUrl),
|
productUrl: text(dto.productUrl),
|
||||||
articleNumber: text(dto.articleNumber),
|
articleNumber: text(dto.articleNumber),
|
||||||
unitPrice: dto.unitPrice.toFixed(2),
|
unitPrice: (dto.unitPrice ?? 0).toFixed(2),
|
||||||
originalPrice: decimal(dto.originalPrice),
|
originalPrice: decimal(dto.originalPrice),
|
||||||
shippingCost: dto.shippingCost.toFixed(2),
|
shippingCost: (dto.shippingCost ?? 0).toFixed(2),
|
||||||
additionalCost: dto.additionalCost.toFixed(2),
|
additionalCost: (dto.additionalCost ?? 0).toFixed(2),
|
||||||
discount: dto.discount.toFixed(2),
|
discount: (dto.discount ?? 0).toFixed(2),
|
||||||
currency: dto.currency.toUpperCase(),
|
currency: dto.currency.toUpperCase(),
|
||||||
quantity: dto.quantity,
|
quantity: dto.quantity ?? 1,
|
||||||
width: decimal(dto.width),
|
width: decimal(dto.width),
|
||||||
height: decimal(dto.height),
|
height: decimal(dto.height),
|
||||||
depth: decimal(dto.depth),
|
depth: decimal(dto.depth),
|
||||||
@@ -1154,15 +1154,19 @@ export class FurnitureService {
|
|||||||
budgetCategoryId: dto.budgetCategoryId ?? null,
|
budgetCategoryId: dto.budgetCategoryId ?? null,
|
||||||
existingItem: dto.existingItem,
|
existingItem: dto.existingItem,
|
||||||
estimatedCurrentValue: decimal(dto.estimatedCurrentValue),
|
estimatedCurrentValue: decimal(dto.estimatedCurrentValue),
|
||||||
movingCost: dto.movingCost.toFixed(2),
|
movingCost: (dto.movingCost ?? 0).toFixed(2),
|
||||||
refurbishmentCost: dto.refurbishmentCost.toFixed(2),
|
refurbishmentCost: (dto.refurbishmentCost ?? 0).toFixed(2),
|
||||||
currentLocation: text(dto.currentLocation),
|
currentLocation: text(dto.currentLocation),
|
||||||
condition: dto.condition ?? null,
|
condition: dto.condition ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
private total(dto: CreateFurnitureOptionDto) {
|
private total(dto: CreateFurnitureOptionDto) {
|
||||||
try {
|
try {
|
||||||
return calculateFurnitureTotal(dto);
|
return calculateFurnitureTotal({
|
||||||
|
...dto,
|
||||||
|
unitPrice: dto.unitPrice ?? 0,
|
||||||
|
quantity: dto.quantity ?? 1,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
this.validation(
|
this.validation(
|
||||||
'Der Rabatt darf die berechneten Gesamtkosten nicht überschreiten.',
|
'Der Rabatt darf die berechneten Gesamtkosten nicht überschreiten.',
|
||||||
|
|||||||
@@ -11,10 +11,44 @@ import type {
|
|||||||
} from './hauspilot-api.service';
|
} from './hauspilot-api.service';
|
||||||
import { HauspilotApiService } from './hauspilot-api.service';
|
import { HauspilotApiService } from './hauspilot-api.service';
|
||||||
import { FurniturePlanningComponent } from './furniture-planning.component';
|
import { FurniturePlanningComponent } from './furniture-planning.component';
|
||||||
|
import { ToastService } from '../../shared/ui';
|
||||||
|
|
||||||
registerLocaleData(localeDe);
|
registerLocaleData(localeDe);
|
||||||
|
|
||||||
describe('FurniturePlanningComponent', () => {
|
describe('FurniturePlanningComponent', () => {
|
||||||
|
it('groups optional furniture details in a collapsed advanced section', async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [FurniturePlanningComponent],
|
||||||
|
providers: [provideHttpClient()],
|
||||||
|
}).compileComponents();
|
||||||
|
const fixture = TestBed.createComponent(FurniturePlanningComponent);
|
||||||
|
fixture.componentInstance.showOptionForm.set(true);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const host: unknown = fixture.nativeElement;
|
||||||
|
if (!(host instanceof HTMLElement)) throw new Error('Test-Hostelement fehlt.');
|
||||||
|
|
||||||
|
const details = host.querySelector('details.advanced-fields');
|
||||||
|
expect(details?.querySelector('summary')?.textContent).toContain('Erweiterte Informationen');
|
||||||
|
expect(details?.textContent).toContain('Versand');
|
||||||
|
expect(details?.textContent).toContain('Erwartete Lieferung');
|
||||||
|
expect(details?.hasAttribute('open')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows a toast naming a missing required furniture selection', async () => {
|
||||||
|
const show = vi.fn();
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [FurniturePlanningComponent],
|
||||||
|
providers: [provideHttpClient(), { provide: ToastService, useValue: { show } }],
|
||||||
|
}).compileComponents();
|
||||||
|
const component = TestBed.createComponent(FurniturePlanningComponent).componentInstance;
|
||||||
|
|
||||||
|
component.chooseOptionRequirement();
|
||||||
|
|
||||||
|
expect(show).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ message: 'Bitte einen Möbelbedarf auswählen.' }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('allows optional furniture and delivery fields to stay empty', async () => {
|
it('allows optional furniture and delivery fields to stay empty', async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [FurniturePlanningComponent],
|
imports: [FurniturePlanningComponent],
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import type {
|
|||||||
import { HauspilotApiService } from './hauspilot-api.service';
|
import { HauspilotApiService } from './hauspilot-api.service';
|
||||||
import { conflictMessage } from './project-workspace.helpers';
|
import { conflictMessage } from './project-workspace.helpers';
|
||||||
import { FurnitureGridComponent } from './furniture-grid.component';
|
import { FurnitureGridComponent } from './furniture-grid.component';
|
||||||
|
import { ToastService } from '../../shared/ui';
|
||||||
|
|
||||||
const categoryLabels: Record<string, string> = {
|
const categoryLabels: Record<string, string> = {
|
||||||
seating: 'Sitzmöbel',
|
seating: 'Sitzmöbel',
|
||||||
@@ -521,40 +522,13 @@ const statusLabels: Record<string, string> = {
|
|||||||
min="0"
|
min="0"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
formControlName="unitPrice" /></label
|
formControlName="unitPrice" /></label
|
||||||
><label>Menge<input type="number" min="1" formControlName="quantity" /></label
|
><label>Menge<input type="number" min="1" formControlName="quantity" /></label>
|
||||||
><label
|
|
||||||
>Versand<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
formControlName="shippingCost" /></label
|
|
||||||
><label
|
|
||||||
>Zusatzkosten<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
step="0.01"
|
|
||||||
formControlName="additionalCost" /></label
|
|
||||||
><label
|
|
||||||
>Rabatt<input type="number" min="0" step="0.01" formControlName="discount"
|
|
||||||
/></label>
|
|
||||||
<output class="calculated"
|
<output class="calculated"
|
||||||
>Gesamtpreis:
|
>Gesamtpreis:
|
||||||
<strong>{{
|
<strong>{{
|
||||||
calculatedTotal() | currency: 'EUR' : 'symbol' : '1.2-2' : 'de'
|
calculatedTotal() | currency: 'EUR' : 'symbol' : '1.2-2' : 'de'
|
||||||
}}</strong></output
|
}}</strong></output
|
||||||
>
|
>
|
||||||
<label>Breite (cm)<input type="number" min="0" formControlName="width" /></label
|
|
||||||
><label>Höhe (cm)<input type="number" min="0" formControlName="height" /></label
|
|
||||||
><label>Tiefe (cm)<input type="number" min="0" formControlName="depth" /></label
|
|
||||||
><label>Farbe<input formControlName="color" /></label
|
|
||||||
><label>Material<input formControlName="material" /></label
|
|
||||||
><label
|
|
||||||
>Lieferzeit (Tage)<input type="number" min="0" formControlName="deliveryDays" /></label
|
|
||||||
><label
|
|
||||||
>Erwartete Lieferung (optional)<input
|
|
||||||
type="date"
|
|
||||||
formControlName="expectedDeliveryDate"
|
|
||||||
/></label>
|
|
||||||
<label
|
<label
|
||||||
>Verfügbarkeit<select formControlName="availability">
|
>Verfügbarkeit<select formControlName="availability">
|
||||||
<option value="unknown">Unbekannt</option>
|
<option value="unknown">Unbekannt</option>
|
||||||
@@ -578,24 +552,60 @@ const statusLabels: Record<string, string> = {
|
|||||||
><label class="check"
|
><label class="check"
|
||||||
><input type="checkbox" formControlName="existingItem" /> Bereits vorhanden</label
|
><input type="checkbox" formControlName="existingItem" /> Bereits vorhanden</label
|
||||||
>
|
>
|
||||||
@if (optionForm.controls.existingItem.value) {
|
<details class="advanced-fields wide">
|
||||||
<label
|
<summary>Erweiterte Informationen</summary>
|
||||||
>Umzugskosten<input
|
<p class="ui-meta">Diese Angaben sind vollständig optional.</p>
|
||||||
type="number"
|
<div class="advanced-grid">
|
||||||
min="0"
|
<label
|
||||||
step="0.01"
|
>Versand<input
|
||||||
formControlName="movingCost" /></label
|
type="number"
|
||||||
><label
|
min="0"
|
||||||
>Aufbereitungskosten<input
|
step="0.01"
|
||||||
type="number"
|
formControlName="shippingCost" /></label
|
||||||
min="0"
|
><label
|
||||||
step="0.01"
|
>Zusatzkosten<input
|
||||||
formControlName="refurbishmentCost" /></label
|
type="number"
|
||||||
><label>Aktueller Standort<input formControlName="currentLocation" /></label>
|
min="0"
|
||||||
}
|
step="0.01"
|
||||||
<label class="wide"
|
formControlName="additionalCost" /></label
|
||||||
>Beschreibung<textarea rows="3" formControlName="description"></textarea></label
|
><label
|
||||||
><label class="wide">Notizen<textarea rows="3" formControlName="notes"></textarea></label>
|
>Rabatt<input type="number" min="0" step="0.01" formControlName="discount"
|
||||||
|
/></label>
|
||||||
|
<label>Breite (cm)<input type="number" min="0" formControlName="width" /></label
|
||||||
|
><label>Höhe (cm)<input type="number" min="0" formControlName="height" /></label
|
||||||
|
><label>Tiefe (cm)<input type="number" min="0" formControlName="depth" /></label
|
||||||
|
><label>Farbe<input formControlName="color" /></label
|
||||||
|
><label>Material<input formControlName="material" /></label
|
||||||
|
><label
|
||||||
|
>Lieferzeit (Tage)<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
formControlName="deliveryDays" /></label
|
||||||
|
><label
|
||||||
|
>Erwartete Lieferung<input type="date" formControlName="expectedDeliveryDate"
|
||||||
|
/></label>
|
||||||
|
@if (optionForm.controls.existingItem.value) {
|
||||||
|
<label
|
||||||
|
>Umzugskosten<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
formControlName="movingCost" /></label
|
||||||
|
><label
|
||||||
|
>Aufbereitungskosten<input
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
formControlName="refurbishmentCost" /></label
|
||||||
|
><label>Aktueller Standort<input formControlName="currentLocation" /></label>
|
||||||
|
}
|
||||||
|
<label class="wide"
|
||||||
|
>Beschreibung<textarea rows="3" formControlName="description"></textarea></label
|
||||||
|
><label class="wide"
|
||||||
|
>Notizen<textarea rows="3" formControlName="notes"></textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="ui-button" type="submit" [disabled]="saving()">Speichern</button
|
<button class="ui-button" type="submit" [disabled]="saving()">Speichern</button
|
||||||
><button class="ui-button ui-button--ghost" type="button" (click)="closeOptionForm()">
|
><button class="ui-button ui-button--ghost" type="button" (click)="closeOptionForm()">
|
||||||
@@ -1069,6 +1079,21 @@ const statusLabels: Record<string, string> = {
|
|||||||
.calculated {
|
.calculated {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
.advanced-fields {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--space-4);
|
||||||
|
}
|
||||||
|
.advanced-fields summary {
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
}
|
||||||
|
.advanced-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: var(--space-4);
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
}
|
||||||
.scenario-grid article {
|
.scenario-grid article {
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
@@ -1087,6 +1112,9 @@ const statusLabels: Record<string, string> = {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@media (max-width: 40rem) {
|
@media (max-width: 40rem) {
|
||||||
|
.advanced-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
.actions .ui-button {
|
.actions .ui-button {
|
||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
}
|
}
|
||||||
@@ -1115,6 +1143,7 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
@ViewChild('scenarioDialog') private scenarioDialog?: ElementRef<HTMLDialogElement>;
|
@ViewChild('scenarioDialog') private scenarioDialog?: ElementRef<HTMLDialogElement>;
|
||||||
@ViewChild('assignmentDialog') private assignmentDialog?: ElementRef<HTMLDialogElement>;
|
@ViewChild('assignmentDialog') private assignmentDialog?: ElementRef<HTMLDialogElement>;
|
||||||
private readonly api = inject(HauspilotApiService);
|
private readonly api = inject(HauspilotApiService);
|
||||||
|
private readonly toasts = inject(ToastService);
|
||||||
readonly requirements = signal<FurnitureRequirement[]>([]);
|
readonly requirements = signal<FurnitureRequirement[]>([]);
|
||||||
readonly scenarios = signal<FurnitureScenario[]>([]);
|
readonly scenarios = signal<FurnitureScenario[]>([]);
|
||||||
readonly summary = signal<FurnitureSummary | null>(null);
|
readonly summary = signal<FurnitureSummary | null>(null);
|
||||||
@@ -1185,24 +1214,19 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
retailer: new FormControl('', { nonNullable: true }),
|
retailer: new FormControl('', { nonNullable: true }),
|
||||||
productUrl: new FormControl('', { nonNullable: true }),
|
productUrl: new FormControl('', { nonNullable: true }),
|
||||||
articleNumber: new FormControl('', { nonNullable: true }),
|
articleNumber: new FormControl('', { nonNullable: true }),
|
||||||
unitPrice: new FormControl(0, {
|
unitPrice: new FormControl<number | null>(null, {
|
||||||
nonNullable: true,
|
|
||||||
validators: [(control) => Validators.min(0)(control)],
|
validators: [(control) => Validators.min(0)(control)],
|
||||||
}),
|
}),
|
||||||
quantity: new FormControl(1, {
|
quantity: new FormControl<number | null>(1, {
|
||||||
nonNullable: true,
|
|
||||||
validators: [(control) => Validators.min(1)(control)],
|
validators: [(control) => Validators.min(1)(control)],
|
||||||
}),
|
}),
|
||||||
shippingCost: new FormControl(0, {
|
shippingCost: new FormControl<number | null>(null, {
|
||||||
nonNullable: true,
|
|
||||||
validators: [(control) => Validators.min(0)(control)],
|
validators: [(control) => Validators.min(0)(control)],
|
||||||
}),
|
}),
|
||||||
additionalCost: new FormControl(0, {
|
additionalCost: new FormControl<number | null>(null, {
|
||||||
nonNullable: true,
|
|
||||||
validators: [(control) => Validators.min(0)(control)],
|
validators: [(control) => Validators.min(0)(control)],
|
||||||
}),
|
}),
|
||||||
discount: new FormControl(0, {
|
discount: new FormControl<number | null>(null, {
|
||||||
nonNullable: true,
|
|
||||||
validators: [(control) => Validators.min(0)(control)],
|
validators: [(control) => Validators.min(0)(control)],
|
||||||
}),
|
}),
|
||||||
width: new FormControl<number | null>(null),
|
width: new FormControl<number | null>(null),
|
||||||
@@ -1251,15 +1275,15 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
});
|
});
|
||||||
readonly calculatedTotal = computed(() => {
|
readonly calculatedTotal = computed(() => {
|
||||||
const v = this.optionForm.getRawValue();
|
const v = this.optionForm.getRawValue();
|
||||||
const acquisition = v.existingItem ? 0 : v.unitPrice * v.quantity;
|
const acquisition = v.existingItem ? 0 : (v.unitPrice ?? 0) * (v.quantity ?? 1);
|
||||||
return Math.max(
|
return Math.max(
|
||||||
0,
|
0,
|
||||||
acquisition +
|
acquisition +
|
||||||
v.shippingCost +
|
(v.shippingCost ?? 0) +
|
||||||
v.additionalCost +
|
(v.additionalCost ?? 0) +
|
||||||
v.movingCost +
|
v.movingCost +
|
||||||
v.refurbishmentCost -
|
v.refurbishmentCost -
|
||||||
v.discount,
|
(v.discount ?? 0),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
@@ -1342,7 +1366,11 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
if (!keepFlow) this.guidedPlanning.set(false);
|
if (!keepFlow) this.guidedPlanning.set(false);
|
||||||
}
|
}
|
||||||
saveRequirement() {
|
saveRequirement() {
|
||||||
if (this.requirementForm.invalid) return this.requirementForm.markAllAsTouched();
|
if (this.requirementForm.invalid) {
|
||||||
|
this.requirementForm.markAllAsTouched();
|
||||||
|
this.validationToast('Bitte eine gültige Menge und nichtnegative Zahlen eingeben.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.saving.set(true);
|
this.saving.set(true);
|
||||||
const existing = this.editingRequirement();
|
const existing = this.editingRequirement();
|
||||||
const value = this.requirementForm.getRawValue();
|
const value = this.requirementForm.getRawValue();
|
||||||
@@ -1381,7 +1409,11 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
if (!keepFlow) this.guidedPlanning.set(false);
|
if (!keepFlow) this.guidedPlanning.set(false);
|
||||||
}
|
}
|
||||||
chooseOptionRequirement() {
|
chooseOptionRequirement() {
|
||||||
if (this.optionTargetForm.invalid) return this.optionTargetForm.markAllAsTouched();
|
if (this.optionTargetForm.invalid) {
|
||||||
|
this.optionTargetForm.markAllAsTouched();
|
||||||
|
this.validationToast('Bitte einen Möbelbedarf auswählen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const requirement = this.requirements().find(
|
const requirement = this.requirements().find(
|
||||||
(entry) => entry.id === this.optionTargetForm.controls.requirementId.value,
|
(entry) => entry.id === this.optionTargetForm.controls.requirementId.value,
|
||||||
);
|
);
|
||||||
@@ -1400,11 +1432,11 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
retailer: '',
|
retailer: '',
|
||||||
productUrl: '',
|
productUrl: '',
|
||||||
articleNumber: '',
|
articleNumber: '',
|
||||||
unitPrice: 0,
|
unitPrice: null,
|
||||||
quantity: r.requiredQuantity,
|
quantity: r.requiredQuantity,
|
||||||
shippingCost: 0,
|
shippingCost: null,
|
||||||
additionalCost: 0,
|
additionalCost: null,
|
||||||
discount: 0,
|
discount: null,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
depth: null,
|
depth: null,
|
||||||
@@ -1467,12 +1499,25 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
saveOption() {
|
saveOption() {
|
||||||
const requirement = this.optionRequirement();
|
const requirement = this.optionRequirement();
|
||||||
if (!requirement || this.optionForm.invalid) return this.optionForm.markAllAsTouched();
|
if (!requirement) {
|
||||||
|
this.validationToast('Bitte zuerst einen Möbelbedarf auswählen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.optionForm.invalid) {
|
||||||
|
this.optionForm.markAllAsTouched();
|
||||||
|
this.validationToast('Bitte ungültige Preis-, Mengen- oder Maßangaben korrigieren.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.saving.set(true);
|
this.saving.set(true);
|
||||||
const value = this.optionForm.getRawValue();
|
const value = this.optionForm.getRawValue();
|
||||||
const body = {
|
const body = {
|
||||||
...value,
|
...value,
|
||||||
name: value.name.trim() || undefined,
|
name: value.name.trim() || undefined,
|
||||||
|
unitPrice: value.unitPrice ?? undefined,
|
||||||
|
quantity: value.quantity ?? undefined,
|
||||||
|
shippingCost: value.shippingCost ?? undefined,
|
||||||
|
additionalCost: value.additionalCost ?? undefined,
|
||||||
|
discount: value.discount ?? undefined,
|
||||||
expectedDeliveryDate: value.expectedDeliveryDate || undefined,
|
expectedDeliveryDate: value.expectedDeliveryDate || undefined,
|
||||||
currency: 'EUR',
|
currency: 'EUR',
|
||||||
};
|
};
|
||||||
@@ -1532,7 +1577,11 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
if (!keepFlow) this.guidedPlanning.set(false);
|
if (!keepFlow) this.guidedPlanning.set(false);
|
||||||
}
|
}
|
||||||
createScenario() {
|
createScenario() {
|
||||||
if (this.scenarioForm.invalid) return this.scenarioForm.markAllAsTouched();
|
if (this.scenarioForm.invalid) {
|
||||||
|
this.scenarioForm.markAllAsTouched();
|
||||||
|
this.validationToast('Bitte einen Namen für das Szenario eingeben.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.saving.set(true);
|
this.saving.set(true);
|
||||||
const value = this.scenarioForm.getRawValue();
|
const value = this.scenarioForm.getRawValue();
|
||||||
this.api
|
this.api
|
||||||
@@ -1631,7 +1680,16 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
this.openOptionPicker();
|
this.openOptionPicker();
|
||||||
}
|
}
|
||||||
saveAssignment() {
|
saveAssignment() {
|
||||||
if (this.assignmentForm.invalid) return this.assignmentForm.markAllAsTouched();
|
if (this.assignmentForm.invalid) {
|
||||||
|
this.assignmentForm.markAllAsTouched();
|
||||||
|
const message = !this.assignmentForm.controls.scenarioId.value
|
||||||
|
? 'Bitte ein Szenario auswählen.'
|
||||||
|
: !this.assignmentForm.controls.requirementId.value
|
||||||
|
? 'Bitte einen Möbelbedarf auswählen.'
|
||||||
|
: 'Bitte einen Möbelvorschlag auswählen.';
|
||||||
|
this.validationToast(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const value = this.assignmentForm.getRawValue();
|
const value = this.assignmentForm.getRawValue();
|
||||||
const scenario = this.scenarios().find((entry) => entry.id === value.scenarioId);
|
const scenario = this.scenarios().find((entry) => entry.id === value.scenarioId);
|
||||||
const requirement = this.requirements().find((entry) => entry.id === value.requirementId);
|
const requirement = this.requirements().find((entry) => entry.id === value.requirementId);
|
||||||
@@ -1688,6 +1746,13 @@ export class FurniturePlanningComponent implements OnChanges {
|
|||||||
: 'Die Möbelplanung konnte nicht gespeichert werden.';
|
: 'Die Möbelplanung konnte nicht gespeichert werden.';
|
||||||
this.error.set(conflictMessage(status, fallback));
|
this.error.set(conflictMessage(status, fallback));
|
||||||
}
|
}
|
||||||
|
private validationToast(message: string): void {
|
||||||
|
this.toasts.show({
|
||||||
|
tone: 'warning',
|
||||||
|
title: 'Angabe fehlt oder ist ungültig',
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
}
|
||||||
private openDialog(dialog?: ElementRef<HTMLDialogElement>) {
|
private openDialog(dialog?: ElementRef<HTMLDialogElement>) {
|
||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
if (dialog && !dialog.nativeElement.open) dialog.nativeElement.showModal();
|
if (dialog && !dialog.nativeElement.open) dialog.nativeElement.showModal();
|
||||||
|
|||||||
Reference in New Issue
Block a user