Files
4ventures-client-portal/src/components/RrssArchiveYear.tsx
4Ventures 068faa3d25 fix: tema claro blanco, logo 4Ventures y menú original
Fondo blanco en la app; menú con labels/iconos originales; logo en sidebar y login.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 09:53:36 +02:00

219 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
);
}