Skip to content

Commit 680c734

Browse files
authored
Merge branch 'main' into bug/803-Perfomance-LessonTeachers-except-LessonAbsences
2 parents f5548fa + ecb71c3 commit 680c734

20 files changed

+495
-73
lines changed

src/app/events/components/events-students-study-course-detail/events-students-study-course-detail.component.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,16 @@ describe("EventsStudentsStudyCourseDetailComponent", () => {
196196
detail1.Id = "1001";
197197
detail1.VssDesignation = "Lieblingsfarbe";
198198
detail1.DropdownItems = [
199-
{ Key: "1234", Value: "salmon" },
200-
{ Key: "5678", Value: "gold" },
199+
{ Key: "1234", Value: "salmon", IsActive: true },
200+
{ Key: "5678", Value: "gold", IsActive: true },
201201
];
202202

203203
const detail2 = buildSubscriptionDetail(1002, "Spaghetti Bolo");
204204
detail2.Id = "1002";
205205
detail2.VssDesignation = "Lieblingsessen";
206206
detail2.DropdownItems = [
207-
{ Key: "1234", Value: "Pizza" },
208-
{ Key: "5678", Value: "Lasagne" },
207+
{ Key: "1234", Value: "Pizza", IsActive: true },
208+
{ Key: "5678", Value: "Lasagne", IsActive: true },
209209
];
210210
detail2.VssStyle = "CB";
211211

src/app/events/components/test-edit-grades/test-edit-grades-common.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
--student-grade-column-width: calc(
1717
var(--spacer) + var(--grade-input-size) + var(--spacer)
1818
);
19-
--student-average-column-width: 100px;
19+
--student-average-column-width: 120px;
2020
--test-grade-column-width: calc(
2121
var(--spacer) + var(--point-input-size) + var(--points-grade-gap) +
2222
var(--grade-input-size) + var(--spacer)

src/app/import/components/emails/import-upload-emails/import-upload-emails.component.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HttpErrorResponse } from "@angular/common/http";
12
import { signal } from "@angular/core";
23
import {
34
ComponentFixture,
@@ -101,6 +102,9 @@ describe("ImportUploadEmailsComponent", () => {
101102

102103
expect(element.textContent).toContain("import.upload.success.title");
103104
expect(element.textContent).not.toContain("import.upload.error.title");
105+
expect(element.textContent).not.toContain(
106+
"import.upload.error.entry-error",
107+
);
104108
expect(element.querySelector("bkd-progress")).toBeNull();
105109
}));
106110

@@ -124,6 +128,38 @@ describe("ImportUploadEmailsComponent", () => {
124128
expect(element.textContent).toContain("import.upload.error.title");
125129
expect(element.querySelector("table")).not.toBeNull();
126130
expect(element.textContent).not.toContain("import.upload.success.title");
131+
expect(element.textContent).toContain("import.upload.error.entry-error");
132+
expect(element.querySelector("bkd-progress")).toBeNull();
133+
}));
134+
135+
it("displays error message from HttpErrorResponse", fakeAsync(() => {
136+
uploadServiceMock.progress.set({
137+
uploading: 0,
138+
success: 0,
139+
error: 1,
140+
total: 1,
141+
});
142+
143+
const entry = buildEntry();
144+
entry.importStatus = "error";
145+
entry.importError = new ImportError(
146+
new HttpErrorResponse({
147+
error: {
148+
Issues: [{ Message: "Some custom error message" }],
149+
},
150+
}),
151+
);
152+
resolveEntries([entry]);
153+
flush();
154+
fixture.detectChanges();
155+
156+
expect(element.textContent).toContain("import.upload.error.title");
157+
expect(element.querySelector("table")).not.toBeNull();
158+
expect(element.textContent).not.toContain("import.upload.success.title");
159+
expect(element.textContent).toContain("Some custom error message");
160+
expect(element.textContent).not.toContain(
161+
"import.upload.error.entry-error",
162+
);
127163
expect(element.querySelector("bkd-progress")).toBeNull();
128164
}));
129165
});

src/app/import/components/emails/import-upload-emails/import-upload-emails.component.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HttpErrorResponse } from "@angular/common/http";
12
import {
23
ChangeDetectionStrategy,
34
Component,
@@ -61,7 +62,16 @@ export class ImportUploadEmailsComponent {
6162

6263
getErrorMessage(entry: EmailImportEntry): Option<string> {
6364
if (entry.importStatus === "error" && entry.importError) {
64-
return this.translate.instant("import.upload.error.entry-error");
65+
const defaultError = this.translate.instant(
66+
"import.upload.error.entry-error",
67+
);
68+
if (entry.importError.error instanceof HttpErrorResponse) {
69+
const { error } = entry.importError.error;
70+
return Array.isArray(error["Issues"])
71+
? error["Issues"].map((issue) => issue["Message"]).join("; ")
72+
: defaultError;
73+
}
74+
return defaultError;
6575
}
6676
return null;
6777
}

src/app/import/components/subscription-details/import-upload-subscription-details/import-upload-subscription-details.component.spec.ts

+33
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HttpErrorResponse } from "@angular/common/http";
12
import { signal } from "@angular/core";
23
import {
34
ComponentFixture,
@@ -124,6 +125,38 @@ describe("ImportUploadSubscriptionDetailsComponent", () => {
124125
expect(element.textContent).toContain("import.upload.error.title");
125126
expect(element.querySelector("table")).not.toBeNull();
126127
expect(element.textContent).not.toContain("import.upload.success.title");
128+
expect(element.textContent).toContain("import.upload.error.entry-error");
129+
expect(element.querySelector("bkd-progress")).toBeNull();
130+
}));
131+
132+
it("displays error message from HttpErrorResponse", fakeAsync(() => {
133+
uploadServiceMock.progress.set({
134+
uploading: 0,
135+
success: 0,
136+
error: 1,
137+
total: 1,
138+
});
139+
140+
const entry = buildEntry();
141+
entry.importStatus = "error";
142+
entry.importError = new ImportError(
143+
new HttpErrorResponse({
144+
error: {
145+
Issues: [{ Message: "Some custom error message" }],
146+
},
147+
}),
148+
);
149+
resolveEntries([entry]);
150+
flush();
151+
fixture.detectChanges();
152+
153+
expect(element.textContent).toContain("import.upload.error.title");
154+
expect(element.querySelector("table")).not.toBeNull();
155+
expect(element.textContent).not.toContain("import.upload.success.title");
156+
expect(element.textContent).toContain("Some custom error message");
157+
expect(element.textContent).not.toContain(
158+
"import.upload.error.entry-error",
159+
);
127160
expect(element.querySelector("bkd-progress")).toBeNull();
128161
}));
129162
});

src/app/import/components/subscription-details/import-upload-subscription-details/import-upload-subscription-details.component.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HttpErrorResponse } from "@angular/common/http";
12
import {
23
ChangeDetectionStrategy,
34
Component,
@@ -62,7 +63,16 @@ export class ImportUploadSubscriptionDetailsComponent {
6263

6364
getErrorMessage(entry: SubscriptionDetailImportEntry): Option<string> {
6465
if (entry.importStatus === "error" && entry.importError) {
65-
return this.translate.instant("import.upload.error.entry-error");
66+
const defaultError = this.translate.instant(
67+
"import.upload.error.entry-error",
68+
);
69+
if (entry.importError.error instanceof HttpErrorResponse) {
70+
const { error } = entry.importError.error;
71+
return Array.isArray(error["Issues"])
72+
? error["Issues"].map((issue) => issue["Message"]).join("; ")
73+
: defaultError;
74+
}
75+
return defaultError;
6676
}
6777
return null;
6878
}

src/app/import/services/common/import-state.service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export type ImportEntry<
2222
TEntry extends ParsedEntry,
2323
TData,
2424
TValidationError extends ValidationError<TEntry>,
25-
TImportError,
25+
TImportError = ImportError,
2626
> = {
2727
validationStatus: ValidationStatus;
2828
importStatus: ImportStatus;

src/app/import/services/emails/import-validate-emails.service.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ export type EmailImportEntry = ImportEntry<
2020
{
2121
person?: PersonSummary;
2222
},
23-
EmailValidationError,
24-
unknown
23+
EmailValidationError
2524
>;
2625

2726
@Injectable({

src/app/import/services/subscription-details/import-validate-subscription-details.service.spec.ts

+54-31
Original file line numberDiff line numberDiff line change
@@ -218,85 +218,108 @@ describe("ImportValidateSubscriptionDetailsService", () => {
218218
});
219219

220220
describe("editable via internet", () => {
221-
it("sets entry status to invalid with SubscriptionDetailNotEditableError if subscription detail's VSSInternet is not 'E'", async () => {
221+
it("sets entry status to valid without error if subscription detail's VSSInternet is 'E'", async () => {
222222
mockSubscriptionDetail({
223-
VssInternet: "X",
223+
VssInternet: "E",
224224
});
225-
await expectsInvalidEntry(SubscriptionDetailNotEditableError);
225+
await expectsValidEntry();
226226
});
227227

228-
it("sets entry status to invalid with SubscriptionDetailNotEditableError if subscription detail's VssStyle is not 'TX'", async () => {
228+
it("sets entry status to invalid with SubscriptionDetailNotEditableError if subscription detail's VSSInternet is not 'E'", async () => {
229229
mockSubscriptionDetail({
230-
VssStyle: "X",
230+
VssInternet: "X",
231231
});
232232
await expectsInvalidEntry(SubscriptionDetailNotEditableError);
233233
});
234234
});
235235

236236
describe("value type", () => {
237-
it("sets entry status to valid without error for int value Int type", async () => {
237+
it("sets entry status to valid without error for int Int type with int value", async () => {
238238
mockSubscriptionDetail({
239239
VssTypeId: SubscriptionDetailType.Int,
240240
});
241241
entry.value = 42;
242242
await expectsValidEntry();
243243
});
244244

245-
it("sets entry status to invalid with InvalidValueTypeError for string value Int type", async () => {
245+
it("sets entry status to invalid with InvalidValueTypeError Int type with float value", async () => {
246+
mockSubscriptionDetail({
247+
VssTypeId: SubscriptionDetailType.Int,
248+
});
249+
entry.value = 1.25;
250+
await expectsInvalidEntry(InvalidValueTypeError);
251+
});
252+
253+
it("sets entry status to invalid with InvalidValueTypeError for Int type with string value", async () => {
246254
mockSubscriptionDetail({
247255
VssTypeId: SubscriptionDetailType.Int,
248256
});
249257
entry.value = "Lorem ipsum";
250258
await expectsInvalidEntry(InvalidValueTypeError);
251259
});
252260

253-
it("sets entry status to valid without error for int value Currency type", async () => {
261+
it("sets entry status to valid without error for Currency type with int value", async () => {
254262
mockSubscriptionDetail({
255263
VssTypeId: SubscriptionDetailType.Currency,
256264
});
257265
entry.value = 42;
258266
await expectsValidEntry();
259267
});
260268

261-
it("sets entry status to invalid with InvalidValueTypeError for string value Currency type", async () => {
269+
it("sets entry status to valid without error for Currency type with float value", async () => {
270+
mockSubscriptionDetail({
271+
VssTypeId: SubscriptionDetailType.Currency,
272+
});
273+
entry.value = 1.25;
274+
await expectsValidEntry();
275+
});
276+
277+
it("sets entry status to invalid with InvalidValueTypeError for Currency type with string value", async () => {
262278
mockSubscriptionDetail({
263279
VssTypeId: SubscriptionDetailType.Currency,
264280
});
265281
entry.value = "Lorem ipsum";
266282
await expectsInvalidEntry(InvalidValueTypeError);
267283
});
268284

269-
it("sets entry status to valid without error for string value Text type", async () => {
285+
it("sets entry status to valid without error for ShortText type with string value", async () => {
270286
mockSubscriptionDetail({
271-
VssTypeId: SubscriptionDetailType.Text,
287+
VssTypeId: SubscriptionDetailType.ShortText,
272288
});
273289
entry.value = "Lorem ipsum";
274290
await expectsValidEntry();
275291
});
276292

277-
it("sets entry status to invalid with InvalidValueTypeError for number value Text type", async () => {
293+
it("sets entry status to invalid with InvalidValueTypeError for ShortText type with number value", async () => {
278294
mockSubscriptionDetail({
279-
VssTypeId: SubscriptionDetailType.Text,
295+
VssTypeId: SubscriptionDetailType.ShortText,
280296
});
281297
entry.value = 42;
282298
await expectsInvalidEntry(InvalidValueTypeError);
283299
});
284300

285-
it("sets entry status to valid without error for string value MemoText type", async () => {
301+
it("sets entry status to valid without error for Text type with string value", async () => {
286302
mockSubscriptionDetail({
287-
VssTypeId: SubscriptionDetailType.MemoText,
303+
VssTypeId: SubscriptionDetailType.Text,
288304
});
289305
entry.value = "Lorem ipsum";
290306
await expectsValidEntry();
291307
});
292308

293-
it("sets entry status to invalid with InvalidValueTypeError for number value MemoText type", async () => {
309+
it("sets entry status to invalid with InvalidValueTypeError for Text type with number value", async () => {
294310
mockSubscriptionDetail({
295-
VssTypeId: SubscriptionDetailType.MemoText,
311+
VssTypeId: SubscriptionDetailType.Text,
296312
});
297313
entry.value = 42;
298314
await expectsInvalidEntry(InvalidValueTypeError);
299315
});
316+
317+
it("sets entry status to invalid with InvalidValueTypeError for unsupported YesNo type", async () => {
318+
mockSubscriptionDetail({
319+
VssTypeId: 291, // YesNo
320+
});
321+
await expectsInvalidEntry(InvalidValueTypeError);
322+
});
300323
});
301324

302325
describe("dropdown items", () => {
@@ -308,36 +331,36 @@ describe("ImportValidateSubscriptionDetailsService", () => {
308331
await expectsValidEntry();
309332
});
310333

311-
it("sets entry status to valid without error for supported dropdown item as number", async () => {
334+
it("sets entry status to valid without error for existing dropdown item", async () => {
312335
mockSubscriptionDetail({
313336
DropdownItems: [
314-
{ Key: 1, Value: "Apple" },
315-
{ Key: 2, Value: "Pear" },
337+
{ Key: 1, Value: "Apple", IsActive: true },
338+
{ Key: 2, Value: "Pear", IsActive: true },
316339
],
317340
});
318-
entry.value = 1;
341+
entry.value = "Apple";
319342
await expectsValidEntry();
320343
});
321344

322-
it("sets entry status to valid without error for supported dropdown item as string", async () => {
345+
it("sets entry status to invalid with InvalidDropdownValueError for non-existing dropdown item", async () => {
323346
mockSubscriptionDetail({
324347
DropdownItems: [
325-
{ Key: 1, Value: "Apple" },
326-
{ Key: 2, Value: "Pear" },
348+
{ Key: 1, Value: "Apple", IsActive: true },
349+
{ Key: 2, Value: "Pear", IsActive: true },
327350
],
328351
});
329-
entry.value = "1";
330-
await expectsValidEntry();
352+
entry.value = "Cherry";
353+
await expectsInvalidEntry(InvalidDropdownValueError);
331354
});
332355

333-
it("sets entry status to invalid with InvalidDropdownValueError for unsupported dropdown item", async () => {
356+
it("sets entry status to invalid with InvalidDropdownValueError for inactive dropdown item", async () => {
334357
mockSubscriptionDetail({
335358
DropdownItems: [
336-
{ Key: 1, Value: "Apple" },
337-
{ Key: 2, Value: "Pear" },
359+
{ Key: 1, Value: "Apple", IsActive: false },
360+
{ Key: 2, Value: "Pear", IsActive: true },
338361
],
339362
});
340-
entry.value = 3;
363+
entry.value = "Apple";
341364
await expectsInvalidEntry(InvalidDropdownValueError);
342365
});
343366
});
@@ -394,7 +417,7 @@ describe("ImportValidateSubscriptionDetailsService", () => {
394417
detail.EventId = 10;
395418
detail.IdPerson = 100;
396419
detail.DropdownItems = value.DropdownItems ?? null;
397-
detail.VssTypeId = value.VssTypeId ?? SubscriptionDetailType.Text;
420+
detail.VssTypeId = value.VssTypeId ?? SubscriptionDetailType.ShortText;
398421
detail.VssInternet = value.VssInternet ?? "E";
399422
detail.VssStyle = value.VssStyle ?? "TX";
400423
detail.Value = value.Value ?? "Lorem ipsum";

src/app/import/services/subscription-details/import-validate-subscription-details.service.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ export type SubscriptionDetailImportEntry = ImportEntry<
4444
person?: PersonFullName;
4545
subscriptionDetail?: SubscriptionDetail;
4646
},
47-
SubscriptionDetailValidationError,
48-
unknown
47+
SubscriptionDetailValidationError
4948
>;
5049

5150
@Injectable({

0 commit comments

Comments
 (0)