Root cause: viewport was hardcoded to width=3840 and body to 3840x2160px. If TV browser has different viewport, content overflows and can't be zoomed to fit. Changes: - viewport meta: width=device-width instead of width=3840 - body: 100vw/100vh instead of fixed pixels - App container: w-screen h-screen - font-size: clamp(14px, 1.15vw, 24px) scales with viewport - Topology: horizontal chain (Modem → FW → Switch) saves vertical space, VM pills in 3-col grid, all sizes relative - Kanban: 3-col grid, compact project cards - All padding/gaps use rem (scale with base font) - Removed all hardcoded pixel max-widths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
96 lines
3.7 KiB
TypeScript
96 lines
3.7 KiB
TypeScript
import { EventCard } from "./EventCard";
|
|
import type { CalendarEvent } from "../../types";
|
|
|
|
interface CalendarViewProps {
|
|
events: CalendarEvent[];
|
|
}
|
|
|
|
export function CalendarView({ events }: CalendarViewProps) {
|
|
const now = new Date();
|
|
const today = now.toISOString().split("T")[0];
|
|
const tomorrow = new Date(now.getTime() + 86400000).toISOString().split("T")[0];
|
|
|
|
const todayEvents = events.filter((e) => e.start.startsWith(today));
|
|
const tomorrowEvents = events.filter((e) => e.start.startsWith(tomorrow));
|
|
const laterEvents = events.filter(
|
|
(e) => !e.start.startsWith(today) && !e.start.startsWith(tomorrow)
|
|
);
|
|
|
|
const formatDate = (dateStr: string) => {
|
|
return new Date(dateStr).toLocaleDateString("es-MX", {
|
|
weekday: "long",
|
|
month: "long",
|
|
day: "numeric",
|
|
});
|
|
};
|
|
|
|
const hasEvents = events.length > 0;
|
|
|
|
return (
|
|
<div className="flex flex-col h-full">
|
|
<div className="flex items-center gap-6 px-8 py-2 bg-bg-secondary border-b border-border shrink-0">
|
|
<span className="text-base text-text-secondary">
|
|
<span className="font-bold text-text-primary">{events.length}</span> eventos esta semana
|
|
</span>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto px-8 py-6">
|
|
{!hasEvents ? (
|
|
<div className="flex flex-col items-center justify-center h-full gap-4">
|
|
<span className="text-6xl opacity-30">📅</span>
|
|
<p className="text-2xl font-semibold text-text-secondary">
|
|
Sin eventos programados
|
|
</p>
|
|
<p className="text-lg text-text-muted">
|
|
Los próximos eventos del calendario de Odoo aparecerán aquí
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-3 gap-8 h-full">
|
|
<section>
|
|
<h2 className="text-xl font-bold text-text-primary mb-1">Hoy</h2>
|
|
<p className="text-sm text-text-muted capitalize mb-4">{formatDate(today)}</p>
|
|
{todayEvents.length === 0 ? (
|
|
<p className="text-base text-text-secondary">Sin eventos</p>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{todayEvents.map((e) => <EventCard key={e.id} event={e} />)}
|
|
</div>
|
|
)}
|
|
</section>
|
|
<section>
|
|
<h2 className="text-xl font-bold text-text-primary mb-1">Mañana</h2>
|
|
<p className="text-sm text-text-muted capitalize mb-4">{formatDate(tomorrow)}</p>
|
|
{tomorrowEvents.length === 0 ? (
|
|
<p className="text-base text-text-secondary">Sin eventos</p>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{tomorrowEvents.map((e) => <EventCard key={e.id} event={e} />)}
|
|
</div>
|
|
)}
|
|
</section>
|
|
<section>
|
|
<h2 className="text-xl font-bold text-text-primary mb-1">Esta semana</h2>
|
|
<p className="text-sm text-text-muted mb-4">Próximos días</p>
|
|
{laterEvents.length === 0 ? (
|
|
<p className="text-base text-text-secondary">Sin eventos</p>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{laterEvents.map((e) => (
|
|
<div key={e.id} className="flex gap-3 text-sm bg-bg-card rounded-lg px-3 py-2 border border-border">
|
|
<span className="font-mono text-text-muted min-w-[10rem] capitalize">
|
|
{formatDate(e.start)}
|
|
</span>
|
|
<span className="text-text-primary truncate">{e.name}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|