import { z } from 'zod'; import { reportError } from '@/lib/error-handling'; import { CrudService } from '@/lib/service-base'; import { SERVICE_CONFIGS } from '@/lib/service-config'; import { createProposalRequestSchema, organizationProposalsResponseSchema, proposalSchema, proposalsResponseSchema, updateProposalStatusRequestSchema, type CreateProposalRequest, type OrganizationProposalsResponse, type Proposal, type ProposalsResponse, type UpdateProposalStatusRequest, } from '@/schemas/proposal'; // Query parameter schemas const getProposalsParamsSchema = z.object({ organization_id: z.string().optional(), status: z.enum(['pending', 'accepted', 'rejected']).optional(), type: z.enum(['incoming', 'outgoing']).optional(), }); const getProposalsForOrganizationParamsSchema = z.object({ type: z.enum(['incoming', 'outgoing']).optional(), }); type GetProposalsParams = z.infer; type GetProposalsForOrganizationParams = z.infer; /** * Proposals Service * Handles all proposal-related API operations with improved reliability and type safety */ class ProposalsService extends CrudService { protected entitySchema = proposalSchema; protected createSchema = createProposalRequestSchema; protected updateSchema = createProposalRequestSchema.partial(); // Allow partial updates constructor() { super(SERVICE_CONFIGS.PROPOSALS); } /** * Get all proposals with optional filters */ async getAllProposals(params?: GetProposalsParams): Promise { const validatedParams = params ? this.validateStrict(getProposalsParamsSchema, params, 'getProposals params') : {}; const query = this.createQuery() .whenDefined('organization_id', validatedParams.organization_id) .whenDefined('status', validatedParams.status) .whenDefined('type', validatedParams.type); const result = await this.get('', proposalsResponseSchema, query.toParams(), { context: 'getAllProposals', }); if (!result.success) { const error = reportError(new Error(result.error?.message || 'Failed to fetch proposals'), { operation: 'getAllProposals', params: validatedParams, }); throw error; } return result.data; } /** * Get proposals for a specific organization */ async getProposalsForOrganization( orgId: string, params?: GetProposalsForOrganizationParams ): Promise { const validatedParams = params ? this.validateStrict( getProposalsForOrganizationParamsSchema, params, 'getProposalsForOrganization params' ) : {}; const query = this.createQuery().whenDefined('type', validatedParams.type); const result = await this.get( `organization/${orgId}`, organizationProposalsResponseSchema, query.toParams(), { context: `getProposalsForOrganization(${orgId})`, } ); if (!result.success) { const error = reportError( new Error(result.error?.message || 'Failed to fetch organization proposals'), { operation: 'getProposalsForOrganization', orgId, params: validatedParams } ); throw error; } return result.data; } /** * Update proposal status */ async updateStatus(id: string, statusUpdate: UpdateProposalStatusRequest): Promise { const validatedUpdate = this.validateStrict( updateProposalStatusRequestSchema, statusUpdate, 'updateStatus request' ); const result = await this.post(`${id}/status`, validatedUpdate, this.entitySchema, { context: `updateStatus(${id})`, }); if (!result.success) { const error = reportError( new Error(result.error?.message || 'Failed to update proposal status'), { operation: 'updateStatus', id, statusUpdate: validatedUpdate } ); throw error; } return result.data; } } // Create and export service instance const proposalsService = new ProposalsService(); // Export service instance for direct usage export { proposalsService }; // Export service methods directly for cleaner imports export const getProposals = proposalsService.getAllProposals.bind(proposalsService); export const getProposalById = (id: string) => proposalsService.getById(id).then((result) => { if (!result.success) { const error = reportError(new Error(result.error?.message || 'Failed to fetch proposal'), { operation: 'getProposalById', id, }); throw error; } return result.data; }); export const createProposal = (request: CreateProposalRequest) => proposalsService.create(request).then((result) => { if (!result.success) { const error = reportError(new Error(result.error?.message || 'Failed to create proposal'), { operation: 'createProposal', request, }); throw error; } return result.data; }); export const updateProposalStatus = proposalsService.updateStatus.bind(proposalsService); export const getProposalsForOrganization = (orgId: string, type?: 'incoming' | 'outgoing') => proposalsService.getProposalsForOrganization(orgId, type ? { type } : undefined);