All files / services documentsApi.ts

47.05% Statements 8/17
100% Branches 0/0
18.18% Functions 2/11
47.05% Lines 8/17

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 165 166                                                                                                                                                          1x 1x                                                                     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[];
	/** Direct URL to view/sign the document - provided by PandaDoc */
	signingUrl: string | null;
	/** Download URL for completed documents */
	downloadUrl: string | null;
}
 
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
 *
 * TODO: Wire up to real backend endpoints once PandaDoc
 * integration is implemented server-side. The backend will proxy requests
 * to PandaDoc's API using the investor's linked documents.
 *
 * Backend endpoints to implement:
 *   GET  /api/investor/documents          → list all documents for investor
 *   GET  /api/investor/documents/:id      → single document details
 *   POST /api/investor/documents/:id/session → create signing session URL
 *   POST /api/investor/documents/:id/resend  → resend document to investor
 */
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
		}),
 
		/** 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,
	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'
];