fix: T6 browser-check findings (compliance, treatments, labels)
Backend Image / build-and-push (push) Successful in 6m39s
Frontend Image / build-and-push (push) Successful in 14m30s

- api-client accepts 2xx answers without body (revoke showed an error)
- authority notices: sentOn in the future is rejected (Berlin day) on
  create, mark-sent and PATCH; dialog defaults to the Berlin day
- service worker never caches /api/v1/site-settings
- treatments/new no longer overflows at 390 px (min-w-0 on form
  wrappers); dose with more than 3 decimals is an error, never rounded
- medicines: expired lots flagged, disposal over the rest warns
- label designer: side panel at lg, sample values instead of
  placeholders, compliance panel reopens on lg, labelled zoom buttons
- skip link to #main-content, headings on no-access pages

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-15 18:34:52 +02:00
1 parent 6c390efe7b
commit e712c5d4d1
64 files changed
+1497 -323

No files matched your search

@@ -86,6 +86,17 @@ function assertYearFits(kind: AuthorityNoticeKind, year?: number): void {
}
}
/** A notice cannot have been sent tomorrow: sentOn must not lie after the Berlin day. */
function futureSentOnError(
sentOn: string | null | undefined,
): BadRequestException | undefined {
return sentOn && sentOn > berlinToday()
? new BadRequestException(
'Das Sendedatum darf nicht in der Zukunft liegen.',
)
: undefined;
}
@Injectable()
export class AuthorityNoticesService {
constructor(
@@ -178,6 +189,10 @@ export class AuthorityNoticesService {
actingUserId: string,
): Promise<AuthorityNotice> {
assertYearFits(dto.kind, dto.year);
const futureError = futureSentOnError(dto.sentOn);
if (futureError) {
throw futureError;
}
const input = {
kind: dto.kind,
year: dto.year,
@@ -234,6 +249,10 @@ export class AuthorityNoticesService {
dto: MarkSentDto,
actingUserId: string,
): Promise<AuthorityNotice> {
const futureError = futureSentOnError(dto.sentOn);
if (futureError) {
return Promise.reject(futureError);
}
return this.transition(id, actingUserId, { mutex: true }, (_tx, before) => {
if (before.status !== 'DRAFT') {
return Promise.reject(
@@ -290,6 +309,10 @@ export class AuthorityNoticesService {
): Promise<AuthorityNotice> {
// A new sent day can change which notice is the baseline: the mutex
// comes first, in lock order, whenever the sent day is touched.
const futureError = futureSentOnError(dto.sentOn);
if (futureError) {
return Promise.reject(futureError);
}
const mutex = dto.sentOn !== undefined;
return this.transition(id, actingUserId, { mutex }, (_tx, before) => {
if (before.status === 'CANCELLED') {