improvement: some new gadgets added to dashboard. some new functionality added to existed gadgets.

This commit is contained in:
2026-07-14 03:06:56 +03:30
parent ec2b23f4b1
commit d383a4abc3
34 changed files with 944 additions and 191 deletions

View File

@@ -12,7 +12,7 @@ import {
CatalogLabelService,
normalizeCatalogLocale,
} from '../catalog/catalog-label.service';
import { TreatmentCatalogService } from '../treatment-catalog/treatment-catalog.service';
import { ProsthesisCatalogService } from '../prosthesis-catalog/prosthesis-catalog.service';
import { normalizeTeeth } from '../treatments/treatment.utils';
import { ListLabCasesDto, UpdateLabCaseImportantDto, AssignLabCaseTaskDto } from './dto/cases.dto';
import {
@@ -92,7 +92,7 @@ type LabCaseTaskWithRelations = Prisma.LabCaseTaskGetPayload<{
export class CasesService {
constructor(
private readonly prisma: PrismaService,
private readonly treatmentCatalog: TreatmentCatalogService,
private readonly prosthesisCatalog: ProsthesisCatalogService,
private readonly catalogLabels: CatalogLabelService,
private readonly labCaseActivity: LabCaseActivityService,
) {}
@@ -107,8 +107,8 @@ export class CasesService {
async list(labOrganizationId: string, actorUserId: string, query: ListLabCasesDto) {
await this.assertCanReadCases(actorUserId, labOrganizationId);
if (query.treatmentType) {
this.treatmentCatalog.assertKnownTreatmentType(query.treatmentType);
if (query.prosthesisTypeCode) {
this.prosthesisCatalog.assertKnownProsthesisType(query.prosthesisTypeCode);
}
const page = query.page ?? 1;
@@ -127,12 +127,7 @@ export class CasesService {
patient: { select: { id: true, firstName: true, lastName: true, mobile: true } },
},
},
details: {
include: {
detail: { select: { treatmentType: true } },
},
},
tasks: { select: { id: true, status: true } },
tasks: { select: { id: true, status: true, prosthesisTypeCode: true, teeth: true } },
},
orderBy: [{ sentAt: 'desc' }],
skip,
@@ -169,43 +164,48 @@ export class CasesService {
async listFilterOptions(labOrganizationId: string, actorUserId: string) {
await this.assertCanReadCases(actorUserId, labOrganizationId);
const rows = await this.prisma.labCase.findMany({
where: {
sentAt: { not: null },
sends: { some: { organizationId: labOrganizationId } },
},
select: {
treatment: {
select: {
organization: { select: { id: true, name: true } },
const [clinicRows, taskRows] = await Promise.all([
this.prisma.labCase.findMany({
where: {
sentAt: { not: null },
sends: { some: { organizationId: labOrganizationId } },
},
select: {
treatment: {
select: {
organization: { select: { id: true, name: true } },
},
},
},
details: {
select: { detail: { select: { treatmentType: true } } },
}),
this.prisma.labCaseTask.findMany({
where: {
labCase: {
sentAt: { not: null },
sends: { some: { organizationId: labOrganizationId } },
},
},
},
});
select: { prosthesisTypeCode: true },
distinct: ['prosthesisTypeCode'],
}),
]);
const clinicsById = new Map<string, { id: string; name: string }>();
const typeCodes = new Set<string>();
for (const row of rows) {
for (const row of clinicRows) {
clinicsById.set(row.treatment.organization.id, row.treatment.organization);
for (const link of row.details) {
typeCodes.add(link.detail.treatmentType);
}
}
const catalog = await this.treatmentCatalog.list();
const treatmentTypes = catalog
.filter((entry) => entry.labDependent && typeCodes.has(entry.code))
.map((entry) => ({ code: entry.code, labDependent: entry.labDependent }));
const typeCodes = new Set(taskRows.map((row) => row.prosthesisTypeCode));
const catalog = await this.prosthesisCatalog.list();
const prosthesisTypes = catalog
.filter((entry) => typeCodes.has(entry.code))
.map((entry) => ({ code: entry.code }));
return {
success: true,
data: {
clinics: [...clinicsById.values()].sort((a, b) => a.name.localeCompare(b.name)),
treatmentTypes,
prosthesisTypes,
},
};
}
@@ -216,8 +216,8 @@ export class CasesService {
labOrganizationId: string,
query: ListLabCasesDto,
) {
if (query.treatmentType) {
this.treatmentCatalog.assertKnownTreatmentType(query.treatmentType);
if (query.prosthesisTypeCode) {
this.prosthesisCatalog.assertKnownProsthesisType(query.prosthesisTypeCode);
}
const page = query.page ?? 1;
@@ -240,12 +240,7 @@ export class CasesService {
patient: { select: { id: true, firstName: true, lastName: true, mobile: true } },
},
},
details: {
include: {
detail: { select: { treatmentType: true } },
},
},
tasks: { select: { id: true, status: true } },
tasks: { select: { id: true, status: true, prosthesisTypeCode: true, teeth: true } },
},
orderBy: [{ sentAt: 'desc' }],
skip,
@@ -516,10 +511,10 @@ export class CasesService {
...(query.clinicOrganizationId
? { treatment: { organizationId: query.clinicOrganizationId } }
: {}),
...(query.treatmentType
...(query.prosthesisTypeCode
? {
details: {
some: { detail: { treatmentType: query.treatmentType } },
tasks: {
some: { prosthesisTypeCode: query.prosthesisTypeCode },
},
}
: {}),
@@ -565,10 +560,14 @@ export class CasesService {
organization: { id: string; name: string };
patient: { id: string; firstName: string; lastName: string; mobile: string };
};
details: Array<{ detail: { treatmentType: string } }>;
tasks: Array<{ id: string; status: LabTaskStatus }>;
tasks: Array<{
id: string;
status: LabTaskStatus;
prosthesisTypeCode: string;
teeth: Prisma.JsonValue;
}>;
}) {
const treatmentType = lc.details[0]?.detail.treatmentType ?? null;
const prosthesisGroups = this.buildProsthesisGroupsFromTasks(lc.tasks);
const completedTasks = lc.tasks.filter((t) => t.status === LabTaskStatus.COMPLETED).length;
return {
@@ -584,7 +583,7 @@ export class CasesService {
lastName: lc.treatment.patient.lastName,
mobile: lc.treatment.patient.mobile,
},
treatmentType,
prosthesisGroups,
taskProgress: {
completed: completedTasks,
total: lc.tasks.length,
@@ -651,6 +650,29 @@ export class CasesService {
};
}
private buildProsthesisGroupsFromTasks(
tasks: Array<{ prosthesisTypeCode: string; teeth: Prisma.JsonValue }>,
): Array<{ prosthesisTypeCode: string; teeth: string[] }> {
const prosthesisByCode = new Map<string, string[]>();
for (const task of tasks) {
if (!task.prosthesisTypeCode) continue;
const teeth = normalizeTaskTeeth(task.teeth);
const list = prosthesisByCode.get(task.prosthesisTypeCode) ?? [];
list.push(...teeth);
prosthesisByCode.set(task.prosthesisTypeCode, list);
}
return [...prosthesisByCode.entries()]
.map(([prosthesisTypeCode, teeth]) => ({
prosthesisTypeCode,
teeth: [...new Set(teeth)].sort((a, b) =>
a.localeCompare(b, undefined, { numeric: true }),
),
}))
.sort((a, b) => a.prosthesisTypeCode.localeCompare(b.prosthesisTypeCode));
}
private groupTasks(
tasks: LabCaseTaskWithRelations[],
prosthesisLabels: Map<string, string>,