improvement: icons added to prosthesis types catalog.
Some checks failed
Production — tag build, push, deploy / build-and-push (push) Failing after 23s
Production — tag build, push, deploy / deploy (push) Has been skipped

This commit is contained in:
2026-09-05 01:29:53 +03:30
parent 4d7a4b390f
commit 5d3597f973
83 changed files with 1146 additions and 187 deletions

View File

@@ -7,7 +7,10 @@ import { LabCaseActivityType, LabTaskStatus, LinkStatus, Prisma, UserNotificatio
import { createReadStream, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import { randomUUID } from 'crypto';
import { generateLabCaseAccessToken } from '../../common/lab-case-access-token';
import {
buildLabCaseShareUrl,
generateLabCaseAccessToken,
} from '../../common/lab-case-access-token';
import { PrismaService } from '../../../prisma/prisma.service';
import { generateLabCaseTasks } from '../cases/lab-case-task.generator';
import { ProsthesisCatalogService } from '../prosthesis-catalog/prosthesis-catalog.service';
@@ -477,6 +480,7 @@ export class TreatmentsService {
appointmentId: string,
organizationId: string,
actorUserId: string,
actorLanguage?: string | null,
) {
await this.assertCanReadTreatment(actorUserId, organizationId);
const appointment = await this.ensureAppointmentProvider(
@@ -493,16 +497,25 @@ export class TreatmentsService {
include: treatmentInclude,
});
return { success: true, data: treatment ? this.mapTreatment(treatment) : null };
if (treatment) {
await this.ensureSentLabCaseAccessTokens(treatment.labCases);
}
return {
success: true,
data: treatment ? this.mapTreatment(treatment, actorLanguage) : null,
};
}
async getDraftForTreatment(
treatmentId: string,
organizationId: string,
actorUserId: string,
actorLanguage?: string | null,
) {
const treatment = await this.ensureTreatmentProvider(treatmentId, organizationId, actorUserId);
return { success: true, data: this.mapTreatment(treatment) };
await this.ensureSentLabCaseAccessTokens(treatment.labCases);
return { success: true, data: this.mapTreatment(treatment, actorLanguage) };
}
async saveDraftForAppointment(
@@ -510,6 +523,7 @@ export class TreatmentsService {
dto: SaveTreatmentDraftDto,
organizationId: string,
actorUserId: string,
actorLanguage?: string | null,
) {
await this.assertCanEditTreatment(actorUserId, organizationId);
const appointment = await this.ensureAppointmentProvider(
@@ -549,7 +563,7 @@ export class TreatmentsService {
patientId: appointment.patientId,
});
return { success: true, data: this.mapTreatment(saved) };
return { success: true, data: this.mapTreatment(saved, actorLanguage) };
}
async saveDraftForTreatment(
@@ -557,6 +571,7 @@ export class TreatmentsService {
dto: SaveTreatmentDraftDto,
organizationId: string,
actorUserId: string,
actorLanguage?: string | null,
) {
await this.assertCanEditTreatment(actorUserId, organizationId);
const existing = await this.ensureTreatmentProvider(treatmentId, organizationId, actorUserId);
@@ -572,7 +587,7 @@ export class TreatmentsService {
patientId: existing.patientId,
});
return { success: true, data: this.mapTreatment(saved) };
return { success: true, data: this.mapTreatment(saved, actorLanguage) };
}
private async persistDraftDetails(args: {
@@ -719,6 +734,7 @@ export class TreatmentsService {
dto: SaveTreatmentLabCasesDto,
organizationId: string,
actorUserId: string,
actorLanguage?: string | null,
) {
await this.assertCanEditTreatment(actorUserId, organizationId);
const appointment = await this.ensureAppointmentProvider(
@@ -736,7 +752,13 @@ export class TreatmentsService {
throw new AppException(ErrorCode.TREATMENT_SAVE_DETAILS_BEFORE_LAB, HttpStatus.NOT_FOUND);
}
return this.saveLabCasesForTreatment(treatment.id, dto, organizationId, actorUserId);
return this.saveLabCasesForTreatment(
treatment.id,
dto,
organizationId,
actorUserId,
actorLanguage,
);
}
async saveLabCasesForTreatment(
@@ -744,6 +766,7 @@ export class TreatmentsService {
dto: SaveTreatmentLabCasesDto,
organizationId: string,
actorUserId: string,
actorLanguage?: string | null,
) {
await this.assertCanEditTreatment(actorUserId, organizationId);
await this.ensureTreatmentProvider(treatmentId, organizationId, actorUserId);
@@ -907,7 +930,7 @@ export class TreatmentsService {
});
});
return { success: true, data: this.mapTreatment(saved) };
return { success: true, data: this.mapTreatment(saved, actorLanguage) };
}
async sendLabCase(
@@ -1049,7 +1072,7 @@ export class TreatmentsService {
},
});
return { success: true, data: this.mapLabCase(refreshed) };
return { success: true, data: this.mapLabCase(refreshed, actorLanguage) };
}
async updateLabCaseDueDate(
@@ -1411,7 +1434,7 @@ export class TreatmentsService {
organization: { id: string; name: string };
}>;
}>;
}) {
}, locale?: string | null) {
const documents = treatment.details.flatMap((d) =>
d.attachments.map((a) => this.mapAttachment(a)),
);
@@ -1434,7 +1457,7 @@ export class TreatmentsService {
}
: null,
details: treatment.details.map((d) => this.mapDetail(d)),
labCases: treatment.labCases.map((lc) => this.mapLabCase(lc)),
labCases: treatment.labCases.map((lc) => this.mapLabCase(lc, locale)),
documents,
};
}
@@ -1497,6 +1520,7 @@ export class TreatmentsService {
destinationOrganizationId?: string | null;
sentAt?: Date | null;
dueDate?: Date | null;
accessToken?: string | null;
details?: Array<{
treatmentDetailId: string;
detail?: {
@@ -1528,7 +1552,7 @@ export class TreatmentsService {
};
}>;
tasks?: Array<{ id: string; status: LabTaskStatus }>;
}) {
}, locale?: string | null) {
const taskProgress = this.mapTaskProgress(lc.tasks ?? []);
const detailTeeth = lc.details?.[0]?.detail
? normalizeTeeth(lc.details[0].detail.teeth)
@@ -1572,9 +1596,32 @@ export class TreatmentsService {
organizationName: s.organization?.name ?? 'Unknown organization',
sentAt: s.sentAt.toISOString(),
})) ?? [],
shareUrl: this.labCaseShareUrl(lc, locale),
};
}
private labCaseShareUrl(
lc: { sentAt?: Date | null; accessToken?: string | null },
locale?: string | null,
): string | null {
if (!lc.sentAt || !lc.accessToken) return null;
return buildLabCaseShareUrl(lc.accessToken, locale ?? 'en');
}
private async ensureSentLabCaseAccessTokens(
labCases: Array<{ id: string; sentAt: Date | null; accessToken: string | null }>,
) {
for (const lc of labCases) {
if (!lc.sentAt || lc.accessToken) continue;
const accessToken = generateLabCaseAccessToken();
await this.prisma.labCase.update({
where: { id: lc.id },
data: { accessToken },
});
lc.accessToken = accessToken;
}
}
private mapTaskProgress(tasks: Array<{ status: LabTaskStatus }>) {
const total = tasks.length;
const completed = tasks.filter((task) => task.status === LabTaskStatus.COMPLETED).length;