feature: phase1 - org-type navigation, Cases permissions, staff filtering, and route guards.
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { AppointmentsService } from './appointments.service';
|
||||
import { ColumnProvidersQueryDto } from './dto/column-providers-query.dto';
|
||||
@@ -20,7 +21,7 @@ import { UpdateAppointmentDto } from './dto/update-appointment.dto';
|
||||
|
||||
@ApiTags('appointments')
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(JwtAuthGuard, ClinicOrgGuard)
|
||||
@Controller('appointments')
|
||||
export class AppointmentsController {
|
||||
constructor(private readonly appointmentsService: AppointmentsService) {}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
|
||||
import { StaffModule } from '../staff/staff.module';
|
||||
import { AppointmentsController } from './appointments.controller';
|
||||
import { AppointmentsService } from './appointments.service';
|
||||
@@ -7,6 +8,6 @@ import { AppointmentsService } from './appointments.service';
|
||||
@Module({
|
||||
imports: [StaffModule],
|
||||
controllers: [AppointmentsController],
|
||||
providers: [AppointmentsService, PrismaService],
|
||||
providers: [AppointmentsService, PrismaService, ClinicOrgGuard],
|
||||
})
|
||||
export class AppointmentsModule {}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
UpdateLanguageDto,
|
||||
} from './dto/update-language.dto';
|
||||
import { JwtPayload } from './interfaces/jwt-payload.interface';
|
||||
import { ownerPermissionsForOrgType, type OrganizationTypeName } from '../../common/organization-type';
|
||||
|
||||
const ALL_PERMISSIONS = [
|
||||
'TAB_TODAY_READ',
|
||||
@@ -34,6 +35,8 @@ const ALL_PERMISSIONS = [
|
||||
'TAB_APPOINTMENTS_EDIT',
|
||||
'TAB_TREATMENT_READ',
|
||||
'TAB_TREATMENT_EDIT',
|
||||
'TAB_CASES_READ',
|
||||
'TAB_CASES_EDIT',
|
||||
'TAB_BILLING_READ',
|
||||
'TAB_BILLING_EDIT',
|
||||
'TAB_REPORTS_READ',
|
||||
@@ -806,11 +809,15 @@ export class AuthService {
|
||||
isOwner: boolean;
|
||||
organization: {
|
||||
plan?: { name: string; maxUsers: number; price: number } | null;
|
||||
type?: { name: string };
|
||||
};
|
||||
permissions?: Array<{ permission: { name: string } }>;
|
||||
}): string[] {
|
||||
if (membership.isOwner) {
|
||||
return membership.organization.plan ? ALL_PERMISSIONS : READ_ONLY_PERMISSIONS;
|
||||
const orgType = (membership.organization.type?.name === 'LAB'
|
||||
? 'LAB'
|
||||
: 'CLINIC') as OrganizationTypeName;
|
||||
return ownerPermissionsForOrgType(orgType, Boolean(membership.organization.plan));
|
||||
}
|
||||
return membership.permissions?.map((p) => p.permission.name) || [];
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { CreatePatientDto } from './dto/create-patient.dto';
|
||||
import { ListPatientsDto } from './dto/list-patients.dto';
|
||||
@@ -18,7 +19,7 @@ import { PatientsService } from './patients.service';
|
||||
|
||||
@ApiTags('patients')
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(JwtAuthGuard, ClinicOrgGuard)
|
||||
@Controller('patients')
|
||||
export class PatientsController {
|
||||
constructor(private readonly patientsService: PatientsService) {}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
|
||||
import { PatientsController } from './patients.controller';
|
||||
import { PatientsService } from './patients.service';
|
||||
|
||||
@Module({
|
||||
controllers: [PatientsController],
|
||||
providers: [PatientsService, PrismaService],
|
||||
providers: [PatientsService, PrismaService, ClinicOrgGuard],
|
||||
})
|
||||
export class PatientsModule {}
|
||||
|
||||
@@ -11,6 +11,10 @@ import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { AcceptStaffInviteDto } from './dto/accept-staff-invite.dto';
|
||||
import { isUnlimitedSeats, normalizeTabPermissions } from '../../common/permissions';
|
||||
import {
|
||||
filterPermissionsForOrgType,
|
||||
getOrganizationTypeName,
|
||||
} from '../../common/organization-type';
|
||||
import { InviteStaffDto } from './dto/invite-staff.dto';
|
||||
import { UpdateStaffMemberDto } from './dto/update-staff-member.dto';
|
||||
|
||||
@@ -96,7 +100,8 @@ export class StaffService {
|
||||
}
|
||||
|
||||
const email = dto.email.trim().toLowerCase();
|
||||
const normalizedPerms = normalizeTabPermissions(dto.permissionNames);
|
||||
const orgType = await getOrganizationTypeName(this.prisma, organizationId);
|
||||
const normalizedPerms = filterPermissionsForOrgType(dto.permissionNames, orgType);
|
||||
|
||||
const permissionRows = await this.prisma.permission.findMany({
|
||||
where: { name: { in: normalizedPerms } },
|
||||
@@ -371,7 +376,8 @@ export class StaffService {
|
||||
}
|
||||
|
||||
if (dto.permissionNames !== undefined) {
|
||||
const normalizedPerms = normalizeTabPermissions(dto.permissionNames);
|
||||
const orgType = await getOrganizationTypeName(this.prisma, organizationId);
|
||||
const normalizedPerms = filterPermissionsForOrgType(dto.permissionNames, orgType);
|
||||
const permissionRows = await this.prisma.permission.findMany({
|
||||
where: { name: { in: normalizedPerms } },
|
||||
select: { id: true, name: true },
|
||||
|
||||
@@ -17,13 +17,14 @@ import { FilesInterceptor } from '@nestjs/platform-express';
|
||||
import { ApiBearerAuth, ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { memoryStorage } from 'multer';
|
||||
import type { Response } from 'express';
|
||||
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { SaveTreatmentDraftDto, SendTreatmentCaseDto } from './dto/treatment.dto';
|
||||
import { TreatmentsService } from './treatments.service';
|
||||
|
||||
@ApiTags('treatments')
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseGuards(JwtAuthGuard, ClinicOrgGuard)
|
||||
@Controller('treatments')
|
||||
export class TreatmentsController {
|
||||
constructor(private readonly treatmentsService: TreatmentsService) {}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
|
||||
import { TreatmentsController } from './treatments.controller';
|
||||
import { TreatmentsService } from './treatments.service';
|
||||
|
||||
@Module({
|
||||
controllers: [TreatmentsController],
|
||||
providers: [TreatmentsService, PrismaService],
|
||||
providers: [TreatmentsService, PrismaService, ClinicOrgGuard],
|
||||
})
|
||||
export class TreatmentsModule {}
|
||||
|
||||
Reference in New Issue
Block a user