156 lines
7.6 KiB
TypeScript
156 lines
7.6 KiB
TypeScript
import { PrismaClient } from "@prisma/client";
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
/**
|
|
* Seeds tasks and page launches for each brand.
|
|
* Run: npx tsx prisma/seed-tasks.ts
|
|
*/
|
|
async function main() {
|
|
console.log("Seeding tasks and page launches...");
|
|
|
|
const brands = await prisma.brand.findMany();
|
|
if (brands.length === 0) {
|
|
console.log("No brands found. Run the main seed first.");
|
|
return;
|
|
}
|
|
|
|
for (const brand of brands) {
|
|
// Clear existing
|
|
await prisma.pageLaunchSnapshot.deleteMany({
|
|
where: { pageLaunch: { brandId: brand.id } },
|
|
});
|
|
await prisma.pageLaunch.deleteMany({ where: { brandId: brand.id } });
|
|
await prisma.task.deleteMany({ where: { brandId: brand.id } });
|
|
|
|
// ── Tasks ──
|
|
const tasks = [
|
|
{ title: "Add meta descriptions to 14 pages", category: "seo", priority: "high", effort: "low", status: "completed", pageUrl: "/features", recommendation: "Missing meta descriptions reduce CTR by ~15%", daysAgo: 21 },
|
|
{ title: "Fix 17 broken internal links", category: "technical", priority: "high", effort: "medium", status: "completed", pageUrl: "/blog", recommendation: "Broken links waste crawl budget and link equity", daysAgo: 18 },
|
|
{ title: "Optimize hero images for Core Web Vitals", category: "technical", priority: "high", effort: "low", status: "completed", pageUrl: "/pricing", recommendation: "LCP is 3.8s — compress images to get under 2.5s", daysAgo: 14 },
|
|
{ title: "Publish SEO pillar page: AI Tools Guide", category: "content", priority: "high", effort: "high", status: "completed", pageUrl: "/blog/ai-tools", recommendation: "Target 'AI SEO tools' — 6.8K monthly searches, low competition", daysAgo: 10 },
|
|
{ title: "Add FAQ schema to top 5 guides", category: "seo", priority: "medium", effort: "low", status: "in_progress", pageUrl: "/blog/seo-guide", recommendation: "FAQ schema increases AI citation probability by ~40%", daysAgo: 7 },
|
|
{ title: "Improve mobile speed on /products", category: "technical", priority: "high", effort: "medium", status: "in_progress", pageUrl: "/products", recommendation: "Mobile score 52/100 — defer JS and lazy-load images", daysAgo: 5 },
|
|
{ title: "Update GBP listing with new services", category: "gbp", priority: "medium", effort: "low", status: "todo", pageUrl: null, recommendation: "GBP listings with complete info get 7x more clicks", daysAgo: 3 },
|
|
{ title: "Create comparison page: meSEO vs RankBoost", category: "content", priority: "medium", effort: "high", status: "todo", pageUrl: "/vs-rankboost", recommendation: "Comparison pages capture 800-2000 visits/mo in your category", daysAgo: 2 },
|
|
{ title: "Optimize AI visibility for Perplexity", category: "llmo", priority: "high", effort: "medium", status: "todo", pageUrl: "/features", recommendation: "Competitor displaced you — publish competing technical guide", daysAgo: 1 },
|
|
{ title: "Fix HTTP mixed content on 4 pages", category: "technical", priority: "low", effort: "low", status: "todo", pageUrl: "/about", recommendation: "HTTPS pages with HTTP resources trigger security warnings", daysAgo: 0 },
|
|
];
|
|
|
|
const createdTasks: Record<string, string> = {};
|
|
|
|
for (const t of tasks) {
|
|
const createdAt = new Date();
|
|
createdAt.setDate(createdAt.getDate() - t.daysAgo);
|
|
|
|
const task = await prisma.task.create({
|
|
data: {
|
|
brandId: brand.id,
|
|
title: t.title,
|
|
category: t.category,
|
|
priority: t.priority,
|
|
effort: t.effort,
|
|
status: t.status,
|
|
pageUrl: t.pageUrl,
|
|
recommendation: t.recommendation,
|
|
completedAt: t.status === "completed" ? new Date(createdAt.getTime() + 3 * 86400000) : null,
|
|
dueDate: new Date(Date.now() + (t.status === "todo" ? 7 : 14) * 86400000),
|
|
createdAt,
|
|
},
|
|
});
|
|
|
|
createdTasks[t.title] = task.id;
|
|
}
|
|
|
|
console.log(` Created ${tasks.length} tasks for ${brand.name}`);
|
|
|
|
// ── Page Launches (from completed tasks) ──
|
|
const launches = [
|
|
{
|
|
taskTitle: "Add meta descriptions to 14 pages",
|
|
pageUrl: "/features",
|
|
pageType: "landing",
|
|
daysAgo: 18,
|
|
baseline: { sessions: 120, clicks: 80, impressions: 2400, position: 14.2, conversions: 3, revenue: 240 },
|
|
snapshots: [
|
|
{ daysAgo: 14, sessions: 135, clicks: 95, impressions: 2800, position: 12.8, conversions: 4, revenue: 320 },
|
|
{ daysAgo: 7, sessions: 158, clicks: 118, impressions: 3200, position: 10.5, conversions: 6, revenue: 480 },
|
|
{ daysAgo: 0, sessions: 172, clicks: 132, impressions: 3600, position: 9.1, conversions: 7, revenue: 560 },
|
|
],
|
|
},
|
|
{
|
|
taskTitle: "Fix 17 broken internal links",
|
|
pageUrl: "/blog",
|
|
pageType: "blog",
|
|
daysAgo: 15,
|
|
baseline: { sessions: 280, clicks: 160, impressions: 5200, position: 11.4, conversions: 5, revenue: 0 },
|
|
snapshots: [
|
|
{ daysAgo: 10, sessions: 310, clicks: 185, impressions: 5800, position: 10.2, conversions: 6, revenue: 0 },
|
|
{ daysAgo: 3, sessions: 340, clicks: 210, impressions: 6400, position: 9.5, conversions: 8, revenue: 0 },
|
|
{ daysAgo: 0, sessions: 355, clicks: 225, impressions: 6800, position: 8.8, conversions: 9, revenue: 0 },
|
|
],
|
|
},
|
|
{
|
|
taskTitle: "Publish SEO pillar page: AI Tools Guide",
|
|
pageUrl: "/blog/ai-tools",
|
|
pageType: "blog",
|
|
daysAgo: 7,
|
|
baseline: { sessions: 0, clicks: 0, impressions: 0, position: 0, conversions: 0, revenue: 0 },
|
|
snapshots: [
|
|
{ daysAgo: 4, sessions: 45, clicks: 28, impressions: 820, position: 18.4, conversions: 1, revenue: 0 },
|
|
{ daysAgo: 0, sessions: 120, clicks: 78, impressions: 2200, position: 11.2, conversions: 3, revenue: 0 },
|
|
],
|
|
},
|
|
];
|
|
|
|
for (const l of launches) {
|
|
const taskId = createdTasks[l.taskTitle];
|
|
const launchDate = new Date();
|
|
launchDate.setDate(launchDate.getDate() - l.daysAgo);
|
|
|
|
const launch = await prisma.pageLaunch.create({
|
|
data: {
|
|
brandId: brand.id,
|
|
taskId,
|
|
pageUrl: l.pageUrl,
|
|
pageType: l.pageType,
|
|
launchDate,
|
|
notes: `Launched after completing: ${l.taskTitle}`,
|
|
baselineSessions: l.baseline.sessions,
|
|
baselineClicks: l.baseline.clicks,
|
|
baselineImpressions: l.baseline.impressions,
|
|
baselinePosition: l.baseline.position,
|
|
baselineConversions: l.baseline.conversions,
|
|
baselineRevenue: l.baseline.revenue,
|
|
},
|
|
});
|
|
|
|
// Add snapshots
|
|
for (const s of l.snapshots) {
|
|
const snapshotDate = new Date();
|
|
snapshotDate.setDate(snapshotDate.getDate() - s.daysAgo);
|
|
await prisma.pageLaunchSnapshot.create({
|
|
data: {
|
|
pageLaunchId: launch.id,
|
|
date: snapshotDate,
|
|
sessions: s.sessions,
|
|
clicks: s.clicks,
|
|
impressions: s.impressions,
|
|
position: s.position,
|
|
conversions: s.conversions,
|
|
revenue: s.revenue,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log(` Created ${launches.length} page launches with snapshots for ${brand.name}`);
|
|
}
|
|
|
|
console.log("Tasks and page launches seeding complete!");
|
|
}
|
|
|
|
main()
|
|
.catch((e) => { console.error(e); process.exit(1); })
|
|
.finally(() => prisma.$disconnect());
|