generated from bastian/boilerplate
szenarien
This commit is contained in:
@@ -281,27 +281,38 @@ export class FurnitureService {
|
|||||||
await this.access.require(projectId, userId, 'edit');
|
await this.access.require(projectId, userId, 'edit');
|
||||||
await this.owned(this.furniture.requirements, projectId, requirementId);
|
await this.owned(this.furniture.requirements, projectId, requirementId);
|
||||||
await this.validateOptionLinks(projectId, dto.budgetCategoryId);
|
await this.validateOptionLinks(projectId, dto.budgetCategoryId);
|
||||||
const totalPrice = this.total(dto);
|
return this.dataSource.transaction(async (manager) => {
|
||||||
const saved = await this.furniture.options.save(
|
const options = manager.getRepository(FurnitureOptionEntity);
|
||||||
this.furniture.options.create({
|
const saved = await options.save(
|
||||||
...this.optionValues(dto),
|
options.create({
|
||||||
|
...this.optionValues(dto),
|
||||||
|
projectId,
|
||||||
|
requirementId,
|
||||||
|
totalPrice: this.total(dto),
|
||||||
|
currentlySelected: false,
|
||||||
|
createdByUserId: userId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await manager
|
||||||
|
.getRepository(FurnitureRequirementEntity)
|
||||||
|
.update(
|
||||||
|
{ id: requirementId, projectId },
|
||||||
|
{ status: FurnitureRequirementStatus.HasOptions },
|
||||||
|
);
|
||||||
|
await this.refreshAutomaticScenarios(projectId, manager);
|
||||||
|
await this.activity(
|
||||||
projectId,
|
projectId,
|
||||||
requirementId,
|
userId,
|
||||||
totalPrice,
|
'furniture.option-created',
|
||||||
currentlySelected: false,
|
{
|
||||||
createdByUserId: userId,
|
requirementId,
|
||||||
}),
|
optionId: saved.id,
|
||||||
);
|
name: saved.name,
|
||||||
await this.furniture.requirements.update(
|
},
|
||||||
{ id: requirementId, projectId },
|
manager,
|
||||||
{ status: FurnitureRequirementStatus.HasOptions },
|
);
|
||||||
);
|
return saved;
|
||||||
await this.activity(projectId, userId, 'furniture.option-created', {
|
|
||||||
requirementId,
|
|
||||||
optionId: saved.id,
|
|
||||||
name: saved.name,
|
|
||||||
});
|
});
|
||||||
return saved;
|
|
||||||
}
|
}
|
||||||
async updateOption(
|
async updateOption(
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@@ -725,12 +736,27 @@ export class FurnitureService {
|
|||||||
}
|
}
|
||||||
async deleteScenario(projectId: string, id: string, userId: string) {
|
async deleteScenario(projectId: string, id: string, userId: string) {
|
||||||
await this.access.require(projectId, userId, 'edit');
|
await this.access.require(projectId, userId, 'edit');
|
||||||
const scenario = await this.owned(this.furniture.scenarios, projectId, id);
|
await this.owned(this.furniture.scenarios, projectId, id);
|
||||||
if (scenario.isDefault)
|
await this.dataSource.transaction(async (manager) => {
|
||||||
this.conflict(
|
const scenarios = manager.getRepository(FurnitureScenarioEntity);
|
||||||
'Das Standardszenario kann nicht archiviert werden. Legen Sie zuerst ein anderes Standardszenario fest.',
|
const scenario = await scenarios.findOneByOrFail({ id, projectId });
|
||||||
|
await scenarios.softDelete({ id, projectId });
|
||||||
|
if (scenario.isDefault) {
|
||||||
|
const replacement = await scenarios.findOne({
|
||||||
|
where: { projectId, deletedAt: IsNull() },
|
||||||
|
order: { createdAt: 'ASC' },
|
||||||
|
});
|
||||||
|
if (replacement)
|
||||||
|
await scenarios.update({ id: replacement.id }, { isDefault: true });
|
||||||
|
}
|
||||||
|
await this.activity(
|
||||||
|
projectId,
|
||||||
|
userId,
|
||||||
|
'furniture.scenario-deleted',
|
||||||
|
{ scenarioId: id },
|
||||||
|
manager,
|
||||||
);
|
);
|
||||||
await this.furniture.scenarios.softDelete({ id, projectId });
|
});
|
||||||
}
|
}
|
||||||
async updateSelections(
|
async updateSelections(
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@@ -1134,6 +1160,35 @@ export class FurnitureService {
|
|||||||
}
|
}
|
||||||
await selectionRepo.save(selections);
|
await selectionRepo.save(selections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async refreshAutomaticScenarios(
|
||||||
|
projectId: string,
|
||||||
|
manager: EntityManager,
|
||||||
|
) {
|
||||||
|
const scenarios = await manager
|
||||||
|
.getRepository(FurnitureScenarioEntity)
|
||||||
|
.find({
|
||||||
|
where: { projectId, deletedAt: IsNull() },
|
||||||
|
});
|
||||||
|
const selectionRepo = manager.getRepository(
|
||||||
|
FurnitureScenarioSelectionEntity,
|
||||||
|
);
|
||||||
|
for (const scenario of scenarios) {
|
||||||
|
if (
|
||||||
|
![
|
||||||
|
FurnitureScenarioType.Budget,
|
||||||
|
FurnitureScenarioType.Preferred,
|
||||||
|
FurnitureScenarioType.Premium,
|
||||||
|
FurnitureScenarioType.Existing,
|
||||||
|
].includes(scenario.type)
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
await selectionRepo.delete({ projectId, scenarioId: scenario.id });
|
||||||
|
await this.autoSelections(projectId, scenario.id, scenario.type, manager);
|
||||||
|
scenario.version += 1;
|
||||||
|
await manager.getRepository(FurnitureScenarioEntity).save(scenario);
|
||||||
|
}
|
||||||
|
}
|
||||||
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 | null) =>
|
const decimal = (value?: number | null) =>
|
||||||
|
|||||||
@@ -97,13 +97,29 @@
|
|||||||
<strong>{{ scenario.total | currency: 'EUR' : 'symbol' : '1.2-2' : 'de' }}</strong>
|
<strong>{{ scenario.total | currency: 'EUR' : 'symbol' : '1.2-2' : 'de' }}</strong>
|
||||||
<p>{{ scenario.selectedRequirements }} gewählt · {{ scenario.openRequirements }} offen</p>
|
<p>{{ scenario.selectedRequirements }} gewählt · {{ scenario.openRequirements }} offen</p>
|
||||||
@if (canEdit) {
|
@if (canEdit) {
|
||||||
<button
|
<div class="scenario-actions">
|
||||||
class="ui-button ui-button--ghost"
|
<button
|
||||||
type="button"
|
class="ui-button ui-button--ghost"
|
||||||
(click)="editAssignment(scenario.id)"
|
type="button"
|
||||||
>
|
(click)="editAssignment(scenario.id)"
|
||||||
Auswahl bearbeiten
|
>
|
||||||
</button>
|
Auswahl
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="ui-button ui-button--ghost"
|
||||||
|
type="button"
|
||||||
|
(click)="editScenario(scenario)"
|
||||||
|
>
|
||||||
|
Bearbeiten
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="ui-button ui-button--danger"
|
||||||
|
type="button"
|
||||||
|
(click)="deleteScenario(scenario)"
|
||||||
|
>
|
||||||
|
Löschen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</article>
|
</article>
|
||||||
}
|
}
|
||||||
@@ -312,7 +328,7 @@
|
|||||||
<form [formGroup]="scenarioForm" (ngSubmit)="saveScenario()">
|
<form [formGroup]="scenarioForm" (ngSubmit)="saveScenario()">
|
||||||
<header>
|
<header>
|
||||||
<span class="eyebrow">Variante</span>
|
<span class="eyebrow">Variante</span>
|
||||||
<h3>Szenario anlegen</h3>
|
<h3>{{ editingScenario() ? 'Szenario bearbeiten' : 'Szenario anlegen' }}</h3>
|
||||||
<p>Eine Vorauswahl kann später jederzeit angepasst werden.</p>
|
<p>Eine Vorauswahl kann später jederzeit angepasst werden.</p>
|
||||||
</header>
|
</header>
|
||||||
<label>Name *<input formControlName="name" placeholder="z. B. Wunschlösung" /></label>
|
<label>Name *<input formControlName="name" placeholder="z. B. Wunschlösung" /></label>
|
||||||
@@ -324,6 +340,14 @@
|
|||||||
<option value="existing">Vorhandene Möbel</option>
|
<option value="existing">Vorhandene Möbel</option>
|
||||||
</select></label
|
</select></label
|
||||||
>
|
>
|
||||||
|
<label
|
||||||
|
>Status<select formControlName="status">
|
||||||
|
<option value="draft">Entwurf</option>
|
||||||
|
<option value="active">Aktiv</option>
|
||||||
|
<option value="archived">Archiviert</option>
|
||||||
|
</select></label
|
||||||
|
>
|
||||||
|
<label>Beschreibung<textarea rows="3" formControlName="description"></textarea></label>
|
||||||
<label class="check"
|
<label class="check"
|
||||||
><input type="checkbox" formControlName="isDefault" /> Als Standard verwenden</label
|
><input type="checkbox" formControlName="isDefault" /> Als Standard verwenden</label
|
||||||
>
|
>
|
||||||
@@ -331,12 +355,14 @@
|
|||||||
<button class="ui-button ui-button--ghost" type="button" (click)="closeDialog('scenario')">
|
<button class="ui-button ui-button--ghost" type="button" (click)="closeDialog('scenario')">
|
||||||
Abbrechen</button
|
Abbrechen</button
|
||||||
><button class="ui-button ui-button--primary" type="submit" [disabled]="saving()">
|
><button class="ui-button ui-button--primary" type="submit" [disabled]="saving()">
|
||||||
Anlegen
|
{{ editingScenario() ? 'Änderungen speichern' : 'Anlegen' }}
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<ui-confirm-dialog />
|
||||||
|
|
||||||
<dialog #assignmentDialog class="editor-dialog" (cancel)="closeDialog('assignment')">
|
<dialog #assignmentDialog class="editor-dialog" (cancel)="closeDialog('assignment')">
|
||||||
<form [formGroup]="assignmentForm" (ngSubmit)="saveAssignment()">
|
<form [formGroup]="assignmentForm" (ngSubmit)="saveAssignment()">
|
||||||
<header>
|
<header>
|
||||||
|
|||||||
@@ -64,6 +64,18 @@ p {
|
|||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scenario-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-actions .ui-button {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
padding-inline: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { ElementRef, OnChanges } from '@angular/core';
|
|||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { forkJoin } from 'rxjs';
|
import { forkJoin } from 'rxjs';
|
||||||
import type { Observable } from 'rxjs';
|
import type { Observable } from 'rxjs';
|
||||||
import { ToastService } from '../../shared/ui';
|
import { ToastService, UiConfirmDialogComponent } from '../../shared/ui';
|
||||||
import { FurnitureGridComponent } from './furniture-grid.component';
|
import { FurnitureGridComponent } from './furniture-grid.component';
|
||||||
import type {
|
import type {
|
||||||
FurnitureOption,
|
FurnitureOption,
|
||||||
@@ -20,7 +20,7 @@ export type FurnitureSection = 'furniture' | 'requirements' | 'scenarios';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-furniture-section',
|
selector: 'app-furniture-section',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CurrencyPipe, FurnitureGridComponent, ReactiveFormsModule],
|
imports: [CurrencyPipe, FurnitureGridComponent, ReactiveFormsModule, UiConfirmDialogComponent],
|
||||||
templateUrl: './furniture-section.component.html',
|
templateUrl: './furniture-section.component.html',
|
||||||
styleUrl: './furniture-section.component.scss',
|
styleUrl: './furniture-section.component.scss',
|
||||||
})
|
})
|
||||||
@@ -35,6 +35,7 @@ export class FurnitureSectionComponent implements OnChanges {
|
|||||||
@ViewChild('optionDialog') private optionDialog?: ElementRef<HTMLDialogElement>;
|
@ViewChild('optionDialog') private optionDialog?: ElementRef<HTMLDialogElement>;
|
||||||
@ViewChild('scenarioDialog') private scenarioDialog?: ElementRef<HTMLDialogElement>;
|
@ViewChild('scenarioDialog') private scenarioDialog?: ElementRef<HTMLDialogElement>;
|
||||||
@ViewChild('assignmentDialog') private assignmentDialog?: ElementRef<HTMLDialogElement>;
|
@ViewChild('assignmentDialog') private assignmentDialog?: ElementRef<HTMLDialogElement>;
|
||||||
|
@ViewChild(UiConfirmDialogComponent) private confirmDialog?: UiConfirmDialogComponent;
|
||||||
|
|
||||||
private readonly api = inject(HauspilotApiService);
|
private readonly api = inject(HauspilotApiService);
|
||||||
private readonly toasts = inject(ToastService);
|
private readonly toasts = inject(ToastService);
|
||||||
@@ -45,6 +46,7 @@ export class FurnitureSectionComponent implements OnChanges {
|
|||||||
readonly error = signal<string | null>(null);
|
readonly error = signal<string | null>(null);
|
||||||
readonly editingRequirement = signal<FurnitureRequirement | null>(null);
|
readonly editingRequirement = signal<FurnitureRequirement | null>(null);
|
||||||
readonly editingOption = signal<FurnitureOption | null>(null);
|
readonly editingOption = signal<FurnitureOption | null>(null);
|
||||||
|
readonly editingScenario = signal<FurnitureScenario | null>(null);
|
||||||
readonly optionRequirement = signal<FurnitureRequirement | null>(null);
|
readonly optionRequirement = signal<FurnitureRequirement | null>(null);
|
||||||
readonly assignableRequirements = computed(() =>
|
readonly assignableRequirements = computed(() =>
|
||||||
this.requirements().filter((requirement) => this.optionsFor(requirement).length > 0),
|
this.requirements().filter((requirement) => this.optionsFor(requirement).length > 0),
|
||||||
@@ -123,6 +125,8 @@ export class FurnitureSectionComponent implements OnChanges {
|
|||||||
validators: [(control) => Validators.required(control)],
|
validators: [(control) => Validators.required(control)],
|
||||||
}),
|
}),
|
||||||
automaticSelection: new FormControl('budget', { nonNullable: true }),
|
automaticSelection: new FormControl('budget', { nonNullable: true }),
|
||||||
|
description: new FormControl('', { nonNullable: true }),
|
||||||
|
status: new FormControl('draft', { nonNullable: true }),
|
||||||
isDefault: new FormControl(false, { nonNullable: true }),
|
isDefault: new FormControl(false, { nonNullable: true }),
|
||||||
});
|
});
|
||||||
readonly assignmentForm = new FormGroup({
|
readonly assignmentForm = new FormGroup({
|
||||||
@@ -344,25 +348,60 @@ export class FurnitureSectionComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newScenario(): void {
|
newScenario(): void {
|
||||||
this.scenarioForm.reset({ name: '', automaticSelection: 'budget', isDefault: false });
|
this.editingScenario.set(null);
|
||||||
|
this.scenarioForm.reset({
|
||||||
|
name: '',
|
||||||
|
automaticSelection: 'budget',
|
||||||
|
description: '',
|
||||||
|
status: 'draft',
|
||||||
|
isDefault: false,
|
||||||
|
});
|
||||||
|
this.open(this.scenarioDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
editScenario(scenario: FurnitureScenario): void {
|
||||||
|
this.editingScenario.set(scenario);
|
||||||
|
this.scenarioForm.reset({
|
||||||
|
name: scenario.name,
|
||||||
|
automaticSelection: scenario.type,
|
||||||
|
description: scenario.description ?? '',
|
||||||
|
status: scenario.status,
|
||||||
|
isDefault: scenario.isDefault,
|
||||||
|
});
|
||||||
this.open(this.scenarioDialog);
|
this.open(this.scenarioDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveScenario(): void {
|
saveScenario(): void {
|
||||||
if (this.scenarioForm.invalid) return this.invalid(this.scenarioForm);
|
if (this.scenarioForm.invalid) return this.invalid(this.scenarioForm);
|
||||||
const value = this.scenarioForm.getRawValue();
|
const value = this.scenarioForm.getRawValue();
|
||||||
|
const existing = this.editingScenario();
|
||||||
|
const body = {
|
||||||
|
name: value.name,
|
||||||
|
description: value.description,
|
||||||
|
type: value.automaticSelection,
|
||||||
|
status: value.status,
|
||||||
|
automaticSelection: value.automaticSelection,
|
||||||
|
isDefault: value.isDefault,
|
||||||
|
};
|
||||||
this.save(
|
this.save(
|
||||||
this.api.createFurnitureScenario(this.projectId, {
|
existing
|
||||||
name: value.name,
|
? this.api.updateFurnitureScenario(this.projectId, existing, body)
|
||||||
type: value.automaticSelection,
|
: this.api.createFurnitureScenario(this.projectId, body),
|
||||||
status: 'draft',
|
|
||||||
automaticSelection: value.automaticSelection,
|
|
||||||
isDefault: value.isDefault,
|
|
||||||
}),
|
|
||||||
() => this.close(this.scenarioDialog),
|
() => this.close(this.scenarioDialog),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteScenario(scenario: FurnitureScenario): Promise<void> {
|
||||||
|
const confirmed = await this.confirmDialog?.open({
|
||||||
|
title: 'Szenario löschen?',
|
||||||
|
description: `„${scenario.name}“ und seine Möbelauswahl werden gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.`,
|
||||||
|
confirmLabel: 'Szenario löschen',
|
||||||
|
danger: true,
|
||||||
|
});
|
||||||
|
if (!confirmed) return;
|
||||||
|
this.save(this.api.deleteFurnitureScenario(this.projectId, scenario), () => undefined);
|
||||||
|
}
|
||||||
|
|
||||||
editAssignment(scenarioId = '', requirement?: FurnitureRequirement): void {
|
editAssignment(scenarioId = '', requirement?: FurnitureRequirement): void {
|
||||||
const scenario = this.scenarios().find(({ id }) => id === scenarioId) ?? this.scenarios()[0];
|
const scenario = this.scenarios().find(({ id }) => id === scenarioId) ?? this.scenarios()[0];
|
||||||
const target = requirement ?? this.assignableRequirements()[0];
|
const target = requirement ?? this.assignableRequirements()[0];
|
||||||
@@ -446,6 +485,7 @@ export class FurnitureSectionComponent implements OnChanges {
|
|||||||
dialog?.nativeElement.close();
|
dialog?.nativeElement.close();
|
||||||
this.editingRequirement.set(null);
|
this.editingRequirement.set(null);
|
||||||
this.editingOption.set(null);
|
this.editingOption.set(null);
|
||||||
|
this.editingScenario.set(null);
|
||||||
this.optionRequirement.set(null);
|
this.optionRequirement.set(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { TestBed } from '@angular/core/testing';
|
|||||||
import { provideHttpClient } from '@angular/common/http';
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||||
import { HauspilotApiService } from './hauspilot-api.service';
|
import { HauspilotApiService } from './hauspilot-api.service';
|
||||||
|
import type { FurnitureScenario } from './hauspilot-api.service';
|
||||||
|
|
||||||
describe('HauspilotApiService', () => {
|
describe('HauspilotApiService', () => {
|
||||||
it('überträgt Pagination, Whitelist-Sortierung und Aufgabenfilter an das Backend', () => {
|
it('überträgt Pagination, Whitelist-Sortierung und Aufgabenfilter an das Backend', () => {
|
||||||
@@ -44,4 +45,28 @@ describe('HauspilotApiService', () => {
|
|||||||
request.flush([]);
|
request.flush([]);
|
||||||
http.verify();
|
http.verify();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('überträgt Szenarioänderungen mit Version und löscht das gewählte Szenario', () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [provideHttpClient(), provideHttpClientTesting()],
|
||||||
|
});
|
||||||
|
const service = TestBed.inject(HauspilotApiService);
|
||||||
|
const http = TestBed.inject(HttpTestingController);
|
||||||
|
const scenario = {
|
||||||
|
id: 'scenario-1',
|
||||||
|
version: 4,
|
||||||
|
} as FurnitureScenario;
|
||||||
|
|
||||||
|
service.updateFurnitureScenario('project-1', scenario, { name: 'Neu' }).subscribe();
|
||||||
|
const update = http.expectOne('/api/projects/project-1/furniture-scenarios/scenario-1');
|
||||||
|
expect(update.request.method).toBe('PATCH');
|
||||||
|
expect(update.request.body).toEqual({ name: 'Neu', version: 4 });
|
||||||
|
update.flush({});
|
||||||
|
|
||||||
|
service.deleteFurnitureScenario('project-1', scenario).subscribe();
|
||||||
|
const deletion = http.expectOne('/api/projects/project-1/furniture-scenarios/scenario-1');
|
||||||
|
expect(deletion.request.method).toBe('DELETE');
|
||||||
|
deletion.flush(null);
|
||||||
|
http.verify();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -573,6 +573,15 @@ export class HauspilotApiService {
|
|||||||
createFurnitureScenario(id: string, body: object) {
|
createFurnitureScenario(id: string, body: object) {
|
||||||
return this.http.post<FurnitureScenario>(`${this.base}/${id}/furniture-scenarios`, body);
|
return this.http.post<FurnitureScenario>(`${this.base}/${id}/furniture-scenarios`, body);
|
||||||
}
|
}
|
||||||
|
updateFurnitureScenario(id: string, scenario: FurnitureScenario, body: object) {
|
||||||
|
return this.http.patch<FurnitureScenario>(
|
||||||
|
`${this.base}/${id}/furniture-scenarios/${scenario.id}`,
|
||||||
|
{ ...body, version: scenario.version },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
deleteFurnitureScenario(id: string, scenario: FurnitureScenario) {
|
||||||
|
return this.http.delete<void>(`${this.base}/${id}/furniture-scenarios/${scenario.id}`);
|
||||||
|
}
|
||||||
updateFurnitureScenarioSelections(
|
updateFurnitureScenarioSelections(
|
||||||
id: string,
|
id: string,
|
||||||
scenario: FurnitureScenario,
|
scenario: FurnitureScenario,
|
||||||
|
|||||||
Reference in New Issue
Block a user