a
This commit is contained in:
214
convex/analysisSections.ts
Normal file
214
convex/analysisSections.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import { mutation, query } from "./_generated/server";
|
||||
import { v } from "convex/values";
|
||||
import { getAuthUserId } from "@convex-dev/auth/server";
|
||||
|
||||
const SECTION_KEYS = [
|
||||
"profile",
|
||||
"features",
|
||||
"competitors",
|
||||
"keywords",
|
||||
"problems",
|
||||
"personas",
|
||||
"useCases",
|
||||
"dorkQueries",
|
||||
] as const;
|
||||
|
||||
function assertSectionKey(sectionKey: string) {
|
||||
if (!SECTION_KEYS.includes(sectionKey as any)) {
|
||||
throw new Error(`Invalid section key: ${sectionKey}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getOwnedAnalysis(ctx: any, analysisId: any) {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (!userId) throw new Error("Unauthorized");
|
||||
|
||||
const analysis = await ctx.db.get(analysisId);
|
||||
if (!analysis) throw new Error("Analysis not found");
|
||||
|
||||
const project = await ctx.db.get(analysis.projectId);
|
||||
if (!project || project.userId !== userId) {
|
||||
throw new Error("Project not found or unauthorized");
|
||||
}
|
||||
|
||||
return analysis;
|
||||
}
|
||||
|
||||
async function patchAnalysisFromSection(
|
||||
ctx: any,
|
||||
analysisId: any,
|
||||
sectionKey: string,
|
||||
items: any
|
||||
) {
|
||||
if (sectionKey === "profile" && items && typeof items === "object") {
|
||||
const patch: Record<string, any> = {};
|
||||
if (typeof items.productName === "string") patch.productName = items.productName;
|
||||
if (typeof items.tagline === "string") patch.tagline = items.tagline;
|
||||
if (typeof items.description === "string") patch.description = items.description;
|
||||
if (typeof items.category === "string") patch.category = items.category;
|
||||
if (typeof items.positioning === "string") patch.positioning = items.positioning;
|
||||
if (Object.keys(patch).length > 0) {
|
||||
await ctx.db.patch(analysisId, patch);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (sectionKey === "features") {
|
||||
await ctx.db.patch(analysisId, { features: items });
|
||||
return;
|
||||
}
|
||||
if (sectionKey === "competitors") {
|
||||
await ctx.db.patch(analysisId, { competitors: items });
|
||||
return;
|
||||
}
|
||||
if (sectionKey === "keywords") {
|
||||
await ctx.db.patch(analysisId, { keywords: items });
|
||||
return;
|
||||
}
|
||||
if (sectionKey === "problems") {
|
||||
await ctx.db.patch(analysisId, { problemsSolved: items });
|
||||
return;
|
||||
}
|
||||
if (sectionKey === "personas") {
|
||||
await ctx.db.patch(analysisId, { personas: items });
|
||||
return;
|
||||
}
|
||||
if (sectionKey === "useCases") {
|
||||
await ctx.db.patch(analysisId, { useCases: items });
|
||||
return;
|
||||
}
|
||||
if (sectionKey === "dorkQueries") {
|
||||
await ctx.db.patch(analysisId, { dorkQueries: items });
|
||||
}
|
||||
}
|
||||
|
||||
export const listByAnalysis = query({
|
||||
args: { analysisId: v.id("analyses") },
|
||||
handler: async (ctx, args) => {
|
||||
await getOwnedAnalysis(ctx, args.analysisId);
|
||||
return await ctx.db
|
||||
.query("analysisSections")
|
||||
.withIndex("by_analysis", (q) => q.eq("analysisId", args.analysisId))
|
||||
.collect();
|
||||
},
|
||||
});
|
||||
|
||||
export const getSection = query({
|
||||
args: { analysisId: v.id("analyses"), sectionKey: v.string() },
|
||||
handler: async (ctx, args) => {
|
||||
await getOwnedAnalysis(ctx, args.analysisId);
|
||||
assertSectionKey(args.sectionKey);
|
||||
return await ctx.db
|
||||
.query("analysisSections")
|
||||
.withIndex("by_analysis_section", (q) =>
|
||||
q.eq("analysisId", args.analysisId).eq("sectionKey", args.sectionKey)
|
||||
)
|
||||
.first();
|
||||
},
|
||||
});
|
||||
|
||||
export const replaceSection = mutation({
|
||||
args: {
|
||||
analysisId: v.id("analyses"),
|
||||
sectionKey: v.string(),
|
||||
items: v.any(),
|
||||
lastPrompt: v.optional(v.string()),
|
||||
source: v.union(v.literal("ai"), v.literal("manual"), v.literal("mixed")),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await getOwnedAnalysis(ctx, args.analysisId);
|
||||
assertSectionKey(args.sectionKey);
|
||||
|
||||
const existing = await ctx.db
|
||||
.query("analysisSections")
|
||||
.withIndex("by_analysis_section", (q) =>
|
||||
q.eq("analysisId", args.analysisId).eq("sectionKey", args.sectionKey)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (existing) {
|
||||
await ctx.db.patch(existing._id, {
|
||||
items: args.items,
|
||||
lastPrompt: args.lastPrompt,
|
||||
source: args.source,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
} else {
|
||||
await ctx.db.insert("analysisSections", {
|
||||
analysisId: args.analysisId,
|
||||
sectionKey: args.sectionKey,
|
||||
items: args.items,
|
||||
lastPrompt: args.lastPrompt,
|
||||
source: args.source,
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
await patchAnalysisFromSection(ctx, args.analysisId, args.sectionKey, args.items);
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
|
||||
export const addItem = mutation({
|
||||
args: {
|
||||
analysisId: v.id("analyses"),
|
||||
sectionKey: v.string(),
|
||||
item: v.any(),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await getOwnedAnalysis(ctx, args.analysisId);
|
||||
assertSectionKey(args.sectionKey);
|
||||
|
||||
const section = await ctx.db
|
||||
.query("analysisSections")
|
||||
.withIndex("by_analysis_section", (q) =>
|
||||
q.eq("analysisId", args.analysisId).eq("sectionKey", args.sectionKey)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (!section || !Array.isArray(section.items)) {
|
||||
throw new Error("Section is not editable as a list.");
|
||||
}
|
||||
|
||||
const updated = [...section.items, args.item];
|
||||
await ctx.db.patch(section._id, {
|
||||
items: updated,
|
||||
source: "mixed",
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
await patchAnalysisFromSection(ctx, args.analysisId, args.sectionKey, updated);
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
|
||||
export const removeItem = mutation({
|
||||
args: {
|
||||
analysisId: v.id("analyses"),
|
||||
sectionKey: v.string(),
|
||||
index: v.number(),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await getOwnedAnalysis(ctx, args.analysisId);
|
||||
assertSectionKey(args.sectionKey);
|
||||
|
||||
const section = await ctx.db
|
||||
.query("analysisSections")
|
||||
.withIndex("by_analysis_section", (q) =>
|
||||
q.eq("analysisId", args.analysisId).eq("sectionKey", args.sectionKey)
|
||||
)
|
||||
.first();
|
||||
|
||||
if (!section || !Array.isArray(section.items)) {
|
||||
throw new Error("Section is not editable as a list.");
|
||||
}
|
||||
|
||||
const updated = section.items.filter((_: any, idx: number) => idx !== args.index);
|
||||
await ctx.db.patch(section._id, {
|
||||
items: updated,
|
||||
source: "mixed",
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
await patchAnalysisFromSection(ctx, args.analysisId, args.sectionKey, updated);
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user