Feed editorial calendar generation with past calendars and live network posts.

Brandbook plus calendar history and published pipeline/IG pieces now shape series mix and Claude strategist prompts without copying old titles.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-10-13 13:53:27 +02:00
parent 3afec6402f
commit 714f250eca
4 changed files with 197 additions and 11 deletions

View File

@@ -12,6 +12,12 @@ import {
BRANDBOOK_DOC_TYPE,
parseBrandbook,
} from '@/lib/brandbook';
import {
pipelinePublishDay,
type PipelineCalendarRow,
} from '@/lib/pipeline-calendar';
import type { PublishedNetworkPost } from '@/lib/calendar-strategy';
import { normalizeApprovalStatus } from '@/lib/approval-status';
export const dynamic = 'force-dynamic';
export const maxDuration = 60;
@@ -75,12 +81,47 @@ export async function POST(req: NextRequest) {
);
const brandbook = parseBrandbook(bb.data?.[0]?.content);
// Publicaciones reales en redes (pipeline): publicadas o con rastro IG
const pipe = await restGet<PipelineCalendarRow[]>(
`/rest/v1/creative_pipeline?tenant_id=eq.${encodeURIComponent(
ctx.tenantId
)}&select=id,title,format,month,status,caption,notes,created_at,scheduled_at,channels,publish_status,external_ref,published_at&order=created_at.desc&limit=400`
);
const publishedPosts: PublishedNetworkPost[] = [];
for (const row of pipe.data || []) {
const st = normalizeApprovalStatus(row.status);
const ref = String(row.external_ref || '').toLowerCase();
// Solo piezas ya salidas / sync IG (no borradores del pipeline)
if (
st !== 'publicada' &&
row.publish_status !== 'published' &&
!ref.startsWith('ig-')
) {
continue;
}
const day =
pipelinePublishDay(row) ||
row.published_at?.slice(0, 10) ||
row.created_at?.slice(0, 10) ||
null;
publishedPosts.push({
title: row.title,
caption: row.caption,
format: row.format,
day,
month: row.month || (day ? day.slice(0, 7) : null),
status: row.status,
channels: row.channels,
});
}
const result = await generateMonthPreview({
ym: month,
slug,
label: ctx.label,
history,
brandbook,
publishedPosts,
});
return NextResponse.json({
@@ -90,5 +131,7 @@ export async function POST(req: NextRequest) {
source: result.source,
brand: slug,
brandbook: !!brandbook,
history_months: history.length,
network_posts: publishedPosts.length,
});
}