improvement: treatments UI/UX updated again to minimize clicking and scrolling.

This commit is contained in:
2026-08-19 23:59:37 +03:30
parent 29c639f5a1
commit d2f07c0ed3
33 changed files with 1651 additions and 744 deletions

View File

@@ -1,6 +1,7 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
@@ -92,6 +93,13 @@ export class CasesController {
return this.casesService.startInternal(id, organizationId, req.user.id, req.user.language);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete an unstarted lab-origin draft (TAB_CASES_EDIT)' })
deleteDraft(@Param('id') id: string, @Req() req) {
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
return this.casesService.deleteInternalDraft(id, organizationId, req.user.id);
}
@Post(':id/lines/:lineClientKey/attachments')
@ApiOperation({ summary: 'Upload attachments for a lab-origin draft line' })
@ApiConsumes('multipart/form-data')

View File

@@ -4,7 +4,7 @@ import {
} from '@nestjs/common';
import { AppException, ErrorCode } from '../../common/errors';
import { randomUUID } from 'crypto';
import { createReadStream, existsSync, mkdirSync } from 'fs';
import { createReadStream, existsSync, mkdirSync, unlinkSync } from 'fs';
import { join } from 'path';
import { CatalogEntityKind, LabCaseActivityType, LabCaseOrigin, LabTaskStatus, LinkStatus, Prisma, UserNotificationType } from '@prisma/client';
import { PrismaService } from '../../../prisma/prisma.service';
@@ -565,6 +565,56 @@ export class CasesService {
return { success: true, data: await this.mapLabCaseDetail(refreshed, localeInput) };
}
async deleteInternalDraft(
labCaseId: string,
labOrganizationId: string,
actorUserId: string,
) {
await this.assertCanEditCases(actorUserId, labOrganizationId);
const existing = await this.prisma.labCase.findFirst({
where: {
id: labCaseId,
origin: LabCaseOrigin.LAB_INTERNAL,
destinationOrganizationId: labOrganizationId,
},
select: {
id: true,
startedAt: true,
attachments: { include: { attachment: { select: { id: true, storagePath: true } } } },
},
});
if (!existing) {
throw new AppException(ErrorCode.CASE_NOT_FOUND, HttpStatus.NOT_FOUND);
}
if (existing.startedAt) {
throw new AppException(ErrorCode.LAB_CASE_ALREADY_STARTED, HttpStatus.CONFLICT);
}
const orphanAttachments = existing.attachments.map((row) => row.attachment);
const orphanIds = orphanAttachments.map((row) => row.id);
await this.prisma.$transaction(async (tx) => {
await tx.labCase.delete({ where: { id: labCaseId } });
if (orphanIds.length > 0) {
await tx.treatmentDetailAttachment.deleteMany({
where: { id: { in: orphanIds }, detailId: null },
});
}
});
for (const attachment of orphanAttachments) {
try {
if (attachment.storagePath && existsSync(attachment.storagePath)) {
unlinkSync(attachment.storagePath);
}
} catch {
// Best-effort disk cleanup; the draft row is already gone.
}
}
return { success: true };
}
async uploadInternalAttachments(
labCaseId: string,
lineClientKey: string,

View File

@@ -1,5 +1,4 @@
import {
ArrayMinSize,
IsArray,
IsBoolean,
IsDateString,
@@ -61,7 +60,6 @@ export class SaveTreatmentDetailDto {
export class SaveTreatmentDraftDto {
@IsArray()
@ArrayMinSize(1)
@ValidateNested({ each: true })
@Type(() => SaveTreatmentDetailDto)
details: SaveTreatmentDetailDto[];