2026-05-05 19:50:07 +03:30
|
|
|
import {
|
|
|
|
|
Body,
|
|
|
|
|
Controller,
|
2026-05-06 18:55:42 +03:30
|
|
|
Delete,
|
2026-05-05 19:50:07 +03:30
|
|
|
Get,
|
2026-05-06 18:55:42 +03:30
|
|
|
Param,
|
|
|
|
|
Patch,
|
2026-05-05 19:50:07 +03:30
|
|
|
Post,
|
|
|
|
|
Query,
|
|
|
|
|
Req,
|
|
|
|
|
UseGuards,
|
|
|
|
|
} from '@nestjs/common';
|
|
|
|
|
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
|
|
|
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
|
|
|
import { AcceptOrganizationInviteDto } from './dto/accept-organization-invite.dto';
|
2026-05-17 13:13:59 +03:30
|
|
|
import { CreateConnectionRequestDto } from './dto/create-connection-request.dto';
|
2026-05-05 19:50:07 +03:30
|
|
|
import { InviteOrganizationDto } from './dto/invite-organization.dto';
|
|
|
|
|
import { PreviewOrganizationInviteDto } from './dto/preview-organization-invite.dto';
|
2026-05-17 13:13:59 +03:30
|
|
|
import { RespondConnectionRequestDto } from './dto/respond-connection-request.dto';
|
2026-05-05 19:50:07 +03:30
|
|
|
import { OrganizationService } from './organization.service';
|
2026-06-28 23:19:33 +03:30
|
|
|
import { ListLabCasesDto } from '../cases/dto/cases.dto';
|
2026-05-05 19:50:07 +03:30
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
/**
|
|
|
|
|
* Counterpart orgs (clinic↔lab).
|
|
|
|
|
*
|
|
|
|
|
* - `/connections` — OrganizationLink rows (connection requests + links created by invites).
|
|
|
|
|
* - `/invite`, `/invitations/*` — signup invitation tokens (orgs not yet on DyoLink).
|
|
|
|
|
*/
|
2026-05-05 19:50:07 +03:30
|
|
|
@ApiTags('organizations')
|
|
|
|
|
@ApiBearerAuth('JWT-auth')
|
|
|
|
|
@Controller('organizations')
|
|
|
|
|
export class OrganizationController {
|
|
|
|
|
constructor(private readonly organizationService: OrganizationService) {}
|
|
|
|
|
|
|
|
|
|
@Get('invitations/preview')
|
|
|
|
|
@ApiOperation({ summary: 'Preview organization invite by token (public)' })
|
|
|
|
|
previewInvite(@Query() query: PreviewOrganizationInviteDto) {
|
|
|
|
|
return this.organizationService.previewInvite(query.token);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-06 18:55:42 +03:30
|
|
|
@Get('invitations')
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiOperation({ summary: 'List invitation history for current organization' })
|
|
|
|
|
listInvitations(@Req() req: { user: { id: string; organizationId?: string } }) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.listInvitationHistory(req.user.id, organizationId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 19:50:07 +03:30
|
|
|
@Post('invitations/accept')
|
|
|
|
|
@ApiOperation({ summary: 'Accept organization invite and create/link counterpart org (public)' })
|
|
|
|
|
acceptInvite(@Body() dto: AcceptOrganizationInviteDto) {
|
|
|
|
|
return this.organizationService.acceptInvite(dto);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Get('search')
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiOperation({
|
|
|
|
|
summary: 'Search counterpart organizations (active subscription only)',
|
|
|
|
|
})
|
|
|
|
|
search(
|
|
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
|
|
|
|
@Query('q') q = '',
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.searchCounterpartOrganizations(req.user.id, organizationId, q);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 13:24:15 +03:30
|
|
|
@Get('connections/pending-count')
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiOperation({ summary: 'Count incoming pending connection requests for sidebar badge' })
|
|
|
|
|
countPendingConnections(@Req() req: { user: { id: string; organizationId?: string } }) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.countIncomingPendingConnections(req.user.id, organizationId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
@Get('connections')
|
2026-05-05 19:50:07 +03:30
|
|
|
@UseGuards(JwtAuthGuard)
|
2026-05-17 13:13:59 +03:30
|
|
|
@ApiOperation({ summary: 'List counterpart connections for current organization' })
|
|
|
|
|
listConnections(@Req() req: { user: { id: string; organizationId?: string } }) {
|
2026-05-05 19:50:07 +03:30
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.list(req.user.id, organizationId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
@Post('connections')
|
2026-05-05 19:50:07 +03:30
|
|
|
@UseGuards(JwtAuthGuard)
|
2026-05-17 13:13:59 +03:30
|
|
|
@ApiOperation({
|
|
|
|
|
summary: 'Create pending connection request to an existing subscribed counterpart org',
|
|
|
|
|
})
|
|
|
|
|
createConnectionRequest(
|
2026-05-05 19:50:07 +03:30
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
2026-05-17 13:13:59 +03:30
|
|
|
@Body() dto: CreateConnectionRequestDto,
|
2026-05-05 19:50:07 +03:30
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
2026-05-17 13:13:59 +03:30
|
|
|
return this.organizationService.createConnectionRequest(req.user.id, organizationId, dto);
|
2026-05-05 19:50:07 +03:30
|
|
|
}
|
|
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
@Patch('connections/:connectionId/respond')
|
2026-05-06 18:55:42 +03:30
|
|
|
@UseGuards(JwtAuthGuard)
|
2026-05-17 13:13:59 +03:30
|
|
|
@ApiOperation({ summary: 'Accept or reject a pending connection request for current organization' })
|
|
|
|
|
respondToConnectionRequest(
|
2026-05-06 18:55:42 +03:30
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
2026-05-17 13:13:59 +03:30
|
|
|
@Param('connectionId') connectionId: string,
|
|
|
|
|
@Body() dto: RespondConnectionRequestDto,
|
2026-05-06 18:55:42 +03:30
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
2026-05-17 13:13:59 +03:30
|
|
|
return this.organizationService.respondToConnectionRequest(
|
|
|
|
|
req.user.id,
|
|
|
|
|
organizationId,
|
|
|
|
|
connectionId,
|
|
|
|
|
dto,
|
|
|
|
|
);
|
2026-05-06 18:55:42 +03:30
|
|
|
}
|
|
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
@Delete('connections/:connectionId')
|
2026-05-06 18:55:42 +03:30
|
|
|
@UseGuards(JwtAuthGuard)
|
2026-05-17 13:13:59 +03:30
|
|
|
@ApiOperation({ summary: 'Remove an active connection' })
|
|
|
|
|
deleteConnection(
|
2026-05-06 18:55:42 +03:30
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
2026-05-17 13:13:59 +03:30
|
|
|
@Param('connectionId') connectionId: string,
|
2026-05-06 18:55:42 +03:30
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
2026-05-17 13:13:59 +03:30
|
|
|
return this.organizationService.deleteConnection(req.user.id, organizationId, connectionId);
|
2026-05-06 18:55:42 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-28 23:19:33 +03:30
|
|
|
@Get('connections/:connectionId/cases')
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiOperation({ summary: 'List cases exchanged with a connected organization' })
|
|
|
|
|
listConnectionCases(
|
|
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
|
|
|
|
@Param('connectionId') connectionId: string,
|
|
|
|
|
@Query() query: ListLabCasesDto,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.listConnectionCases(
|
|
|
|
|
req.user.id,
|
|
|
|
|
organizationId,
|
|
|
|
|
connectionId,
|
|
|
|
|
query,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Get('connections/:connectionId/cases/:caseId')
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiOperation({ summary: 'Get one case exchanged with a connected organization' })
|
|
|
|
|
getConnectionCase(
|
|
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
|
|
|
|
@Param('connectionId') connectionId: string,
|
|
|
|
|
@Param('caseId') caseId: string,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.getConnectionCase(
|
|
|
|
|
req.user.id,
|
|
|
|
|
organizationId,
|
|
|
|
|
connectionId,
|
|
|
|
|
caseId,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-06 18:55:42 +03:30
|
|
|
@Post('invitations/:invitationId/link')
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiOperation({ summary: 'Get a shareable invite link for a pending invitation' })
|
|
|
|
|
getInvitationLink(
|
|
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
|
|
|
|
@Param('invitationId') invitationId: string,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.getInvitationLink(req.user.id, organizationId, invitationId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 19:50:07 +03:30
|
|
|
@Post('invite')
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiOperation({ summary: 'Create invite link for owner of not-yet-subscribed counterpart org' })
|
|
|
|
|
inviteOrganization(
|
|
|
|
|
@Req() req: { user: { id: string; organizationId?: string } },
|
|
|
|
|
@Body() dto: InviteOrganizationDto,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.organizationService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.organizationService.inviteOrganization(req.user.id, organizationId, dto);
|
|
|
|
|
}
|
|
|
|
|
}
|