feature: Phase3 - Task templates + generation on send

This commit is contained in:
2026-06-28 16:46:42 +03:30
parent 8b4ef6195d
commit 21f545ebdb
25 changed files with 1448 additions and 31 deletions

View File

@@ -0,0 +1,21 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { TreatmentCatalogService } from './treatment-catalog.service';
@ApiTags('treatment-catalog')
@ApiBearerAuth('JWT-auth')
@UseGuards(JwtAuthGuard)
@Controller('treatment-catalog')
export class TreatmentCatalogController {
constructor(private readonly treatmentCatalogService: TreatmentCatalogService) {}
@Get()
@ApiOperation({ summary: 'List treatment types from the catalog (data-driven)' })
list() {
return {
success: true,
data: this.treatmentCatalogService.list(),
};
}
}

View File

@@ -0,0 +1,12 @@
import { Global, Module } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import { TreatmentCatalogController } from './treatment-catalog.controller';
import { TreatmentCatalogService } from './treatment-catalog.service';
@Global()
@Module({
controllers: [TreatmentCatalogController],
providers: [TreatmentCatalogService, PrismaService],
exports: [TreatmentCatalogService],
})
export class TreatmentCatalogModule {}

View File

@@ -0,0 +1,69 @@
import { BadRequestException, Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
export type TreatmentTypeCatalogEntry = {
id: string;
code: string;
labDependent: boolean;
sortOrder: number;
};
@Injectable()
export class TreatmentCatalogService implements OnModuleInit {
private loaded = false;
private byCode = new Map<string, TreatmentTypeCatalogEntry>();
constructor(private readonly prisma: PrismaService) {}
async onModuleInit() {
await this.refresh();
}
async refresh(): Promise<void> {
const rows = await this.prisma.treatmentType.findMany({
orderBy: [{ sortOrder: 'asc' }, { code: 'asc' }],
select: { id: true, code: true, labDependent: true, sortOrder: true },
});
this.byCode = new Map(rows.map((row) => [row.code, row]));
this.loaded = true;
}
list(): TreatmentTypeCatalogEntry[] {
this.ensureLoaded();
return [...this.byCode.values()];
}
getByCode(code: string): TreatmentTypeCatalogEntry | undefined {
this.ensureLoaded();
return this.byCode.get(code);
}
assertKnownTreatmentType(code: string): TreatmentTypeCatalogEntry {
const entry = this.getByCode(code);
if (!entry) {
throw new BadRequestException(`Unknown treatment type: ${code}`);
}
return entry;
}
assertLabDependentTreatmentType(code: string): TreatmentTypeCatalogEntry {
const entry = this.assertKnownTreatmentType(code);
if (!entry.labDependent) {
throw new BadRequestException(
`Treatment type "${code}" is completed in the clinic and cannot be sent to a lab`,
);
}
return entry;
}
isLabDependent(code: string): boolean {
return this.getByCode(code)?.labDependent ?? false;
}
private ensureLoaded() {
if (!this.loaded) {
throw new Error('Treatment catalog is not loaded yet');
}
}
}