Files
frontend/components/organisms/shift-schedule/MonthlyCalendar.tsx
T
Thanh Quy - wolf 54d5d6150c
Release package / release (pull_request) Successful in 3m23s
Refactor: Update Vietnamese text to English across multiple components
- Translated payment page text from Vietnamese to English.
- Updated staff creation page with English labels and messages.
- Changed staff schedule page labels and navigation items to English.
- Modified shift card and search bar components to use English text.
- Updated delete confirmation and product modal components with English messages.
- Translated product management tab and category sidebar to English.
- Changed shift schedule components to display English text for shifts and actions.
- Updated header logo alt text to improve accessibility.
2026-05-14 21:09:18 +07:00

123 lines
4.3 KiB
TypeScript

"use client";
import { useShift } from "@/lib/shift-context";
import { useMemo } from "react";
import type { MonthlyCalendarProps } from "./ShiftSchedule.types";
const DAY_HEADERS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
export function formatDateISO(d: Date): string {
const y = d.getFullYear();
const m = (d.getMonth() + 1).toString().padStart(2, "0");
const day = d.getDate().toString().padStart(2, "0");
return `${y}-${m}-${day}`;
}
function isToday(d: Date): boolean {
const today = new Date(2026, 3, 10);
return (
d.getDate() === today.getDate() &&
d.getMonth() === today.getMonth() &&
d.getFullYear() === today.getFullYear()
);
}
export default function MonthlyCalendar({ onShiftClick, onDateSelect }: MonthlyCalendarProps) {
const { currentDate, getShiftsForDate } = useShift();
const calendarDays = useMemo(() => {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
let startOffset = firstDay.getDay() - 1;
if (startOffset < 0) startOffset = 6;
const days: (Date | null)[] = [];
for (let i = 0; i < startOffset; i++) days.push(null);
for (let d = 1; d <= lastDay.getDate(); d++) days.push(new Date(year, month, d));
while (days.length % 7 !== 0) days.push(null);
return days;
}, [currentDate]);
return (
<div className="overflow-hidden rounded-2xl border border-(--color-border-light) shadow-sm">
{/* Day headers */}
<div className="grid grid-cols-7 border-b border-(--color-border-light) bg-gradient-to-r from-(--color-primary)/8 to-(--color-primary)/3">
{DAY_HEADERS.map((day, i) => (
<div
key={day}
className={`px-2 py-3 text-center text-[11px] font-bold tracking-wider uppercase ${
i >= 5 ? "text-rose-400" : "text-(--color-primary)"
}`}
>
{day}
</div>
))}
</div>
{/* Calendar grid */}
<div className="grid grid-cols-7 bg-white">
{calendarDays.map((date, i) => {
if (!date) {
return (
<div
key={`empty-${i}`}
className="min-h-[100px] border-r border-b border-(--color-border-light) bg-gray-50/50"
/>
);
}
const today = isToday(date);
const shifts = getShiftsForDate(date);
const shiftCount = shifts.length;
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
return (
<button
type="button"
key={i}
onClick={() => onDateSelect?.(date)}
className={`group min-h-[100px] cursor-pointer border-r border-b border-(--color-border-light) bg-transparent p-2 text-left transition hover:bg-(--color-primary)/5 ${
today ? "bg-(--color-primary)/8" : ""
} ${isWeekend && !today ? "bg-rose-50/30" : ""}`}
>
{/* Date number */}
<span
className={`inline-flex h-7 w-7 items-center justify-center rounded-full text-sm font-bold transition ${
today
? "bg-(--color-primary) text-white shadow-sm"
: isWeekend
? "text-rose-500"
: "text-(--color-text-secondary) group-hover:text-(--color-primary)"
}`}
>
{date.getDate()}
</span>
{/* Shift count badge */}
{shiftCount > 0 && (
<div className="mt-1.5 space-y-1">
<span className="inline-flex items-center gap-1 rounded-full bg-(--color-primary)/10 px-1.5 py-0.5 text-[10px] font-semibold text-(--color-primary)">
<i className="fa-solid fa-clock text-[8px]"></i>
{shiftCount} shifts
</span>
</div>
)}
{/* Add indicator for manager */}
{shiftCount === 0 && onDateSelect && (
<p className="mt-1 text-[10px] text-(--color-text-muted) opacity-0 transition group-hover:opacity-100">
+ Add shift
</p>
)}
</button>
);
})}
</div>
</div>
);
}