Add agency-only schedule fields and queue for pipeline creatives.

Approved pieces can be timed for IG/FB from the agency board; cron queues due jobs and calendar uses scheduled_at. Meta publish comes next.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-10-06 17:54:00 +02:00
parent 392af11060
commit 12ecde1b3b
9 changed files with 629 additions and 13 deletions

View File

@@ -0,0 +1,56 @@
/** Agency scheduling helpers for creative_pipeline. */
export const PUBLISH_CHANNELS = ['instagram', 'facebook'] as const;
export type PublishChannel = (typeof PUBLISH_CHANNELS)[number];
export const PUBLISH_STATUSES = [
'idle',
'queued',
'publishing',
'published',
'failed',
] as const;
export type PublishStatus = (typeof PUBLISH_STATUSES)[number];
export function normalizeChannels(raw: unknown): PublishChannel[] {
const list = Array.isArray(raw)
? raw.map((x) => String(x).toLowerCase().trim())
: typeof raw === 'string'
? raw.split(',').map((x) => x.toLowerCase().trim())
: [];
const out = PUBLISH_CHANNELS.filter((c) => list.includes(c));
return out.length ? [...out] : ['instagram', 'facebook'];
}
/** Parse datetime-local or ISO into timestamptz ISO, or null to clear. */
export function parseScheduledAt(raw: unknown): string | null | undefined {
if (raw === null) return null;
if (raw === undefined) return undefined;
if (typeof raw !== 'string') return undefined;
const t = raw.trim();
if (!t) return null;
const d = new Date(t);
if (Number.isNaN(d.getTime())) return undefined;
return d.toISOString();
}
export function toDatetimeLocalValue(iso?: string | null): string {
if (!iso) return '';
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return '';
const pad = (n: number) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
export function scheduleLabel(iso?: string | null): string | null {
if (!iso) return null;
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return null;
return d.toLocaleString('es-ES', {
weekday: 'short',
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
});
}