Some functionality added to dashboard gadgets.

This commit is contained in:
2026-07-12 00:28:43 +03:30
parent 83f6003a2b
commit d79aab907c
21 changed files with 557 additions and 412 deletions

View File

@@ -38,13 +38,12 @@ type CaseCompletionChart = {
type TodayCharts = {
treatmentMixWeek?: ChartBucket[];
tasksByWorkflowStep?: ChartBucket[];
tasksByProsthesis?: ChartBucket[];
appointmentsByProvider?: ChartBucket[];
caseCompletion?: CaseCompletionChart;
appointmentsWeekAll?: ChartBucket[];
appointmentsWeekMine?: ChartBucket[];
labTaskActivityWeek?: StackedDayBucket[];
inProgressTasksByProsthesis?: ChartBucket[];
efficiencyReport?: ChartBucket[];
};
@@ -76,7 +75,6 @@ type TodayWidgets = {
appointmentsToday?: { count: number };
patientsToday?: { count: number };
treatmentsToday?: { count: number };
draftTreatments?: { count: number };
labCasesPendingSend?: { count: number };
casesReceivedToday?: { count: number };
casesInProgress?: { count: number };
@@ -144,11 +142,22 @@ export class TodayService {
tasks.push(
this.loadTreatmentsToday(organizationId, from, to, widgets),
);
tasks.push(this.loadDraftTreatments(organizationId, widgets));
tasks.push(this.loadLabCasesPendingSend(organizationId, widgets));
tasks.push(
this.loadTreatmentMixWeek(organizationId, to, locale, charts),
);
}
if (this.canViewMyAppointmentsWeekChart(membership.isOwner, permissionNames)) {
tasks.push(
this.loadUpcomingAppointmentsToday(
organizationId,
userId,
from,
to,
actions,
),
);
tasks.push(
this.loadAppointmentsWeekMine(
organizationId,
@@ -158,15 +167,6 @@ export class TodayService {
charts,
),
);
tasks.push(
this.loadUpcomingAppointmentsToday(
organizationId,
userId,
from,
to,
actions,
),
);
}
if (
@@ -201,7 +201,7 @@ export class TodayService {
if (this.canViewTasks(membership.isOwner, permissionNames)) {
tasks.push(this.loadTasksInProgress(organizationId, widgets));
tasks.push(this.loadImportantTasks(organizationId, widgets));
tasks.push(this.loadTasksByWorkflowStep(organizationId, charts));
tasks.push(this.loadTasksByProsthesis(organizationId, locale, charts));
}
if (canViewLabWork) {
@@ -213,9 +213,6 @@ export class TodayService {
charts,
),
);
tasks.push(
this.loadInProgressTasksByProsthesis(organizationId, locale, charts),
);
}
}
@@ -305,12 +302,13 @@ export class TodayService {
}));
}
private async loadTasksByWorkflowStep(
private async loadTasksByProsthesis(
labOrganizationId: string,
locale: CatalogLocale,
charts: TodayCharts,
) {
const grouped = await this.prisma.labCaseTask.groupBy({
by: ['workflowStepCode', 'stepLabel'],
by: ['prosthesisTypeCode'],
where: {
status: LabTaskStatus.IN_PROGRESS,
labCase: {
@@ -321,14 +319,29 @@ export class TodayService {
_count: { _all: true },
});
charts.tasksByWorkflowStep = grouped
const sorted = grouped
.map((row) => ({
code: row.workflowStepCode,
label: row.stepLabel,
code: row.prosthesisTypeCode,
count: aggregateCount(row._count),
}))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
.sort((a, b) => b.count - a.count);
if (sorted.length === 0) {
charts.tasksByProsthesis = [];
return;
}
const labels = await this.catalogLabels.resolveLabels(
CatalogEntityKind.PROSTHESIS_TYPE,
sorted.map((row) => row.code),
locale,
);
charts.tasksByProsthesis = sorted.map((row) => ({
code: row.code,
label: labels.get(row.code) ?? row.code,
count: row.count,
}));
}
private resolveDayRange(query: TodaySummaryQueryDto): { from: Date; to: Date } {
@@ -432,16 +445,6 @@ export class TodayService {
widgets.treatmentsToday = { count };
}
private async loadDraftTreatments(organizationId: string, widgets: TodayWidgets) {
const count = await this.prisma.treatment.count({
where: {
organizationId,
details: { none: {} },
},
});
widgets.draftTreatments = { count };
}
private async loadLabCasesPendingSend(organizationId: string, widgets: TodayWidgets) {
const count = await this.prisma.labCase.count({
where: {
@@ -983,48 +986,6 @@ export class TodayService {
}));
}
private async loadInProgressTasksByProsthesis(
labOrganizationId: string,
locale: CatalogLocale,
charts: TodayCharts,
) {
const grouped = await this.prisma.labCaseTask.groupBy({
by: ['prosthesisTypeCode'],
where: {
status: LabTaskStatus.IN_PROGRESS,
labCase: {
sentAt: { not: null },
sends: { some: { organizationId: labOrganizationId } },
},
},
_count: { _all: true },
});
const sorted = grouped
.map((row) => ({
code: row.prosthesisTypeCode,
count: aggregateCount(row._count),
}))
.sort((a, b) => b.count - a.count);
if (sorted.length === 0) {
charts.inProgressTasksByProsthesis = [];
return;
}
const labels = await this.catalogLabels.resolveLabels(
CatalogEntityKind.PROSTHESIS_TYPE,
sorted.map((row) => row.code),
locale,
);
charts.inProgressTasksByProsthesis = sorted.map((row) => ({
code: row.code,
label: labels.get(row.code) ?? row.code,
count: row.count,
}));
}
private getRequesterOrganizationId(sharedDataTypes: unknown): string | null {
if (!sharedDataTypes || typeof sharedDataTypes !== 'object') {
return null;
@@ -1106,6 +1067,11 @@ export class TodayService {
);
}
private canViewMyAppointmentsWeekChart(isOwner: boolean, names: string[]): boolean {
if (isOwner) return false;
return names.includes('TAB_TREATMENT_EDIT');
}
private canViewCases(isOwner: boolean, names: string[]): boolean {
if (isOwner) return true;
return names.some((p) =>