bugfix: appointment hours now use the client timezone on UTC servers.

Logical API errors throw stable codes so users see translated messages instead of a generic bad request.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-19 01:43:50 +03:30
parent d6958b2e48
commit 80167c622c
38 changed files with 833 additions and 392 deletions

View File

@@ -1,8 +1,8 @@
import {
BadRequestException,
ForbiddenException,
HttpStatus,
Injectable,
} from '@nestjs/common';
import { AppException, ErrorCode } from '../../common/errors';
import { LabTaskStatus, LinkStatus, CatalogEntityKind } from '@prisma/client';
import { PrismaService } from '../../../prisma/prisma.service';
import {
@@ -107,7 +107,7 @@ export class TodayService {
getOrganizationIdFromUser(user: { organizationId?: string }) {
if (!user?.organizationId) {
throw new BadRequestException('Organization is not selected');
throw new AppException(ErrorCode.AUTH_ORG_NOT_SELECTED, HttpStatus.BAD_REQUEST);
}
return user.organizationId;
}
@@ -428,10 +428,10 @@ export class TodayService {
const from = new Date(query.from);
const to = new Date(query.to);
if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) {
throw new BadRequestException('Invalid date range');
throw new AppException(ErrorCode.TODAY_INVALID_RANGE, HttpStatus.BAD_REQUEST);
}
if (to <= from) {
throw new BadRequestException('Range "to" must be after "from"');
throw new AppException(ErrorCode.TODAY_INVALID_RANGE_ORDER, HttpStatus.BAD_REQUEST);
}
return { from, to };
}
@@ -1324,7 +1324,7 @@ export class TodayService {
});
if (!membership) {
throw new ForbiddenException('You are not a member of this organization');
throw new AppException(ErrorCode.PERMISSION_NOT_MEMBER, HttpStatus.FORBIDDEN);
}
return membership;
@@ -1345,7 +1345,7 @@ export class TodayService {
private assertCanViewToday(isOwner: boolean, permissionNames: string[]) {
if (isOwner) return;
if (!permissionNames.includes('TAB_TODAY_READ')) {
throw new ForbiddenException('You do not have access to Today');
throw new AppException(ErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN);
}
}