S16 postgresql opslag added
All checks were successful
Build, test and publish / verify (push) Successful in 7m31s
All checks were successful
Build, test and publish / verify (push) Successful in 7m31s
This commit is contained in:
@@ -50,11 +50,33 @@ describe('EntityEditor', () => {
|
||||
const saved = vi.fn();
|
||||
component.saved.subscribe(saved);
|
||||
await component.save();
|
||||
expect(api.saveSchool).toHaveBeenCalledWith({ name: 'Studio' }, undefined);
|
||||
expect(api.saveSchool).toHaveBeenCalledWith({ name: 'Studio' }, undefined, undefined);
|
||||
expect(saved).toHaveBeenCalledOnce();
|
||||
expect(component.busy()).toBe(false);
|
||||
});
|
||||
|
||||
it('retains the revision from opening the editor across background refreshes', async () => {
|
||||
api.saveDancer.mockReturnValue(of({ id: 3 }));
|
||||
const fixture = TestBed.createComponent(EntityEditor);
|
||||
fixture.componentRef.setInput('target', {
|
||||
kind: 'dancer',
|
||||
value: { id: 3, name: 'Sam', birthDate: '2010-01-01' },
|
||||
});
|
||||
fixture.componentRef.setInput('schoolId', 7);
|
||||
fixture.componentRef.setInput('revision', 4);
|
||||
const component = fixture.componentInstance;
|
||||
component.ngOnInit();
|
||||
fixture.componentRef.setInput('revision', 5);
|
||||
component.name = 'Sam edited';
|
||||
await component.save();
|
||||
expect(api.saveDancer).toHaveBeenCalledWith(
|
||||
7,
|
||||
{ name: 'Sam edited', birthDate: '2010-01-01' },
|
||||
3,
|
||||
4,
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps the editor open and preserves input when saving fails', async () => {
|
||||
api.saveSchool.mockReturnValue(throwError(() => new HttpErrorResponse({ status: 0 })));
|
||||
const component = editor({ kind: 'school' });
|
||||
@@ -116,6 +138,7 @@ describe('EntityEditor', () => {
|
||||
7,
|
||||
expect.objectContaining({ preferredDayParts: 1 }),
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -174,6 +174,8 @@ import { DialogFrame } from './dialog-frame';
|
||||
})
|
||||
export class EntityEditor implements OnInit {
|
||||
readonly target = input.required<EditorTarget>();
|
||||
readonly revision = input<number>();
|
||||
private originalRevision?: number;
|
||||
readonly schoolId = input<number>();
|
||||
readonly shows = input<Show[]>([]);
|
||||
readonly closed = output<void>();
|
||||
@@ -207,6 +209,7 @@ export class EntityEditor implements OnInit {
|
||||
return `${labels[this.target().kind]} ${this.target().value ? 'bewerken' : 'toevoegen'}`;
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.originalRevision = this.revision();
|
||||
const target = this.target();
|
||||
if (!target.value) return;
|
||||
if (target.kind === 'group') {
|
||||
@@ -298,13 +301,14 @@ export class EntityEditor implements OnInit {
|
||||
let request: Observable<unknown>;
|
||||
switch (target.kind) {
|
||||
case 'school':
|
||||
request = this.api.saveSchool({ name: this.name.trim() }, id);
|
||||
request = this.api.saveSchool({ name: this.name.trim() }, id, this.originalRevision);
|
||||
break;
|
||||
case 'dancer':
|
||||
request = this.api.saveDancer(
|
||||
schoolId!,
|
||||
{ name: this.name.trim(), birthDate: this.date },
|
||||
id,
|
||||
this.originalRevision,
|
||||
);
|
||||
break;
|
||||
case 'group':
|
||||
@@ -318,6 +322,7 @@ export class EntityEditor implements OnInit {
|
||||
styles: this.selectedStyles,
|
||||
},
|
||||
id,
|
||||
this.originalRevision,
|
||||
);
|
||||
break;
|
||||
case 'show':
|
||||
@@ -329,6 +334,7 @@ export class EntityEditor implements OnInit {
|
||||
preferredDayParts: this.preferredDayParts,
|
||||
},
|
||||
id,
|
||||
this.originalRevision,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ import { DialogFrame } from './dialog-frame';
|
||||
`,
|
||||
})
|
||||
export class GroupMembers implements OnInit {
|
||||
readonly revision = input<number>();
|
||||
private originalRevision?: number;
|
||||
readonly schoolId = input.required<number>();
|
||||
readonly group = input.required<DanceGroup>();
|
||||
readonly dancers = input.required<Dancer[]>();
|
||||
@@ -70,6 +72,7 @@ export class GroupMembers implements OnInit {
|
||||
);
|
||||
private readonly api = inject(SchoolApi);
|
||||
ngOnInit(): void {
|
||||
this.originalRevision = this.revision();
|
||||
this.memberIds.set([...this.group().dancerIds]);
|
||||
}
|
||||
async toggle(dancerId: number, event: Event): Promise<void> {
|
||||
@@ -82,7 +85,10 @@ export class GroupMembers implements OnInit {
|
||||
this.busy.set(true);
|
||||
this.error.set('');
|
||||
try {
|
||||
await firstValueFrom(this.api.membership(this.schoolId(), this.group().id, dancerId, add));
|
||||
await firstValueFrom(
|
||||
this.api.membership(this.schoolId(), this.group().id, dancerId, add, this.originalRevision),
|
||||
);
|
||||
if (this.originalRevision !== undefined) this.originalRevision++;
|
||||
this.memberIds.update((ids) =>
|
||||
add ? [...ids, dancerId] : ids.filter((id) => id !== dancerId),
|
||||
);
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('ScheduleEditor', () => {
|
||||
const draft = { ...show, finalized: false, violations: ['Group 1 requires 1 dayparts.'] };
|
||||
api.saveAssignments.mockReturnValue(of(draft));
|
||||
await component.save();
|
||||
expect(api.saveAssignments).toHaveBeenCalledWith(7, 4, [[2], [3]]);
|
||||
expect(api.saveAssignments).toHaveBeenCalledWith(7, 4, [[2], [3]], undefined);
|
||||
expect(component.current()).toEqual(draft);
|
||||
expect(component.dirty()).toBe(false);
|
||||
expect(api.finalize).not.toHaveBeenCalled();
|
||||
|
||||
@@ -113,6 +113,8 @@ import { DialogFrame } from './dialog-frame';
|
||||
export class ScheduleEditor implements OnInit {
|
||||
readonly show = input.required<Show>();
|
||||
readonly groups = input.required<DanceGroup[]>();
|
||||
readonly revision = input<number>();
|
||||
private originalRevision?: number;
|
||||
readonly schoolId = input.required<number>();
|
||||
readonly closed = output<void>();
|
||||
readonly saved = output<void>();
|
||||
@@ -124,6 +126,7 @@ export class ScheduleEditor implements OnInit {
|
||||
assignments: number[][] = [];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.originalRevision = this.revision();
|
||||
this.current.set(this.show());
|
||||
this.assignments = this.show().dayParts.map((p) => [...p.danceGroupIds]);
|
||||
}
|
||||
@@ -162,9 +165,15 @@ export class ScheduleEditor implements OnInit {
|
||||
try {
|
||||
const result = await firstValueFrom(
|
||||
finalize
|
||||
? this.api.finalize(this.schoolId(), this.show().id)
|
||||
: this.api.saveAssignments(this.schoolId(), this.show().id, this.assignments),
|
||||
? this.api.finalize(this.schoolId(), this.show().id, this.originalRevision)
|
||||
: this.api.saveAssignments(
|
||||
this.schoolId(),
|
||||
this.show().id,
|
||||
this.assignments,
|
||||
this.originalRevision,
|
||||
),
|
||||
);
|
||||
if (this.originalRevision !== undefined) this.originalRevision++;
|
||||
this.current.set(result);
|
||||
this.assignments = result.dayParts.map((p) => [...p.danceGroupIds]);
|
||||
this.dirty.set(false);
|
||||
|
||||
@@ -10,6 +10,7 @@ interface Preview {
|
||||
shows: number;
|
||||
existingId: number | null;
|
||||
existingName: string | null;
|
||||
existingRevision: number | null;
|
||||
}
|
||||
@Component({
|
||||
selector: 'app-school-transfer',
|
||||
@@ -117,7 +118,12 @@ export class SchoolTransfer {
|
||||
const query = p.existingId === null ? '' : `?replaceId=${p.existingId}`;
|
||||
await firstValueFrom(
|
||||
this.http.post(`${this.base}/import${query}`, this.json, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(p.existingRevision === null
|
||||
? {}
|
||||
: { 'X-School-Revision': String(p.existingRevision) }),
|
||||
},
|
||||
}),
|
||||
);
|
||||
this.cancel();
|
||||
|
||||
Reference in New Issue
Block a user