// Copyright: © 2026 TWWIM UG. All rights reserved. (www.twwim.com) /** * Members API client * * Thin wrappers around the customer-api Members endpoints. All routes are * gated to ADMIN role on the server; the dashboard's /members page is also * zone-gated client-side via requireZone(CPZone.MEMBERS). */ import { COMPANY_MEMBERS_LIST, COMPANY_MEMBER_UPDATE, COMPANY_MEMBER_DELETE, MEMBER_INVITATIONS_LIST, MEMBER_INVITATION_REVOKE, MEMBER_INVITATION_RESEND, MEMBER_SEAT_TRANSFER, INVITE_USER, SEAT_INVITE, } from '@archer/api-interface/endpoints/customer-api'; import type { CompanyMembersListResponse, CompanyMemberResponse, MemberInvitationsListResponse, MessageResponse, SeatInviteResponse, } from '@archer/api-interface'; import { apiClient } from '../../ApiClient'; export type MemberRole = 'admin' | 'manager' | 'worker' | 'operator'; function fillPath(template: string, params: Record): string { return Object.entries(params).reduce((acc, [key, value]) => acc.replace(`:${key}`, value), template); } export const membersApi = { async listMembers(companyId: string): Promise { const url = fillPath(COMPANY_MEMBERS_LIST.path, { id: companyId }); return apiClient.get(url); }, async updateMemberRole(companyId: string, memberId: string, role: MemberRole): Promise { const url = fillPath(COMPANY_MEMBER_UPDATE.path, { id: companyId, memberId }); return apiClient.put(url, { role }); }, async removeMember(companyId: string, memberId: string): Promise { const url = fillPath(COMPANY_MEMBER_DELETE.path, { id: companyId, memberId }); await apiClient.delete(url); }, async listInvitations(companyId: string): Promise { const url = fillPath(MEMBER_INVITATIONS_LIST.path, { id: companyId }); return apiClient.get(url); }, async createInvitation(input: { companyId: string; invitedBy: string; email: string; role: MemberRole; message?: string; }): Promise { return apiClient.post(INVITE_USER.path, input); }, /** Pay-first invite. Returns {paid:false} when the invite was created now, or * {paid:true, clientSecret} when a paid seat must be purchased via checkout. */ async seatInvite(companyId: string, input: { email: string; role: MemberRole; message?: string }): Promise { const url = fillPath(SEAT_INVITE.path, { id: companyId }); return apiClient.post(url, input); }, async revokeInvitation(companyId: string, invitationId: string): Promise { const url = fillPath(MEMBER_INVITATION_REVOKE.path, { id: companyId, invitationId }); await apiClient.delete(url); }, async resendInvitation(companyId: string, invitationId: string): Promise { const url = fillPath(MEMBER_INVITATION_RESEND.path, { id: companyId, invitationId }); return apiClient.post(url); }, async transferSeat( companyId: string, input: { fromMembershipId?: string; fromInvitationId?: string; toEmail: string; toRole: MemberRole; message?: string }, ): Promise { const url = fillPath(MEMBER_SEAT_TRANSFER.path, { id: companyId }); return apiClient.post(url, input); }, };