mission-control/app/api/voxyz/proposals/route.ts

190 lines
4.8 KiB
TypeScript

/**
* 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 { createClient } from '@/lib/supabase/server';
// 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 = await createClient();
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 = await createClient();
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 }
);
}
}