generated from bastian/boilerplate
xx
This commit is contained in:
52
apps/backend/src/renovation/furniture-selection.ts
Normal file
52
apps/backend/src/renovation/furniture-selection.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
FurnitureOptionStatus,
|
||||||
|
FurnitureRequirementStatus,
|
||||||
|
} from './entities/furniture.entities';
|
||||||
|
|
||||||
|
export interface FurnitureSelectionState {
|
||||||
|
currentlySelected: boolean;
|
||||||
|
favorite: boolean;
|
||||||
|
status: FurnitureOptionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deselectFurnitureOption(
|
||||||
|
option: FurnitureSelectionState,
|
||||||
|
): FurnitureSelectionState {
|
||||||
|
return {
|
||||||
|
...option,
|
||||||
|
currentlySelected: false,
|
||||||
|
status:
|
||||||
|
option.status === FurnitureOptionStatus.Selected
|
||||||
|
? option.favorite
|
||||||
|
? FurnitureOptionStatus.Favorite
|
||||||
|
: FurnitureOptionStatus.Reviewing
|
||||||
|
: option.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleFurnitureSelection(
|
||||||
|
option: FurnitureSelectionState,
|
||||||
|
requirementStatus: FurnitureRequirementStatus,
|
||||||
|
): {
|
||||||
|
option: FurnitureSelectionState;
|
||||||
|
requirementStatus: FurnitureRequirementStatus;
|
||||||
|
} {
|
||||||
|
if (option.currentlySelected) {
|
||||||
|
return {
|
||||||
|
option: deselectFurnitureOption(option),
|
||||||
|
requirementStatus:
|
||||||
|
option.status === FurnitureOptionStatus.Selected
|
||||||
|
? FurnitureRequirementStatus.DecisionOpen
|
||||||
|
: requirementStatus,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
option: {
|
||||||
|
...option,
|
||||||
|
currentlySelected: true,
|
||||||
|
status: FurnitureOptionStatus.Selected,
|
||||||
|
},
|
||||||
|
requirementStatus: FurnitureRequirementStatus.Selected,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -40,6 +40,10 @@ import {
|
|||||||
sumMoney,
|
sumMoney,
|
||||||
} from './furniture-pricing';
|
} from './furniture-pricing';
|
||||||
import { FurnitureRepository } from './furniture.repository';
|
import { FurnitureRepository } from './furniture.repository';
|
||||||
|
import {
|
||||||
|
deselectFurnitureOption,
|
||||||
|
toggleFurnitureSelection,
|
||||||
|
} from './furniture-selection';
|
||||||
import { RenovationRepository } from './renovation.repository';
|
import { RenovationRepository } from './renovation.repository';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -430,7 +434,7 @@ export class FurnitureService {
|
|||||||
const options = manager.getRepository(FurnitureOptionEntity);
|
const options = manager.getRepository(FurnitureOptionEntity);
|
||||||
const candidate = await options.findOneBy({ id, projectId });
|
const candidate = await options.findOneBy({ id, projectId });
|
||||||
if (!candidate || candidate.deletedAt) this.notFound();
|
if (!candidate || candidate.deletedAt) this.notFound();
|
||||||
await manager
|
const requirement = await manager
|
||||||
.getRepository(FurnitureRequirementEntity)
|
.getRepository(FurnitureRequirementEntity)
|
||||||
.createQueryBuilder('requirement')
|
.createQueryBuilder('requirement')
|
||||||
.setLock('pessimistic_write')
|
.setLock('pessimistic_write')
|
||||||
@@ -456,7 +460,8 @@ export class FurnitureService {
|
|||||||
'Die Alternative wurde zwischenzeitlich geändert. Laden Sie die Daten neu.',
|
'Die Alternative wurde zwischenzeitlich geändert. Laden Sie die Daten neu.',
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
[
|
!option.currentlySelected &&
|
||||||
|
([
|
||||||
FurnitureOptionStatus.Archived,
|
FurnitureOptionStatus.Archived,
|
||||||
FurnitureOptionStatus.Unavailable,
|
FurnitureOptionStatus.Unavailable,
|
||||||
FurnitureOptionStatus.Rejected,
|
FurnitureOptionStatus.Rejected,
|
||||||
@@ -465,30 +470,37 @@ export class FurnitureService {
|
|||||||
[
|
[
|
||||||
FurnitureAvailability.Unavailable,
|
FurnitureAvailability.Unavailable,
|
||||||
FurnitureAvailability.Discontinued,
|
FurnitureAvailability.Discontinued,
|
||||||
].includes(option.availability)
|
].includes(option.availability))
|
||||||
)
|
)
|
||||||
this.conflict('Diese Alternative ist nicht auswählbar.');
|
this.conflict('Diese Alternative ist nicht auswählbar.');
|
||||||
await options.update(
|
if (!option.currentlySelected) {
|
||||||
{
|
const previousSelections = await options.find({
|
||||||
|
where: {
|
||||||
projectId,
|
projectId,
|
||||||
requirementId: option.requirementId,
|
requirementId: option.requirementId,
|
||||||
currentlySelected: true,
|
currentlySelected: true,
|
||||||
},
|
},
|
||||||
{ currentlySelected: false },
|
});
|
||||||
);
|
for (const previous of previousSelections) {
|
||||||
option.currentlySelected = true;
|
Object.assign(previous, deselectFurnitureOption(previous));
|
||||||
option.status = FurnitureOptionStatus.Selected;
|
}
|
||||||
|
await options.save(previousSelections);
|
||||||
|
}
|
||||||
|
const transition = toggleFurnitureSelection(option, requirement.status);
|
||||||
|
Object.assign(option, transition.option);
|
||||||
const saved = await options.save(option);
|
const saved = await options.save(option);
|
||||||
await manager
|
await manager
|
||||||
.getRepository(FurnitureRequirementEntity)
|
.getRepository(FurnitureRequirementEntity)
|
||||||
.update(
|
.update(
|
||||||
{ id: option.requirementId, projectId },
|
{ id: option.requirementId, projectId },
|
||||||
{ status: FurnitureRequirementStatus.Selected },
|
{ status: transition.requirementStatus },
|
||||||
);
|
);
|
||||||
await this.activity(
|
await this.activity(
|
||||||
projectId,
|
projectId,
|
||||||
userId,
|
userId,
|
||||||
'furniture.option-selected',
|
saved.currentlySelected
|
||||||
|
? 'furniture.option-selected'
|
||||||
|
: 'furniture.option-deselected',
|
||||||
{ optionId: id, requirementId: option.requirementId },
|
{ optionId: id, requirementId: option.requirementId },
|
||||||
manager,
|
manager,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import {
|
||||||
|
FurnitureOptionStatus,
|
||||||
|
FurnitureRequirementStatus,
|
||||||
|
} from '../entities/furniture.entities';
|
||||||
|
import { toggleFurnitureSelection } from '../furniture-selection';
|
||||||
|
|
||||||
|
describe('furniture selection', () => {
|
||||||
|
it('removes a selected option from the cost selection', () => {
|
||||||
|
const result = toggleFurnitureSelection(
|
||||||
|
{
|
||||||
|
currentlySelected: true,
|
||||||
|
favorite: false,
|
||||||
|
status: FurnitureOptionStatus.Selected,
|
||||||
|
},
|
||||||
|
FurnitureRequirementStatus.Selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
option: {
|
||||||
|
currentlySelected: false,
|
||||||
|
favorite: false,
|
||||||
|
status: FurnitureOptionStatus.Reviewing,
|
||||||
|
},
|
||||||
|
requirementStatus: FurnitureRequirementStatus.DecisionOpen,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the favorite marker when a selected favorite is deselected', () => {
|
||||||
|
const result = toggleFurnitureSelection(
|
||||||
|
{
|
||||||
|
currentlySelected: true,
|
||||||
|
favorite: true,
|
||||||
|
status: FurnitureOptionStatus.Selected,
|
||||||
|
},
|
||||||
|
FurnitureRequirementStatus.Selected,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.option.status).toBe(FurnitureOptionStatus.Favorite);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -690,7 +690,7 @@ export class FurnitureGridComponent implements OnChanges {
|
|||||||
{
|
{
|
||||||
field: 'currentlySelected',
|
field: 'currentlySelected',
|
||||||
headerName: 'Auswahl',
|
headerName: 'Auswahl',
|
||||||
valueFormatter: (p) => (p.value ? '◉ Ausgewählt' : '○ auswählen'),
|
valueFormatter: (p) => (p.value ? '◉ Auswahl aufheben' : '○ auswählen'),
|
||||||
sortable: false,
|
sortable: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -420,10 +420,11 @@ const statusLabels: Record<string, string> = {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="ui-button"
|
class="ui-button"
|
||||||
|
[class.ui-button--secondary]="option.currentlySelected"
|
||||||
(click)="select(option)"
|
(click)="select(option)"
|
||||||
[disabled]="option.currentlySelected"
|
[attr.aria-pressed]="option.currentlySelected"
|
||||||
>
|
>
|
||||||
Auswählen
|
{{ option.currentlySelected ? 'Auswahl aufheben' : 'Auswählen' }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
Reference in New Issue
Block a user