Spaces:
Running
Running
| 'use client' | |
| import React, { useState } from 'react' | |
| import { CaretLeft, CaretRight } from '@phosphor-icons/react' | |
| import { motion } from 'framer-motion' | |
| export function CalendarWidget() { | |
| const [currentDate, setCurrentDate] = useState(new Date()) | |
| const today = new Date() | |
| const monthNames = [ | |
| 'January', 'February', 'March', 'April', 'May', 'June', | |
| 'July', 'August', 'September', 'October', 'November', 'December' | |
| ] | |
| const daysOfWeek = ['S', 'M', 'T', 'W', 'T', 'F', 'S'] | |
| const getDaysInMonth = (date: Date) => { | |
| const year = date.getFullYear() | |
| const month = date.getMonth() | |
| const firstDay = new Date(year, month, 1) | |
| const lastDay = new Date(year, month + 1, 0) | |
| const daysInMonth = lastDay.getDate() | |
| const startingDayOfWeek = firstDay.getDay() | |
| const days: (number | null)[] = [] | |
| for (let i = 0; i < startingDayOfWeek; i++) { | |
| days.push(null) | |
| } | |
| for (let i = 1; i <= daysInMonth; i++) { | |
| days.push(i) | |
| } | |
| return days | |
| } | |
| const days = getDaysInMonth(currentDate) | |
| const isToday = (day: number | null) => { | |
| if (!day) return false | |
| return ( | |
| day === today.getDate() && | |
| currentDate.getMonth() === today.getMonth() && | |
| currentDate.getFullYear() === today.getFullYear() | |
| ) | |
| } | |
| const previousMonth = () => { | |
| setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1)) | |
| } | |
| const nextMonth = () => { | |
| setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1)) | |
| } | |
| return ( | |
| <div className="w-80 bg-white/80 backdrop-blur-xl rounded-xl shadow-2xl border border-white/40 p-4 text-gray-800"> | |
| {/* Header */} | |
| <div className="flex items-center justify-between mb-4"> | |
| <div className="font-bold text-lg"> | |
| {monthNames[currentDate.getMonth()]} {currentDate.getFullYear()} | |
| </div> | |
| <div className="flex space-x-1"> | |
| <button | |
| onClick={previousMonth} | |
| className="p-1 hover:bg-gray-200/50 rounded-full transition-colors" | |
| > | |
| <CaretLeft size={16} weight="bold" /> | |
| </button> | |
| <button | |
| onClick={nextMonth} | |
| className="p-1 hover:bg-gray-200/50 rounded-full transition-colors" | |
| > | |
| <CaretRight size={16} weight="bold" /> | |
| </button> | |
| </div> | |
| </div> | |
| {/* Days of Week */} | |
| <div className="grid grid-cols-7 mb-2"> | |
| {daysOfWeek.map((day, i) => ( | |
| <div key={i} className="text-center text-xs font-semibold text-gray-500"> | |
| {day} | |
| </div> | |
| ))} | |
| </div> | |
| {/* Calendar Grid */} | |
| <div className="grid grid-cols-7 gap-1"> | |
| {days.map((day, index) => ( | |
| <div | |
| key={index} | |
| className={` | |
| aspect-square flex items-center justify-center text-sm rounded-full | |
| ${day ? 'cursor-default' : ''} | |
| ${isToday(day) ? 'bg-blue-500 text-white font-bold shadow-md' : 'text-gray-700'} | |
| ${day && !isToday(day) ? 'hover:bg-gray-200/50' : ''} | |
| `} | |
| > | |
| {day} | |
| </div> | |
| ))} | |
| </div> | |
| {/* Event Dots (Mock) */} | |
| <div className="mt-4 pt-3 border-t border-gray-200/50"> | |
| <div className="text-xs font-semibold text-gray-500 mb-2">UPCOMING EVENTS</div> | |
| <div className="space-y-2"> | |
| <div className="flex items-center gap-2"> | |
| <div className="w-1 h-8 rounded-full bg-blue-500"></div> | |
| <div> | |
| <div className="text-xs font-bold">Team Meeting</div> | |
| <div className="text-[10px] text-gray-500">10:00 AM - 11:00 AM</div> | |
| </div> | |
| </div> | |
| <div className="flex items-center gap-2"> | |
| <div className="w-1 h-8 rounded-full bg-purple-500"></div> | |
| <div> | |
| <div className="text-xs font-bold">Project Review</div> | |
| <div className="text-[10px] text-gray-500">2:00 PM - 3:30 PM</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ) | |
| } | |