2026-05-05 19:50:07 +03:30
|
|
|
import { apiClient } from './client';
|
|
|
|
|
|
|
|
|
|
export interface CounterpartSearchResultDto {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
email: string;
|
|
|
|
|
phone: string | null;
|
|
|
|
|
owner: { email: string; name: string };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CounterpartItemDto {
|
|
|
|
|
id: string;
|
2026-05-06 18:55:42 +03:30
|
|
|
requestedByOrganizationId: string | null;
|
2026-05-05 19:50:07 +03:30
|
|
|
counterpartOrganizationId: string | null;
|
|
|
|
|
organizationName: string;
|
|
|
|
|
ownerEmail: string;
|
|
|
|
|
phone: string | null;
|
|
|
|
|
status: 'PENDING' | 'ACTIVE' | 'REJECTED' | 'EXPIRED';
|
2026-05-06 18:55:42 +03:30
|
|
|
createdAt: string;
|
|
|
|
|
acceptedAt: string | null;
|
2026-05-17 13:13:59 +03:30
|
|
|
/**
|
|
|
|
|
* Set by GET /organizations/connections when this PENDING row came from inviteOrganization()
|
|
|
|
|
* (joined server-side). Lets the main table show copy-invite without opening history.
|
|
|
|
|
*/
|
|
|
|
|
pendingInvitationId?: string | null;
|
|
|
|
|
invitationStatus?: OrganizationInvitationHistoryItemDto['status'] | null;
|
2026-05-06 18:55:42 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface OrganizationInvitationHistoryItemDto {
|
|
|
|
|
id: string;
|
|
|
|
|
organizationName: string;
|
|
|
|
|
ownerEmail: string;
|
|
|
|
|
status: 'PENDING' | 'ACTIVE' | 'REJECTED' | 'EXPIRED';
|
2026-05-05 19:50:07 +03:30
|
|
|
createdAt: string;
|
|
|
|
|
acceptedAt: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const organizationApi = {
|
|
|
|
|
search: async (q: string): Promise<{ success: boolean; data: CounterpartSearchResultDto[] }> => {
|
|
|
|
|
const response = await apiClient.get(`/organizations/search?q=${encodeURIComponent(q)}`);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
list: async (): Promise<{ success: boolean; data: { items: CounterpartItemDto[] } }> => {
|
2026-05-17 13:13:59 +03:30
|
|
|
const response = await apiClient.get('/organizations/connections');
|
2026-05-05 19:50:07 +03:30
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-06 18:55:42 +03:30
|
|
|
listInvitations: async (): Promise<{
|
|
|
|
|
success: boolean;
|
|
|
|
|
data: { items: OrganizationInvitationHistoryItemDto[] };
|
|
|
|
|
}> => {
|
|
|
|
|
const response = await apiClient.get('/organizations/invitations');
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
createConnectionRequest: async (
|
2026-05-05 19:50:07 +03:30
|
|
|
targetOrganizationId: string,
|
|
|
|
|
): Promise<{ success: boolean; data: { id: string; status: 'PENDING' | 'ACTIVE' | 'REJECTED' } }> => {
|
2026-05-17 13:13:59 +03:30
|
|
|
const response = await apiClient.post('/organizations/connections', { targetOrganizationId });
|
2026-05-05 19:50:07 +03:30
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
respondToConnectionRequest: async (
|
|
|
|
|
connectionId: string,
|
2026-05-06 18:55:42 +03:30
|
|
|
action: 'ACCEPT' | 'REJECT',
|
|
|
|
|
): Promise<{ success: boolean; data: { id: string; status: 'PENDING' | 'ACTIVE' | 'REJECTED' } }> => {
|
2026-05-17 13:13:59 +03:30
|
|
|
const response = await apiClient.patch(`/organizations/connections/${connectionId}/respond`, {
|
|
|
|
|
action,
|
|
|
|
|
});
|
2026-05-06 18:55:42 +03:30
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-17 13:13:59 +03:30
|
|
|
deleteConnection: async (
|
|
|
|
|
connectionId: string,
|
|
|
|
|
): Promise<{ success: boolean; data: { id: string }; message: string }> => {
|
|
|
|
|
const response = await apiClient.delete(`/organizations/connections/${connectionId}`);
|
2026-05-06 18:55:42 +03:30
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-05 19:50:07 +03:30
|
|
|
invite: async (body: {
|
|
|
|
|
organizationName: string;
|
|
|
|
|
ownerEmail: string;
|
|
|
|
|
phone?: string;
|
|
|
|
|
}): Promise<{ success: boolean; data: { invitationId: string; invitationUrl: string; status: 'PENDING' } }> => {
|
|
|
|
|
const response = await apiClient.post('/organizations/invite', body);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-06 18:55:42 +03:30
|
|
|
getInvitationLink: async (
|
|
|
|
|
invitationId: string,
|
|
|
|
|
): Promise<{ success: boolean; data: { invitationId: string; invitationUrl: string } }> => {
|
|
|
|
|
const response = await apiClient.post(`/organizations/invitations/${invitationId}/link`);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-05 19:50:07 +03:30
|
|
|
previewInvite: async (
|
|
|
|
|
token: string,
|
|
|
|
|
): Promise<{
|
|
|
|
|
success: boolean;
|
|
|
|
|
data: {
|
|
|
|
|
ownerEmail: string;
|
|
|
|
|
organizationName: string;
|
|
|
|
|
organizationType: 'CLINIC' | 'LAB';
|
2026-05-17 02:03:18 +03:30
|
|
|
organizationEmail?: string;
|
2026-05-05 19:50:07 +03:30
|
|
|
inviterOrganizationName: string;
|
|
|
|
|
expiresAt: string;
|
|
|
|
|
status: 'PENDING' | 'ACCEPTED';
|
|
|
|
|
};
|
|
|
|
|
}> => {
|
|
|
|
|
const response = await apiClient.get(
|
|
|
|
|
`/organizations/invitations/preview?token=${encodeURIComponent(token)}`,
|
|
|
|
|
);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
acceptInvite: async (body: {
|
|
|
|
|
token: string;
|
|
|
|
|
organizationName: string;
|
2026-05-17 02:03:18 +03:30
|
|
|
organizationEmail: string;
|
|
|
|
|
organizationType: 'CLINIC' | 'LAB';
|
2026-05-05 19:50:07 +03:30
|
|
|
ownerName: string;
|
|
|
|
|
password: string;
|
|
|
|
|
}): Promise<{ success: boolean; message: string; data: { organizationId: string } }> => {
|
|
|
|
|
const response = await apiClient.post('/organizations/invitations/accept', body);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
};
|