Fondo blanco en la app; menú con labels/iconos originales; logo en sidebar y login. Co-authored-by: Cursor <cursoragent@cursor.com>
219 lines
6.9 KiB
TypeScript
219 lines
6.9 KiB
TypeScript
import React from 'react';
|
||
import Link from 'next/link';
|
||
import type { RrssMonthPlan, RrssPiece } from '@/components/RrssMonthView';
|
||
|
||
const MONTH_NAMES = [
|
||
'Enero',
|
||
'Febrero',
|
||
'Marzo',
|
||
'Abril',
|
||
'Mayo',
|
||
'Junio',
|
||
'Julio',
|
||
'Agosto',
|
||
'Septiembre',
|
||
'Octubre',
|
||
'Noviembre',
|
||
'Diciembre',
|
||
];
|
||
|
||
function colorFor(piece: RrssPiece): string {
|
||
if (piece.color) return piece.color;
|
||
if (piece.platform === 'linkedin') return '#7C66B8';
|
||
if ((piece.format || '').toLowerCase().includes('stor')) return '#374151';
|
||
const hay = `${piece.pillar || ''} ${piece.meta_line || ''} ${piece.title}`.toLowerCase();
|
||
if (hay.includes('familia') || hay.includes('bienestar')) return '#E08E2E';
|
||
if (hay.includes('sostenib')) return '#0F9D8F';
|
||
if (hay.includes('b2b') || hay.includes('hosteler')) return '#7C66B8';
|
||
if (hay.includes('prueba') || hay.includes('reseña')) return '#8A93A6';
|
||
return '#2071cf';
|
||
}
|
||
|
||
function MiniMonth({
|
||
year,
|
||
monthIndex,
|
||
plan,
|
||
}: {
|
||
year: number;
|
||
monthIndex: number; // 0-11
|
||
plan?: RrssMonthPlan;
|
||
}) {
|
||
const monthKey = `${year}-${String(monthIndex + 1).padStart(2, '0')}`;
|
||
const first = new Date(Date.UTC(year, monthIndex, 1));
|
||
const daysInMonth = new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
|
||
const startPad = (first.getUTCDay() + 6) % 7;
|
||
const live = Boolean(plan);
|
||
const byDate = new Map<string, RrssPiece[]>();
|
||
for (const p of plan?.pieces || []) {
|
||
const k = p.date.slice(0, 10);
|
||
(byDate.get(k) || byDate.set(k, []).get(k)!).push(p);
|
||
}
|
||
|
||
const headerCls = live
|
||
? 'flex items-center justify-between gap-2 bg-gradient-to-r from-[#1a2a63] to-[#2071CF] text-white px-3.5 py-2.5'
|
||
: 'flex items-center justify-between gap-2 bg-[#9aa6c0] text-white px-3.5 py-2.5';
|
||
|
||
const body = (
|
||
<>
|
||
<div className={headerCls}>
|
||
<span className="text-[13px] font-extrabold tracking-wider uppercase">
|
||
{MONTH_NAMES[monthIndex]}
|
||
</span>
|
||
<em className="not-italic text-[10px] font-bold tracking-wider uppercase px-2 py-1 rounded-full bg-white/20">
|
||
{live ? 'Publicado' : 'Pronto'}
|
||
</em>
|
||
</div>
|
||
<div className="grid grid-cols-7 gap-0.5 px-2.5 pt-3">
|
||
{['L', 'M', 'X', 'J', 'V', 'S', 'D'].map((d, i) => (
|
||
<span
|
||
key={d}
|
||
className={`text-center text-[10px] font-bold ${
|
||
i >= 5 ? 'text-sky-400' : 'text-gray-500'
|
||
}`}
|
||
>
|
||
{d}
|
||
</span>
|
||
))}
|
||
</div>
|
||
<div className="grid grid-cols-7 gap-0.5 px-2.5 pb-2.5 pt-1">
|
||
{Array.from({ length: startPad }).map((_, i) => (
|
||
<span key={`e-${i}`} className="aspect-square invisible" />
|
||
))}
|
||
{Array.from({ length: daysInMonth }).map((_, i) => {
|
||
const day = i + 1;
|
||
const key = `${monthKey}-${String(day).padStart(2, '0')}`;
|
||
const hits = byDate.get(key) || [];
|
||
const weekend =
|
||
new Date(Date.UTC(year, monthIndex, day)).getUTCDay() % 6 === 0;
|
||
const bg = hits[0] ? colorFor(hits[0]) : undefined;
|
||
return (
|
||
<span
|
||
key={key}
|
||
title={hits.map((h) => h.title).join(' · ') || undefined}
|
||
className={`aspect-square flex items-center justify-center text-[11px] font-bold rounded-full ${
|
||
weekend && !bg ? 'text-sky-400' : 'text-gray-600'
|
||
}`}
|
||
style={
|
||
bg
|
||
? {
|
||
background: bg,
|
||
color: '#fff',
|
||
boxShadow: 'inset 0 0 0 1px rgba(255,255,255,.35)',
|
||
}
|
||
: undefined
|
||
}
|
||
>
|
||
{day}
|
||
</span>
|
||
);
|
||
})}
|
||
</div>
|
||
<div className="mt-auto px-3.5 py-3 text-xs border-t border-gray-200 text-gray-500">
|
||
{live
|
||
? `${plan!.pieces.length} piezas · abrir mes`
|
||
: 'Pendiente de publicar'}
|
||
</div>
|
||
</>
|
||
);
|
||
|
||
const className = `rounded-2xl border overflow-hidden flex flex-col min-h-full transition-all ${
|
||
live
|
||
? 'border-gray-200 bg-white hover:border-sky-500/50 hover:-translate-y-0.5 shadow-lg shadow-sky-900/10'
|
||
: 'border-gray-200 bg-white opacity-80'
|
||
}`;
|
||
|
||
if (live) {
|
||
return (
|
||
<Link
|
||
href={`/dashboard/calendario?view=month&month=${monthKey}`}
|
||
className={className}
|
||
>
|
||
{body}
|
||
</Link>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className={className} aria-disabled>
|
||
{body}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/** Year archive cover like /agua-miia/rrss/ig-fb/ */
|
||
export default function RrssArchiveYear({
|
||
year = 2026,
|
||
plans,
|
||
tenantLabel,
|
||
agencyPublish,
|
||
}: {
|
||
year?: number;
|
||
plans: RrssMonthPlan[];
|
||
tenantLabel: string;
|
||
agencyPublish?: React.ReactNode;
|
||
}) {
|
||
const byMonth = new Map(plans.map((p) => [p.month, p]));
|
||
const legend = plans[0]?.legend || [
|
||
{ label: 'Educativo / serie', color: '#2071cf' },
|
||
{ label: 'Familia / bienestar', color: '#E08E2E' },
|
||
{ label: 'Sostenibilidad', color: '#0F9D8F' },
|
||
{ label: 'B2B / LinkedIn', color: '#7C66B8' },
|
||
{ label: 'Stories', color: '#374151' },
|
||
];
|
||
|
||
return (
|
||
<div className="space-y-7">
|
||
<div className="flex flex-wrap items-end justify-between gap-4">
|
||
<div>
|
||
<Link
|
||
href="/dashboard/calendario?view=hub"
|
||
className="text-xs text-gray-400 hover:text-gray-900"
|
||
>
|
||
← Hub RRSS
|
||
</Link>
|
||
<h2 className="text-3xl font-extrabold text-gray-900 tracking-tight mt-2">
|
||
Calendario IG / FB {year}
|
||
</h2>
|
||
<p className="text-sm text-gray-400 mt-2 max-w-xl leading-relaxed">
|
||
Archivo mensual de contenidos. Los días redondeados y coloreados
|
||
marcan el tipo de publicación (misma paleta que en cada mes).
|
||
</p>
|
||
</div>
|
||
<div className="flex flex-wrap items-center gap-3">
|
||
<div className="inline-flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2.5 text-sm font-bold text-gray-900">
|
||
<span className="w-2 h-2 rounded-full bg-sky-400" />
|
||
Año {year} · {tenantLabel} × 4Ventures
|
||
</div>
|
||
{agencyPublish}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{Array.from({ length: 12 }).map((_, i) => {
|
||
const key = `${year}-${String(i + 1).padStart(2, '0')}`;
|
||
return (
|
||
<MiniMonth
|
||
key={key}
|
||
year={year}
|
||
monthIndex={i}
|
||
plan={byMonth.get(key)}
|
||
/>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
<div className="flex flex-wrap gap-4 text-xs text-gray-400">
|
||
{legend.map((l) => (
|
||
<span key={l.label} className="inline-flex items-center gap-2">
|
||
<span
|
||
className="w-3 h-3 rounded-full"
|
||
style={{ background: l.color }}
|
||
/>
|
||
{l.label}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|