feat: add complete frontend with React, Tailwind 4K, and Docker setup
- Vite + React 18 + TypeScript scaffolding - Tailwind CSS configured for 4K dark theme (24px base) - Three full-screen rotating views: Network Topology (D3.js), Kanban Board (Odoo tasks), Calendar (Odoo events) - Hooks for data fetching, WebSocket, and view rotation - Header with live clock and connection status - Framer Motion fade transitions between views - Docker Compose with backend (host network for nmap) and frontend (nginx proxy to backend API/WS) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
76
frontend/src/components/Calendar/CalendarView.tsx
Normal file
76
frontend/src/components/Calendar/CalendarView.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
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",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full p-12 overflow-hidden">
|
||||
<section className="mb-10">
|
||||
<h2 className="text-3xl font-bold text-text-primary mb-6">
|
||||
Hoy — {formatDate(today)}
|
||||
</h2>
|
||||
{todayEvents.length === 0 ? (
|
||||
<p className="text-xl text-text-secondary">Sin eventos programados</p>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{todayEvents.map((e) => (
|
||||
<EventCard key={e.id} event={e} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-2xl font-bold text-text-primary mb-4">
|
||||
Mañana — {formatDate(tomorrow)}
|
||||
</h2>
|
||||
{tomorrowEvents.length === 0 ? (
|
||||
<p className="text-lg text-text-secondary">Sin eventos programados</p>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{tomorrowEvents.map((e) => (
|
||||
<EventCard key={e.id} event={e} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{laterEvents.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold text-text-primary mb-4">Esta semana</h2>
|
||||
<div className="space-y-3">
|
||||
{laterEvents.map((e) => (
|
||||
<div key={e.id} className="flex gap-4 text-lg text-text-secondary">
|
||||
<span className="font-mono min-w-[200px] capitalize">
|
||||
{formatDate(e.start)}
|
||||
</span>
|
||||
<span className="text-text-primary">{e.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user