All files / services documentsApi.ts

47.36% Statements 9/19
100% Branches 0/0
23.07% Functions 3/13
47.36% Lines 9/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164                                                                                                                                1x 1x                                                                                         4x       4x                         1x             1x                         1x             1x         1x        
// src/services/documentsApi.ts
import {api} from './api';
 
// ============================================================================
// Types
// ============================================================================
 
/**
 * PandaDoc document status values
 * @see https://developers.pandadoc.com/reference/document-status
 */
export type DocumentStatus =
	| 'document.draft'
	| 'document.sent'
	| 'document.viewed'
	| 'document.waiting_approval'
	| 'document.in_progress'
	| 'document.completed'
	| 'document.declined'
	| 'document.expired'
	| 'document.voided';
 
export interface PandaDocRecipient {
	email: string;
	firstName: string;
	lastName: string;
	role: string;
	signingOrder?: number;
}
 
export interface PandaDocDocument {
	id: string;
	name: string;
	status: DocumentStatus;
	dateSent: string | null;
	dateCompleted: string | null;
	expirationDate: string | null;
	createdAt: string;
	updatedAt: string;
	recipients: PandaDocRecipient[];
}
 
export interface DocumentsResponse {
	documents: PandaDocDocument[];
}
 
export interface DocumentDetailResponse {
	document: PandaDocDocument;
}
 
export interface DocumentSessionResponse {
	/** Short-lived session URL for embedded signing */
	sessionUrl: string;
	expiresAt: string;
}
 
// ============================================================================
// API Endpoints
// ============================================================================
 
/**
 * Documents API endpoints. The backend proxies all calls to PandaDoc using
 * the API key, so the browser only ever talks to our Slim API.
 */
export const documentsApi = api.injectEndpoints({
	endpoints: (builder) => ({
		/** Get all documents for the authenticated investor */
		getDocuments: builder.query<DocumentsResponse, void>({
			query: () => '/investor/documents',
			transformResponse: (response: { data: DocumentsResponse }) => response.data,
			providesTags: ['Document']
		}),
 
		/** Get a single document by ID */
		getDocument: builder.query<PandaDocDocument, string>({
			query: (documentId) => `/investor/documents/${documentId}`,
			transformResponse: (response: { data: PandaDocDocument }) => response.data,
			providesTags: (_result, _error, id) => [{type: 'Document', id}]
		}),
 
		/** Create a PandaDoc session URL for embedded signing */
		createSigningSession: builder.mutation<DocumentSessionResponse, string>({
			query: (documentId) => ({
				url: `/investor/documents/${documentId}/session`,
				method: 'POST'
			}),
			transformResponse: (response: { data: DocumentSessionResponse }) => response.data
		}),
 
		/** Download a completed document as a PDF blob */
		downloadDocument: builder.mutation<Blob, string>({
			query: (documentId) => ({
				url: `/investor/documents/${documentId}/download`,
				method: 'GET',
				responseHandler: (response) => response.blob(),
				cache: 'no-store'
			})
		}),
 
		/** Resend a document to the investor */
		resendDocument: builder.mutation<void, string>({
			query: (documentId) => ({
				url: `/investor/documents/${documentId}/resend`,
				method: 'POST'
			}),
			invalidatesTags: ['Document']
		}),
 
		/** Create registration documents (W-9, etc.) for the authenticated investor */
		createRegistrationDocuments: builder.mutation<{ documentId: string; status: string }, void>({
			query: () => ({
				url: '/investor/documents',
				method: 'POST'
			}),
			transformResponse: (response: { data: { documentId: string; status: string } }) => response.data,
			invalidatesTags: ['Document']
		})
	})
});
 
export const {
	useGetDocumentsQuery,
	useGetDocumentQuery,
	useCreateSigningSessionMutation,
	useDownloadDocumentMutation,
	useResendDocumentMutation,
	useCreateRegistrationDocumentsMutation
} = documentsApi;
 
// ============================================================================
// Helpers
// ============================================================================
 
/** Human-readable labels for each PandaDoc status */
export const DOCUMENT_STATUS_LABELS: Record<DocumentStatus, string> = {
	'document.draft': 'Draft',
	'document.sent': 'Sent',
	'document.viewed': 'Viewed',
	'document.waiting_approval': 'Waiting Approval',
	'document.in_progress': 'In Progress',
	'document.completed': 'Completed',
	'document.declined': 'Declined',
	'document.expired': 'Expired',
	'document.voided': 'Voided'
};
 
/** Documents that require investor action */
export const ACTION_REQUIRED_STATUSES: DocumentStatus[] = [
	'document.sent',
	'document.viewed',
	'document.in_progress'
];
 
/** Completed documents */
export const COMPLETED_STATUSES: DocumentStatus[] = [
	'document.completed'
];
 
/** Inactive / closed documents */
export const INACTIVE_STATUSES: DocumentStatus[] = [
	'document.declined',
	'document.expired',
	'document.voided'
];