improvement: tasks and cases feature updated based on the new prosthesis types and their steps. the whole assignment proccess removed from the flow.
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
import {
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { LabCaseCommentSide, Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { CreateLabCaseCommentDto } from './dto/lab-case-comment.dto';
|
||||
|
||||
const commentInclude = {
|
||||
authorUser: { select: { id: true, name: true } },
|
||||
authorOrganization: { select: { id: true, name: true } },
|
||||
} satisfies Prisma.LabCaseCommentInclude;
|
||||
|
||||
type CommentWithRelations = Prisma.LabCaseCommentGetPayload<{
|
||||
include: typeof commentInclude;
|
||||
}>;
|
||||
|
||||
@Injectable()
|
||||
export class LabCaseCommentsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
// ---------- Lab side (TAB_TASKS_EDIT) ----------
|
||||
|
||||
async listForLab(caseId: string, labOrganizationId: string, actorUserId: string) {
|
||||
await this.assertLabCanComment(caseId, labOrganizationId, actorUserId);
|
||||
const comments = await this.fetchComments(caseId);
|
||||
return { success: true, data: comments.map((c) => this.mapComment(c, LabCaseCommentSide.LAB)) };
|
||||
}
|
||||
|
||||
async addForLab(
|
||||
caseId: string,
|
||||
labOrganizationId: string,
|
||||
actorUserId: string,
|
||||
dto: CreateLabCaseCommentDto,
|
||||
) {
|
||||
await this.assertLabCanComment(caseId, labOrganizationId, actorUserId);
|
||||
const created = await this.prisma.labCaseComment.create({
|
||||
data: {
|
||||
labCaseId: caseId,
|
||||
authorUserId: actorUserId,
|
||||
authorOrganizationId: labOrganizationId,
|
||||
authorSide: LabCaseCommentSide.LAB,
|
||||
body: dto.body.trim(),
|
||||
visibleToClinic: dto.visibleToClinic ?? false,
|
||||
},
|
||||
include: commentInclude,
|
||||
});
|
||||
return { success: true, data: this.mapComment(created, LabCaseCommentSide.LAB) };
|
||||
}
|
||||
|
||||
async setVisibility(
|
||||
commentId: string,
|
||||
labOrganizationId: string,
|
||||
actorUserId: string,
|
||||
visibleToClinic: boolean,
|
||||
) {
|
||||
const comment = await this.prisma.labCaseComment.findUnique({
|
||||
where: { id: commentId },
|
||||
select: { id: true, labCaseId: true, authorSide: true },
|
||||
});
|
||||
if (!comment) {
|
||||
throw new NotFoundException('Comment not found');
|
||||
}
|
||||
await this.assertLabCanComment(comment.labCaseId, labOrganizationId, actorUserId);
|
||||
if (comment.authorSide !== LabCaseCommentSide.LAB) {
|
||||
throw new ForbiddenException('Only lab comments can change visibility');
|
||||
}
|
||||
const updated = await this.prisma.labCaseComment.update({
|
||||
where: { id: commentId },
|
||||
data: { visibleToClinic },
|
||||
include: commentInclude,
|
||||
});
|
||||
return { success: true, data: this.mapComment(updated, LabCaseCommentSide.LAB) };
|
||||
}
|
||||
|
||||
// ---------- Clinic side (connection access is validated by caller) ----------
|
||||
|
||||
async listForClinic(caseId: string, clinicOrganizationId: string) {
|
||||
await this.assertClinicOwnsCase(caseId, clinicOrganizationId);
|
||||
const comments = await this.fetchComments(caseId, { visibleOnly: true });
|
||||
return {
|
||||
success: true,
|
||||
data: comments.map((c) => this.mapComment(c, LabCaseCommentSide.CLINIC)),
|
||||
};
|
||||
}
|
||||
|
||||
async addForClinic(
|
||||
caseId: string,
|
||||
clinicOrganizationId: string,
|
||||
actorUserId: string,
|
||||
dto: CreateLabCaseCommentDto,
|
||||
) {
|
||||
await this.assertClinicOwnsCase(caseId, clinicOrganizationId);
|
||||
const created = await this.prisma.labCaseComment.create({
|
||||
data: {
|
||||
labCaseId: caseId,
|
||||
authorUserId: actorUserId,
|
||||
authorOrganizationId: clinicOrganizationId,
|
||||
authorSide: LabCaseCommentSide.CLINIC,
|
||||
body: dto.body.trim(),
|
||||
// Clinic-authored comments are inherently visible to the clinic.
|
||||
visibleToClinic: true,
|
||||
},
|
||||
include: commentInclude,
|
||||
});
|
||||
return { success: true, data: this.mapComment(created, LabCaseCommentSide.CLINIC) };
|
||||
}
|
||||
|
||||
// ---------- Helpers ----------
|
||||
|
||||
private fetchComments(caseId: string, opts?: { visibleOnly?: boolean }) {
|
||||
return this.prisma.labCaseComment.findMany({
|
||||
where: {
|
||||
labCaseId: caseId,
|
||||
...(opts?.visibleOnly ? { visibleToClinic: true } : {}),
|
||||
},
|
||||
include: commentInclude,
|
||||
orderBy: { createdAt: 'asc' },
|
||||
});
|
||||
}
|
||||
|
||||
private mapComment(comment: CommentWithRelations, viewerSide: LabCaseCommentSide) {
|
||||
return {
|
||||
id: comment.id,
|
||||
body: comment.body,
|
||||
authorSide: comment.authorSide,
|
||||
authorName: comment.authorUser?.name ?? null,
|
||||
authorOrganizationName: comment.authorOrganization?.name ?? null,
|
||||
visibleToClinic: comment.visibleToClinic,
|
||||
createdAt: comment.createdAt.toISOString(),
|
||||
// Only lab viewers can toggle visibility, and only on lab-authored comments.
|
||||
canToggleVisibility:
|
||||
viewerSide === LabCaseCommentSide.LAB &&
|
||||
comment.authorSide === LabCaseCommentSide.LAB,
|
||||
};
|
||||
}
|
||||
|
||||
private async assertLabCanComment(
|
||||
caseId: string,
|
||||
labOrganizationId: string,
|
||||
actorUserId: string,
|
||||
) {
|
||||
const labCase = await this.prisma.labCase.findFirst({
|
||||
where: {
|
||||
id: caseId,
|
||||
sentAt: { not: null },
|
||||
sends: { some: { organizationId: labOrganizationId } },
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (!labCase) {
|
||||
throw new NotFoundException('Case not found');
|
||||
}
|
||||
|
||||
const membership = await this.prisma.membership.findFirst({
|
||||
where: { userId: actorUserId, organizationId: labOrganizationId, isActive: true },
|
||||
include: { permissions: { include: { permission: true } } },
|
||||
});
|
||||
if (!membership) {
|
||||
throw new ForbiddenException('You are not a member of this organization');
|
||||
}
|
||||
if (membership.isOwner) return;
|
||||
const names = membership.permissions.map((p) => p.permission.name);
|
||||
if (!names.includes('TAB_TASKS_EDIT')) {
|
||||
throw new ForbiddenException('You do not have access to task comments');
|
||||
}
|
||||
}
|
||||
|
||||
private async assertClinicOwnsCase(caseId: string, clinicOrganizationId: string) {
|
||||
const labCase = await this.prisma.labCase.findFirst({
|
||||
where: {
|
||||
id: caseId,
|
||||
sentAt: { not: null },
|
||||
treatment: { organizationId: clinicOrganizationId },
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (!labCase) {
|
||||
throw new NotFoundException('Case not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user