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,
|
||||
} from './furniture-pricing';
|
||||
import { FurnitureRepository } from './furniture.repository';
|
||||
import {
|
||||
deselectFurnitureOption,
|
||||
toggleFurnitureSelection,
|
||||
} from './furniture-selection';
|
||||
import { RenovationRepository } from './renovation.repository';
|
||||
|
||||
@Injectable()
|
||||
@@ -430,7 +434,7 @@ export class FurnitureService {
|
||||
const options = manager.getRepository(FurnitureOptionEntity);
|
||||
const candidate = await options.findOneBy({ id, projectId });
|
||||
if (!candidate || candidate.deletedAt) this.notFound();
|
||||
await manager
|
||||
const requirement = await manager
|
||||
.getRepository(FurnitureRequirementEntity)
|
||||
.createQueryBuilder('requirement')
|
||||
.setLock('pessimistic_write')
|
||||
@@ -456,39 +460,47 @@ export class FurnitureService {
|
||||
'Die Alternative wurde zwischenzeitlich geändert. Laden Sie die Daten neu.',
|
||||
);
|
||||
if (
|
||||
[
|
||||
!option.currentlySelected &&
|
||||
([
|
||||
FurnitureOptionStatus.Archived,
|
||||
FurnitureOptionStatus.Unavailable,
|
||||
FurnitureOptionStatus.Rejected,
|
||||
FurnitureOptionStatus.Returned,
|
||||
].includes(option.status) ||
|
||||
[
|
||||
FurnitureAvailability.Unavailable,
|
||||
FurnitureAvailability.Discontinued,
|
||||
].includes(option.availability)
|
||||
[
|
||||
FurnitureAvailability.Unavailable,
|
||||
FurnitureAvailability.Discontinued,
|
||||
].includes(option.availability))
|
||||
)
|
||||
this.conflict('Diese Alternative ist nicht auswählbar.');
|
||||
await options.update(
|
||||
{
|
||||
projectId,
|
||||
requirementId: option.requirementId,
|
||||
currentlySelected: true,
|
||||
},
|
||||
{ currentlySelected: false },
|
||||
);
|
||||
option.currentlySelected = true;
|
||||
option.status = FurnitureOptionStatus.Selected;
|
||||
if (!option.currentlySelected) {
|
||||
const previousSelections = await options.find({
|
||||
where: {
|
||||
projectId,
|
||||
requirementId: option.requirementId,
|
||||
currentlySelected: true,
|
||||
},
|
||||
});
|
||||
for (const previous of previousSelections) {
|
||||
Object.assign(previous, deselectFurnitureOption(previous));
|
||||
}
|
||||
await options.save(previousSelections);
|
||||
}
|
||||
const transition = toggleFurnitureSelection(option, requirement.status);
|
||||
Object.assign(option, transition.option);
|
||||
const saved = await options.save(option);
|
||||
await manager
|
||||
.getRepository(FurnitureRequirementEntity)
|
||||
.update(
|
||||
{ id: option.requirementId, projectId },
|
||||
{ status: FurnitureRequirementStatus.Selected },
|
||||
{ status: transition.requirementStatus },
|
||||
);
|
||||
await this.activity(
|
||||
projectId,
|
||||
userId,
|
||||
'furniture.option-selected',
|
||||
saved.currentlySelected
|
||||
? 'furniture.option-selected'
|
||||
: 'furniture.option-deselected',
|
||||
{ optionId: id, requirementId: option.requirementId },
|
||||
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',
|
||||
headerName: 'Auswahl',
|
||||
valueFormatter: (p) => (p.value ? '◉ Ausgewählt' : '○ auswählen'),
|
||||
valueFormatter: (p) => (p.value ? '◉ Auswahl aufheben' : '○ auswählen'),
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -420,10 +420,11 @@ const statusLabels: Record<string, string> = {
|
||||
<button
|
||||
type="button"
|
||||
class="ui-button"
|
||||
[class.ui-button--secondary]="option.currentlySelected"
|
||||
(click)="select(option)"
|
||||
[disabled]="option.currentlySelected"
|
||||
[attr.aria-pressed]="option.currentlySelected"
|
||||
>
|
||||
Auswählen
|
||||
{{ option.currentlySelected ? 'Auswahl aufheben' : 'Auswählen' }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user