Files
stack/apps/web/src/components/widgets/CalendarWidget.tsx
Jason Woltje ac1f2c176f
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix: Resolve all ESLint errors and warnings in web package
Fixes all 542 ESLint problems in the web package to achieve 0 errors and 0 warnings.

Changes:
- Fixed 144 issues: nullish coalescing, return types, unused variables
- Fixed 118 issues: unnecessary conditions, type safety, template literals
- Fixed 79 issues: non-null assertions, unsafe assignments, empty functions
- Fixed 67 issues: explicit return types, promise handling, enum comparisons
- Fixed 45 final warnings: missing return types, optional chains
- Fixed 25 typecheck-related issues: async/await, type assertions, formatting
- Fixed JSX.Element namespace errors across 90+ files

All Quality Rails violations resolved. Lint and typecheck both pass with 0 problems.

Files modified: 118 components, tests, hooks, and utilities

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-31 00:10:03 -06:00

142 lines
4.4 KiB
TypeScript

/**
* Calendar Widget - displays upcoming events
*/
import { useState, useEffect } from "react";
import { Calendar as CalendarIcon, Clock, MapPin } from "lucide-react";
import type { WidgetProps } from "@mosaic/shared";
interface Event {
id: string;
title: string;
startTime: string;
endTime?: string;
location?: string;
allDay: boolean;
}
export function CalendarWidget({ id: _id, config: _config }: WidgetProps): React.JSX.Element {
const [events, setEvents] = useState<Event[]>([]);
const [isLoading, setIsLoading] = useState(true);
// Mock data for now - will fetch from API later
useEffect(() => {
setIsLoading(true);
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
setTimeout(() => {
setEvents([
{
id: "1",
title: "Team Standup",
startTime: new Date(today.setHours(9, 0, 0, 0)).toISOString(),
endTime: new Date(today.setHours(9, 30, 0, 0)).toISOString(),
location: "Zoom",
allDay: false,
},
{
id: "2",
title: "Project Review",
startTime: new Date(today.setHours(14, 0, 0, 0)).toISOString(),
endTime: new Date(today.setHours(15, 0, 0, 0)).toISOString(),
location: "Conference Room A",
allDay: false,
},
{
id: "3",
title: "Sprint Planning",
startTime: new Date(tomorrow.setHours(10, 0, 0, 0)).toISOString(),
endTime: new Date(tomorrow.setHours(12, 0, 0, 0)).toISOString(),
allDay: false,
},
]);
setIsLoading(false);
}, 500);
}, []);
const formatTime = (dateString: string): string => {
const date = new Date(dateString);
return date.toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
hour12: true,
});
};
const formatDay = (dateString: string): string => {
const date = new Date(dateString);
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
if (date.toDateString() === today.toDateString()) {
return "Today";
} else if (date.toDateString() === tomorrow.toDateString()) {
return "Tomorrow";
}
return date.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" });
};
const getUpcomingEvents = (): Event[] => {
const now = new Date();
return events
.filter((e) => new Date(e.startTime) > now)
.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime())
.slice(0, 5);
};
if (isLoading) {
return (
<div className="flex items-center justify-center h-full">
<div className="text-gray-500 text-sm">Loading events...</div>
</div>
);
}
const upcomingEvents = getUpcomingEvents();
return (
<div className="flex flex-col h-full space-y-3">
{/* Header */}
<div className="flex items-center gap-2 text-gray-700">
<CalendarIcon className="w-4 h-4" />
<span className="text-sm font-medium">Upcoming Events</span>
</div>
{/* Event list */}
<div className="flex-1 overflow-auto space-y-3">
{upcomingEvents.length === 0 ? (
<div className="text-center text-gray-500 text-sm py-4">No upcoming events</div>
) : (
upcomingEvents.map((event) => (
<div key={event.id} className="border-l-2 border-blue-500 pl-3 py-1">
<div className="text-sm font-medium text-gray-900">{event.title}</div>
<div className="flex items-center gap-3 mt-1 text-xs text-gray-500">
{!event.allDay && (
<div className="flex items-center gap-1">
<Clock className="w-3 h-3" />
<span>
{formatTime(event.startTime)}
{event.endTime && ` - ${formatTime(event.endTime)}`}
</span>
</div>
)}
{event.location && (
<div className="flex items-center gap-1">
<MapPin className="w-3 h-3" />
<span>{event.location}</span>
</div>
)}
<div className="text-gray-400">{formatDay(event.startTime)}</div>
</div>
</div>
))
)}
</div>
</div>
);
}