This commit is contained in:
@@ -37,7 +37,7 @@ function formatDateISO(d: Date): string {
|
||||
}
|
||||
|
||||
function isToday(d: Date): boolean {
|
||||
const today = new Date(2026, 3, 10);
|
||||
const today = Date.now();
|
||||
return (
|
||||
d.getDate() === today.getDate() &&
|
||||
d.getMonth() === today.getMonth() &&
|
||||
@@ -60,35 +60,31 @@ export default function WeeklySchedule({
|
||||
const weekDates = getWeekDates();
|
||||
|
||||
const [selectedDate, setSelectedDate] = useState<string>(
|
||||
formatDateISO(weekDates[0] ?? currentDate),
|
||||
weekDates[0] ?? currentDate,
|
||||
);
|
||||
|
||||
const statusDotsByDate = useMemo(() => {
|
||||
const map: Record<string, string[]> = {};
|
||||
weekDates.forEach((date) => {
|
||||
const dateStr = formatDateISO(date);
|
||||
const dayShifts = getShiftsForDate(date);
|
||||
const dots: string[] = [];
|
||||
if (dayShifts.some((s) => s.status === "available"))
|
||||
dots.push("bg-sky-300");
|
||||
if (dayShifts.some((s) => s.status === "registered"))
|
||||
dots.push("bg-blue-600");
|
||||
if (dayShifts.some((s) => s.status === "approved_leave"))
|
||||
dots.push("bg-purple-400");
|
||||
if (dayShifts.some((s) => s.status === "absent"))
|
||||
dots.push("bg-rose-400");
|
||||
map[dateStr] = dots.slice(0, 3);
|
||||
// if (dayShifts.some((s) => s.status === "available"))
|
||||
// dots.push("bg-sky-300");
|
||||
// if (dayShifts.some((s) => s.status === "registered"))
|
||||
// dots.push("bg-blue-600");
|
||||
// if (dayShifts.some((s) => s.status === "approved_leave"))
|
||||
// dots.push("bg-purple-400");
|
||||
// if (dayShifts.some((s) => s.status === "absent"))
|
||||
// dots.push("bg-rose-400");
|
||||
map[date] = dots.slice(0, 3);
|
||||
});
|
||||
return map;
|
||||
}, [weekDates, getShiftsForDate]);
|
||||
|
||||
const selectedDateObj = useMemo(() => {
|
||||
const selectedDateStr = useMemo(() => {
|
||||
const inWeek = weekDates.find((d) => formatDateISO(d) === selectedDate);
|
||||
return inWeek ?? weekDates[0] ?? currentDate;
|
||||
}, [selectedDate, weekDates, currentDate]);
|
||||
|
||||
const selectedDateStr = formatDateISO(selectedDateObj);
|
||||
|
||||
const renderMobileDayView = mobileCalendarHeader && (
|
||||
<div className="space-y-3">
|
||||
<div className="rounded-xl border border-(--color-border-light) bg-white p-3">
|
||||
@@ -116,14 +112,13 @@ export default function WeeklySchedule({
|
||||
|
||||
<div className="grid grid-cols-7 gap-1">
|
||||
{weekDates.map((date, i) => {
|
||||
const dateStr = formatDateISO(date);
|
||||
const active = dateStr === selectedDateStr;
|
||||
const dots = statusDotsByDate[dateStr] ?? [];
|
||||
const active = date === selectedDateStr;
|
||||
const dots = statusDotsByDate[date] ?? [];
|
||||
return (
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
onClick={() => setSelectedDate(dateStr)}
|
||||
onClick={() => setSelectedDate(date)}
|
||||
className={`flex cursor-pointer flex-col items-center gap-1 rounded-xl border-none p-1 ${
|
||||
active ? "bg-(--color-primary)/10" : "bg-transparent"
|
||||
}`}
|
||||
@@ -135,14 +130,14 @@ export default function WeeklySchedule({
|
||||
</span>
|
||||
<span
|
||||
className={`flex h-9 w-9 items-center justify-center rounded-full text-sm font-semibold ${
|
||||
active || isToday(date)
|
||||
active || isToday(new Date(date))
|
||||
? "bg-indigo-500 text-white"
|
||||
: i >= 5
|
||||
? "text-pink-500"
|
||||
: "text-sky-500"
|
||||
}`}
|
||||
>
|
||||
{date.getDate()}
|
||||
{new Date(date).getDate()}
|
||||
</span>
|
||||
<div className="flex min-h-2 items-center gap-0.5">
|
||||
{dots.map((dot, idx) => (
|
||||
@@ -160,7 +155,7 @@ export default function WeeklySchedule({
|
||||
|
||||
<div className="space-y-3">
|
||||
{DEPARTMENTS.map((dept) => {
|
||||
const deptShifts = getShiftsForDate(selectedDateObj)
|
||||
const deptShifts = getShiftsForDate(new Date(selectedDateStr));
|
||||
// .filter(
|
||||
// (s) => s.department === dept.id,
|
||||
// );
|
||||
@@ -248,7 +243,7 @@ export default function WeeklySchedule({
|
||||
</td>
|
||||
{weekDates.map((date, i) => {
|
||||
const dateStr = formatDateISO(date);
|
||||
const shifts = getShiftsForDate(date)
|
||||
const shifts = getShiftsForDate(date);
|
||||
// .filter(
|
||||
// (s) => s.department === dept.id,
|
||||
// );
|
||||
|
||||
+12
-14
@@ -12,10 +12,8 @@ import {
|
||||
} from "react";
|
||||
|
||||
import { eateryClient } from "./apollo-clients";
|
||||
import { MOCK_SHIFT_SLOTS } from "./constants";
|
||||
import {
|
||||
type ShiftEntity,
|
||||
type ShiftStatus,
|
||||
type allShiftsQuery,
|
||||
createShiftMutation,
|
||||
} from "./types";
|
||||
@@ -52,9 +50,9 @@ interface ShiftContextType {
|
||||
// Helpers
|
||||
getShiftsForDate: (date: Date) => ShiftEntity[];
|
||||
getShiftsForWeek: (weekStart: Date) => ShiftEntity[];
|
||||
getWeekDates: () => Date[];
|
||||
getWeekDates: () => string[];
|
||||
hasConflict: (
|
||||
date: Date,
|
||||
date: string,
|
||||
startTime: string,
|
||||
endTime: string,
|
||||
staffId: string,
|
||||
@@ -197,12 +195,12 @@ export function ShiftProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
// ── Query helpers ───────────────────────────────────────────────────────
|
||||
|
||||
const getWeekDates = useCallback((): Date[] => {
|
||||
const getWeekDates = useCallback((): string[] => {
|
||||
const monday = getMonday(currentDate);
|
||||
return Array.from({ length: 7 }, (_, i) => {
|
||||
const d = new Date(monday);
|
||||
d.setDate(monday.getDate() + i);
|
||||
return d;
|
||||
return d.toISOString();
|
||||
});
|
||||
}, [currentDate]);
|
||||
|
||||
@@ -220,7 +218,7 @@ export function ShiftProvider({ children }: { children: ReactNode }) {
|
||||
const dates = Array.from({ length: 7 }, (_, i) => {
|
||||
const d = new Date(weekStart);
|
||||
d.setDate(weekStart.getDate() + i);
|
||||
return d;
|
||||
return d.toISOString();
|
||||
});
|
||||
return shifts.filter((s) => dates.includes(s.date));
|
||||
},
|
||||
@@ -229,7 +227,7 @@ export function ShiftProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const hasConflict = useCallback(
|
||||
(
|
||||
date: Date,
|
||||
date: string,
|
||||
startTime: string,
|
||||
endTime: string,
|
||||
staffId: string,
|
||||
@@ -239,7 +237,7 @@ export function ShiftProvider({ children }: { children: ReactNode }) {
|
||||
(s) =>
|
||||
s.date === date &&
|
||||
s.id !== excludeShiftId &&
|
||||
s.registeredStaff.some((rs) => rs.id === staffId) &&
|
||||
s.registeredStaff!.some((rs) => rs.id === staffId) &&
|
||||
timesOverlap(startTime, endTime, s.startTime, s.endTime),
|
||||
);
|
||||
},
|
||||
@@ -249,8 +247,8 @@ export function ShiftProvider({ children }: { children: ReactNode }) {
|
||||
const getWeeklyBudget = useCallback((): number => {
|
||||
const weekDates = getWeekDates();
|
||||
return shifts
|
||||
.filter((s) => weekDates.includes(s.date) && s.registeredStaff.length > 0)
|
||||
.reduce((sum, s) => sum + s.wage * s.registeredStaff.length, 0);
|
||||
.filter((s) => weekDates.includes(s.date) && s.registeredStaff!.length > 0)
|
||||
.reduce((sum, s) => sum + s.wage * s.registeredStaff!.length, 0);
|
||||
}, [shifts, getWeekDates]);
|
||||
|
||||
// ── Shift actions ───────────────────────────────────────────────────────
|
||||
@@ -264,11 +262,11 @@ export function ShiftProvider({ children }: { children: ReactNode }) {
|
||||
const shift = shifts.find((s) => s.id === shiftId);
|
||||
if (!shift) return { success: false, error: "Ca làm không tồn tại." };
|
||||
|
||||
if (shift.registeredStaff.length >= shift.maxStaff) {
|
||||
if (shift.registeredStaff!.length >= shift.maxStaff) {
|
||||
return { success: false, error: "Ca làm đã đủ người." };
|
||||
}
|
||||
|
||||
if (shift.registeredStaff.some((rs) => rs.id === staffId)) {
|
||||
if (shift.registeredStaff!.some((rs) => rs.id === staffId)) {
|
||||
return { success: false, error: "Bạn đã đăng ký ca này rồi." };
|
||||
}
|
||||
|
||||
@@ -311,7 +309,7 @@ export function ShiftProvider({ children }: { children: ReactNode }) {
|
||||
setShifts((prev) =>
|
||||
prev.map((s) => {
|
||||
if (s.id !== shiftId) return s;
|
||||
const updated = s.registeredStaff.filter((rs) => rs.id !== staffId);
|
||||
const updated = s.registeredStaff!.filter((rs) => rs.id !== staffId);
|
||||
return {
|
||||
...s,
|
||||
registeredStaff: updated,
|
||||
|
||||
Reference in New Issue
Block a user