/** * Voxyz API Routes - Mission Proposals * Handles proposal CRUD operations */ import { NextRequest, NextResponse } from 'next/server'; import { getProposalsForDate, approveProposal, rejectProposal, completeProposal, getTopProposalsForToday, expireOldProposals } from '@/lib/services/proposal.service'; import { getServiceSupabase } from '@/lib/supabase/client'; // GET /api/voxyz/proposals - Get proposals export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const date = searchParams.get('date'); const today = searchParams.get('today'); const status = searchParams.get('status'); if (today) { const proposals = await getTopProposalsForToday(); return NextResponse.json({ proposals }); } if (date) { const proposals = await getProposalsForDate(new Date(date)); return NextResponse.json({ proposals }); } // Get all proposals with optional status filter const supabase = getServiceSupabase(); let query = supabase.from('ops_mission_proposals').select('*'); if (status) { query = query.eq('status', status); } const { data: proposals, error } = await query .order('created_at', { ascending: false }) .limit(100); if (error) { throw error; } return NextResponse.json({ proposals: proposals || [] }); } catch (error) { console.error('Error fetching proposals:', error); return NextResponse.json( { error: 'Failed to fetch proposals' }, { status: 500 } ); } } // POST /api/voxyz/proposals - Create a new proposal (manual) export async function POST(request: NextRequest) { try { const body = await request.json(); const { title, description, taskIds, projectIds, priorityScore } = body; if (!title) { return NextResponse.json( { error: 'Missing required field: title' }, { status: 400 } ); } const supabase = getServiceSupabase(); const proposal = { proposal_date: new Date().toISOString().split('T')[0], status: 'pending', priority_score: priorityScore || 50, passes_cap_gates: true, cap_gate_violations: [], source_task_ids: taskIds || [], source_project_ids: projectIds || [], title, description: description || null, rationale: 'Manually created proposal', ai_analysis: {}, expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), }; const { data, error } = await supabase .from('ops_mission_proposals') .insert(proposal) .select() .single(); if (error) { throw error; } return NextResponse.json({ proposal: data }, { status: 201 }); } catch (error) { console.error('Error creating proposal:', error); return NextResponse.json( { error: 'Failed to create proposal' }, { status: 500 } ); } } // PATCH /api/voxyz/proposals - Update proposal status export async function PATCH(request: NextRequest) { try { const body = await request.json(); const { id, action, reason, notes } = body; if (!id || !action) { return NextResponse.json( { error: 'Missing required fields: id, action' }, { status: 400 } ); } let success = false; switch (action) { case 'approve': success = await approveProposal(id, 'manual-user'); // TODO: Get actual user ID break; case 'reject': if (!reason) { return NextResponse.json( { error: 'Rejection requires a reason' }, { status: 400 } ); } success = await rejectProposal(id, reason); break; case 'complete': success = await completeProposal(id, notes); break; default: return NextResponse.json( { error: `Unknown action: ${action}` }, { status: 400 } ); } if (!success) { return NextResponse.json( { error: `Failed to ${action} proposal` }, { status: 500 } ); } return NextResponse.json({ success: true }); } catch (error) { console.error('Error updating proposal:', error); return NextResponse.json( { error: 'Failed to update proposal' }, { status: 500 } ); } } // DELETE /api/voxyz/proposals - Expire old proposals export async function DELETE(request: NextRequest) { try { const expiredCount = await expireOldProposals(); return NextResponse.json({ success: true, expiredCount }); } catch (error) { console.error('Error expiring proposals:', error); return NextResponse.json( { error: 'Failed to expire proposals' }, { status: 500 } ); } }