121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { adminDb } from "@/lib/firebase/admin";
|
|
import { getAuth } from "firebase-admin/auth";
|
|
|
|
interface VisionBoardUpdate {
|
|
vision?: {
|
|
problemSolving?: string;
|
|
changeCreated?: string;
|
|
};
|
|
targetUser?: {
|
|
who?: string;
|
|
whereTheyHangOut?: string;
|
|
};
|
|
needs?: {
|
|
problemSolved?: string;
|
|
benefitProvided?: string;
|
|
};
|
|
product?: {
|
|
description?: string;
|
|
differentiation?: string;
|
|
};
|
|
validationGoals?: {
|
|
firstUser?: string;
|
|
pathTo10Users?: string;
|
|
pricing?: string;
|
|
};
|
|
}
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const authHeader = request.headers.get("authorization");
|
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
const token = authHeader.split("Bearer ")[1];
|
|
const decodedToken = await getAuth().verifyIdToken(token);
|
|
const userId = decodedToken.uid;
|
|
|
|
const { projectId, updates } = (await request.json()) as {
|
|
projectId: string;
|
|
updates: VisionBoardUpdate;
|
|
};
|
|
|
|
if (!projectId || !updates) {
|
|
return NextResponse.json(
|
|
{ error: "Missing projectId or updates" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Verify user has access to this project
|
|
const projectRef = adminDb.collection("projects").doc(projectId);
|
|
const projectSnap = await projectRef.get();
|
|
|
|
if (!projectSnap.exists) {
|
|
return NextResponse.json({ error: "Project not found" }, { status: 404 });
|
|
}
|
|
|
|
const projectData = projectSnap.data();
|
|
if (projectData?.userId !== userId) {
|
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
}
|
|
|
|
// Update vision board
|
|
const visionRef = projectRef.collection("visionBoard").doc("current");
|
|
const visionSnap = await visionRef.get();
|
|
|
|
const now = new Date();
|
|
|
|
if (visionSnap.exists()) {
|
|
// Merge updates
|
|
const currentData = visionSnap.data();
|
|
const mergedData = {
|
|
...currentData,
|
|
...updates,
|
|
vision: { ...currentData?.vision, ...updates.vision },
|
|
targetUser: { ...currentData?.targetUser, ...updates.targetUser },
|
|
needs: { ...currentData?.needs, ...updates.needs },
|
|
product: { ...currentData?.product, ...updates.product },
|
|
validationGoals: {
|
|
...currentData?.validationGoals,
|
|
...updates.validationGoals,
|
|
},
|
|
updatedAt: now,
|
|
};
|
|
|
|
await visionRef.update(mergedData);
|
|
return NextResponse.json({ success: true, data: mergedData });
|
|
} else {
|
|
// Create new vision board
|
|
const newData = {
|
|
vision: updates.vision || { problemSolving: "", changeCreated: "" },
|
|
targetUser: updates.targetUser || { who: "", whereTheyHangOut: "" },
|
|
needs: updates.needs || { problemSolved: "", benefitProvided: "" },
|
|
product: updates.product || { description: "", differentiation: "" },
|
|
validationGoals: updates.validationGoals || {
|
|
firstUser: "",
|
|
pathTo10Users: "",
|
|
pricing: "",
|
|
},
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
};
|
|
|
|
await visionRef.set(newData);
|
|
return NextResponse.json({ success: true, data: newData });
|
|
}
|
|
} catch (error) {
|
|
console.error("Vision board update error:", error);
|
|
return NextResponse.json(
|
|
{
|
|
error: "Failed to update vision board",
|
|
details: error instanceof Error ? error.message : String(error),
|
|
},
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|