chore: release [ci skip]
This commit is contained in:
@@ -8,7 +8,9 @@ function formatWage(wage: number): string {
|
||||
return wage.toLocaleString("vi-VN");
|
||||
}
|
||||
|
||||
function getShiftPeriod(startTime: string): "morning" | "afternoon" | "evening" {
|
||||
function getShiftPeriod(
|
||||
startTime: string,
|
||||
): "morning" | "afternoon" | "evening" {
|
||||
const h = parseInt(startTime.split(":")[0] ?? "0", 10);
|
||||
if (h < 12) return "morning";
|
||||
if (h < 18) return "afternoon";
|
||||
@@ -36,7 +38,11 @@ const PERIOD_STYLES = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function ShiftCard({ shift, compact = false, onClick }: ShiftCardProps) {
|
||||
export default function ShiftCard({
|
||||
shift,
|
||||
compact = false,
|
||||
onClick,
|
||||
}: ShiftCardProps) {
|
||||
const registeredCount = shift.registeredStaff?.length ?? 0;
|
||||
const period = getShiftPeriod(shift.startTime);
|
||||
const s = PERIOD_STYLES[period];
|
||||
@@ -49,12 +55,16 @@ export default function ShiftCard({ shift, compact = false, onClick }: ShiftCard
|
||||
className={`group w-full cursor-pointer rounded-xl border-l-[3px] bg-white text-left shadow-sm transition hover:-translate-y-px hover:shadow-md ${s.border}`}
|
||||
>
|
||||
<div className="px-2.5 py-2">
|
||||
<p className={`text-xs font-bold leading-tight ${s.text}`}>
|
||||
<p className={`text-xs leading-tight font-bold ${s.text}`}>
|
||||
{shift.startTime}–{shift.endTime}
|
||||
</p>
|
||||
<div className="mt-1 flex items-center justify-between gap-1">
|
||||
<p className="text-[10px] text-(--color-text-muted)">{formatWage(shift.wage ?? 0)}đ</p>
|
||||
<span className={`rounded-full px-1.5 py-px text-[9px] font-semibold ${s.badge}`}>
|
||||
<p className="text-[10px] text-(--color-text-muted)">
|
||||
{formatWage(shift.wage ?? 0)}đ
|
||||
</p>
|
||||
<span
|
||||
className={`rounded-full px-1.5 py-px text-[9px] font-semibold ${s.badge}`}
|
||||
>
|
||||
{registeredCount}/{shift.maxStaff}
|
||||
</span>
|
||||
</div>
|
||||
@@ -82,7 +92,9 @@ export default function ShiftCard({ shift, compact = false, onClick }: ShiftCard
|
||||
{(shift.wage ?? 0).toLocaleString("vi-VN")} VND/ca
|
||||
</p>
|
||||
</div>
|
||||
<span className={`shrink-0 rounded-full px-2.5 py-1 text-xs font-semibold ${s.badge}`}>
|
||||
<span
|
||||
className={`shrink-0 rounded-full px-2.5 py-1 text-xs font-semibold ${s.badge}`}
|
||||
>
|
||||
{registeredCount}/{shift.maxStaff} staff
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,10 @@ export default function LoginForm() {
|
||||
|
||||
const role = (await res.text().catch(() => "")).trim().toLowerCase();
|
||||
|
||||
if (res.ok && (role === "customer" || role === "manager" || role === "staff")) {
|
||||
if (
|
||||
res.ok &&
|
||||
(role === "customer" || role === "manager" || role === "staff")
|
||||
) {
|
||||
sessionStorage.setItem("login_phone", phone);
|
||||
sessionStorage.setItem("login_role", role);
|
||||
router.push(role === "customer" ? "/login/otp" : "/login/password");
|
||||
|
||||
@@ -17,7 +17,9 @@ export default function DeleteConfirm({
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-red-100">
|
||||
<i className="fa-solid fa-trash-can text-xl text-red-500"></i>
|
||||
</div>
|
||||
<h3 className="text-foreground text-base font-bold">Delete "{name}"?</h3>
|
||||
<h3 className="text-foreground text-base font-bold">
|
||||
Delete "{name}"?
|
||||
</h3>
|
||||
<p className="text-sm text-(--color-text-muted)">
|
||||
This action cannot be undone.
|
||||
</p>
|
||||
|
||||
@@ -99,8 +99,8 @@ export default function ProductsTab() {
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-(--color-text-muted)">
|
||||
Showing <strong className="text-foreground">{filtered.length}</strong>{" "}
|
||||
/ {products.length} items
|
||||
Showing <strong className="text-foreground">{filtered.length}</strong> /{" "}
|
||||
{products.length} items
|
||||
</p>
|
||||
|
||||
{/* Table */}
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function ReviewsTab() {
|
||||
{review.comment}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-sm italic text-(--color-text-muted)">
|
||||
<p className="text-sm text-(--color-text-muted) italic">
|
||||
No comment provided.
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -169,9 +169,7 @@ export default function ReviewModal({
|
||||
</div>
|
||||
|
||||
{/* Error message */}
|
||||
{error && (
|
||||
<p className="mb-4 text-sm text-red-500">{error}</p>
|
||||
)}
|
||||
{error && <p className="mb-4 text-sm text-red-500">{error}</p>}
|
||||
|
||||
{/* Footer buttons */}
|
||||
<div className="flex gap-3">
|
||||
|
||||
@@ -4,14 +4,14 @@ import { ProductCard } from "@/components/molecules/cards";
|
||||
import { useCart } from "@/lib/cart-context";
|
||||
import { useManager } from "@/lib/manager-context";
|
||||
|
||||
import type { ProductGridProps } from "./ProductGrid.types";
|
||||
|
||||
function toDisplayUrl(filename: string) {
|
||||
if (!filename) return "/imgs/products/placeholder.jpg";
|
||||
if (filename.startsWith("/") || filename.startsWith("http")) return filename;
|
||||
return `/api/file/${filename}`;
|
||||
}
|
||||
|
||||
import type { ProductGridProps } from "./ProductGrid.types";
|
||||
|
||||
export default function ProductGrid({
|
||||
searchQuery = "",
|
||||
isSidebarOpen = false,
|
||||
|
||||
@@ -8,8 +8,18 @@ import type { MobileShiftViewProps } from "./ShiftSchedule.types";
|
||||
|
||||
const DAY_HEADERS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
||||
const MONTH_NAMES = [
|
||||
"January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December",
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
function formatDateKey(d: Date): string {
|
||||
@@ -24,8 +34,11 @@ function isToday(d: Date): boolean {
|
||||
return formatDateKey(d) === formatDateKey(today);
|
||||
}
|
||||
|
||||
export default function MobileShiftView({ onShiftClick }: MobileShiftViewProps) {
|
||||
const { currentDate, getShiftsForDate, goToNextMonth, goToPrevMonth } = useShift();
|
||||
export default function MobileShiftView({
|
||||
onShiftClick,
|
||||
}: MobileShiftViewProps) {
|
||||
const { currentDate, getShiftsForDate, goToNextMonth, goToPrevMonth } =
|
||||
useShift();
|
||||
const [selectedDate, setSelectedDate] = useState<Date>(new Date(2026, 3, 10));
|
||||
|
||||
const calendarDays = useMemo(() => {
|
||||
@@ -39,12 +52,16 @@ export default function MobileShiftView({ onShiftClick }: MobileShiftViewProps)
|
||||
|
||||
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));
|
||||
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]);
|
||||
|
||||
const selectedShifts = useMemo(() => getShiftsForDate(selectedDate), [selectedDate, getShiftsForDate]);
|
||||
const selectedShifts = useMemo(
|
||||
() => getShiftsForDate(selectedDate),
|
||||
[selectedDate, getShiftsForDate],
|
||||
);
|
||||
|
||||
const dayOfWeek = DAY_HEADERS[(selectedDate.getDay() + 6) % 7];
|
||||
|
||||
@@ -96,7 +113,8 @@ export default function MobileShiftView({ onShiftClick }: MobileShiftViewProps)
|
||||
if (!date) return <div key={`empty-${i}`} className="p-1" />;
|
||||
|
||||
const today = isToday(date);
|
||||
const selected = formatDateKey(date) === formatDateKey(selectedDate);
|
||||
const selected =
|
||||
formatDateKey(date) === formatDateKey(selectedDate);
|
||||
const dayShifts = getShiftsForDate(date);
|
||||
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
|
||||
|
||||
@@ -136,14 +154,16 @@ export default function MobileShiftView({ onShiftClick }: MobileShiftViewProps)
|
||||
<div>
|
||||
<div className="mb-3 flex items-center justify-between px-1">
|
||||
<h3 className="text-sm font-bold text-(--color-primary-dark)">
|
||||
{dayOfWeek},{" "}
|
||||
{selectedDate.getDate()}/{selectedDate.getMonth() + 1}/{selectedDate.getFullYear()}
|
||||
{dayOfWeek}, {selectedDate.getDate()}/{selectedDate.getMonth() + 1}/
|
||||
{selectedDate.getFullYear()}
|
||||
</h3>
|
||||
<span className={`rounded-full px-2.5 py-1 text-xs font-semibold ${
|
||||
selectedShifts.length > 0
|
||||
? "bg-(--color-primary)/10 text-(--color-primary)"
|
||||
: "bg-gray-100 text-(--color-text-muted)"
|
||||
}`}>
|
||||
<span
|
||||
className={`rounded-full px-2.5 py-1 text-xs font-semibold ${
|
||||
selectedShifts.length > 0
|
||||
? "bg-(--color-primary)/10 text-(--color-primary)"
|
||||
: "bg-gray-100 text-(--color-text-muted)"
|
||||
}`}
|
||||
>
|
||||
{selectedShifts.length} shifts
|
||||
</span>
|
||||
</div>
|
||||
@@ -151,8 +171,12 @@ export default function MobileShiftView({ onShiftClick }: MobileShiftViewProps)
|
||||
{selectedShifts.length === 0 ? (
|
||||
<div className="rounded-2xl border border-dashed border-(--color-border-light) py-10 text-center">
|
||||
<i className="fa-regular fa-calendar-xmark mb-3 text-3xl text-gray-200"></i>
|
||||
<p className="text-sm font-medium text-(--color-text-muted)">No shifts</p>
|
||||
<p className="mt-0.5 text-xs text-(--color-text-muted)">No shifts created for this day</p>
|
||||
<p className="text-sm font-medium text-(--color-text-muted)">
|
||||
No shifts
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs text-(--color-text-muted)">
|
||||
No shifts created for this day
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2.5">
|
||||
|
||||
@@ -23,7 +23,10 @@ function isToday(d: Date): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export default function MonthlyCalendar({ onShiftClick, onDateSelect }: MonthlyCalendarProps) {
|
||||
export default function MonthlyCalendar({
|
||||
onShiftClick,
|
||||
onDateSelect,
|
||||
}: MonthlyCalendarProps) {
|
||||
const { currentDate, getShiftsForDate } = useShift();
|
||||
|
||||
const calendarDays = useMemo(() => {
|
||||
@@ -37,7 +40,8 @@ export default function MonthlyCalendar({ onShiftClick, onDateSelect }: MonthlyC
|
||||
|
||||
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));
|
||||
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]);
|
||||
|
||||
@@ -21,8 +21,16 @@ function TimeInput({
|
||||
const hour12 = h24 === 0 ? 12 : h24 > 12 ? h24 - 12 : h24;
|
||||
|
||||
const emit = (newH12: number, newM: number, newIsPM: boolean) => {
|
||||
let h = newIsPM ? (newH12 === 12 ? 12 : newH12 + 12) : (newH12 === 12 ? 0 : newH12);
|
||||
onChange(`${h.toString().padStart(2, "0")}:${newM.toString().padStart(2, "0")}`);
|
||||
let h = newIsPM
|
||||
? newH12 === 12
|
||||
? 12
|
||||
: newH12 + 12
|
||||
: newH12 === 12
|
||||
? 0
|
||||
: newH12;
|
||||
onChange(
|
||||
`${h.toString().padStart(2, "0")}:${newM.toString().padStart(2, "0")}`,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -33,23 +41,27 @@ function TimeInput({
|
||||
min={1}
|
||||
max={12}
|
||||
value={hour12}
|
||||
onChange={(e) => emit(Math.min(12, Math.max(1, Number(e.target.value))), m, isPM)}
|
||||
onChange={(e) =>
|
||||
emit(Math.min(12, Math.max(1, Number(e.target.value))), m, isPM)
|
||||
}
|
||||
className="text-foreground w-12 border-none bg-transparent py-2.5 pl-3 text-sm outline-none"
|
||||
/>
|
||||
<span className="select-none text-sm text-(--color-text-muted)">:</span>
|
||||
<span className="text-sm text-(--color-text-muted) select-none">:</span>
|
||||
<input
|
||||
title={`${title} minute`}
|
||||
type="number"
|
||||
min={0}
|
||||
max={59}
|
||||
value={m.toString().padStart(2, "0")}
|
||||
onChange={(e) => emit(hour12, Math.min(59, Math.max(0, Number(e.target.value))), isPM)}
|
||||
onChange={(e) =>
|
||||
emit(hour12, Math.min(59, Math.max(0, Number(e.target.value))), isPM)
|
||||
}
|
||||
className="text-foreground w-10 border-none bg-transparent py-2.5 text-sm outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => emit(hour12, m, !isPM)}
|
||||
className="ml-1 mr-2 cursor-pointer rounded-lg border-none bg-(--color-primary)/10 px-2 py-1 text-xs font-bold text-(--color-primary) transition hover:bg-(--color-primary) hover:text-white"
|
||||
className="mr-2 ml-1 cursor-pointer rounded-lg border-none bg-(--color-primary)/10 px-2 py-1 text-xs font-bold text-(--color-primary) transition hover:bg-(--color-primary) hover:text-white"
|
||||
>
|
||||
{isPM ? "PM" : "AM"}
|
||||
</button>
|
||||
|
||||
@@ -32,7 +32,9 @@ export default function ShiftDetailModal({
|
||||
// const dept = DEPARTMENTS.find((d) => d.id === shift.department);
|
||||
const isManager = user?.role === "manager";
|
||||
const registeredStaff = shift.registeredStaff ?? [];
|
||||
const isRegistered = user ? registeredStaff.some((s) => s.id === user.id) : false;
|
||||
const isRegistered = user
|
||||
? registeredStaff.some((s) => s.id === user.id)
|
||||
: false;
|
||||
const isFull = registeredStaff.length >= shift.maxStaff;
|
||||
|
||||
const handleRegister = async () => {
|
||||
@@ -126,15 +128,14 @@ export default function ShiftDetailModal({
|
||||
Date
|
||||
</p>
|
||||
<p className="text-foreground mt-1 text-sm font-bold">
|
||||
{parseShiftDate(shift.date as Date | string | undefined)?.toLocaleDateString(
|
||||
"vi-VN",
|
||||
{
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
},
|
||||
) ?? "—"}
|
||||
{parseShiftDate(
|
||||
shift.date as Date | string | undefined,
|
||||
)?.toLocaleDateString("vi-VN", {
|
||||
weekday: "long",
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
}) ?? "—"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-xl bg-gray-50 p-3">
|
||||
@@ -164,8 +165,7 @@ export default function ShiftDetailModal({
|
||||
{/* Registered staff */}
|
||||
<div>
|
||||
<p className="mb-2 text-xs font-semibold text-(--color-text-secondary)">
|
||||
Registered staff ({registeredStaff.length}/
|
||||
{shift.maxStaff})
|
||||
Registered staff ({registeredStaff.length}/{shift.maxStaff})
|
||||
</p>
|
||||
{registeredStaff.length === 0 ? (
|
||||
<p className="text-xs text-(--color-text-muted) italic">
|
||||
|
||||
@@ -8,8 +8,18 @@ import { useMemo, useState } from "react";
|
||||
import type { WeeklyScheduleProps } from "./ShiftSchedule.types";
|
||||
|
||||
const MONTH_NAMES = [
|
||||
"January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December",
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
const DAY_LABELS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
||||
@@ -32,13 +42,23 @@ export default function WeeklySchedule({
|
||||
onCreateShift,
|
||||
mobileCalendarHeader = false,
|
||||
}: WeeklyScheduleProps) {
|
||||
const { currentDate, getWeekDates, getShiftsForDate, goToNextWeek, goToPrevWeek } = useShift();
|
||||
const {
|
||||
currentDate,
|
||||
getWeekDates,
|
||||
getShiftsForDate,
|
||||
goToNextWeek,
|
||||
goToPrevWeek,
|
||||
} = useShift();
|
||||
const weekDates = getWeekDates();
|
||||
|
||||
const [selectedDate, setSelectedDate] = useState<Date>(weekDates[0] ?? currentDate);
|
||||
const [selectedDate, setSelectedDate] = useState<Date>(
|
||||
weekDates[0] ?? currentDate,
|
||||
);
|
||||
|
||||
const selectedDateRef = useMemo(() => {
|
||||
return weekDates.find((d) => d === selectedDate) ?? weekDates[0] ?? currentDate;
|
||||
return (
|
||||
weekDates.find((d) => d === selectedDate) ?? weekDates[0] ?? currentDate
|
||||
);
|
||||
}, [selectedDate, weekDates, currentDate]);
|
||||
|
||||
// ── Mobile calendar header view ────────────────────────────────────────────
|
||||
@@ -83,18 +103,22 @@ export default function WeeklySchedule({
|
||||
onClick={() => setSelectedDate(date)}
|
||||
className="flex cursor-pointer flex-col items-center gap-1 rounded-xl border-none bg-transparent py-1.5 transition"
|
||||
>
|
||||
<span className={`text-[10px] font-semibold uppercase ${i >= 5 ? "text-rose-400" : "text-(--color-text-muted)"}`}>
|
||||
<span
|
||||
className={`text-[10px] font-semibold uppercase ${i >= 5 ? "text-rose-400" : "text-(--color-text-muted)"}`}
|
||||
>
|
||||
{DAY_LABELS[i]}
|
||||
</span>
|
||||
<span className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold transition ${
|
||||
today
|
||||
? "bg-(--color-primary) text-white shadow-sm"
|
||||
: active
|
||||
? "bg-(--color-primary)/15 text-(--color-primary)"
|
||||
: i >= 5
|
||||
? "text-rose-500"
|
||||
: "text-(--color-text-secondary)"
|
||||
}`}>
|
||||
<span
|
||||
className={`flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold transition ${
|
||||
today
|
||||
? "bg-(--color-primary) text-white shadow-sm"
|
||||
: active
|
||||
? "bg-(--color-primary)/15 text-(--color-primary)"
|
||||
: i >= 5
|
||||
? "text-rose-500"
|
||||
: "text-(--color-text-secondary)"
|
||||
}`}
|
||||
>
|
||||
{new Date(date).getDate()}
|
||||
</span>
|
||||
<div className="flex min-h-2 items-center gap-px">
|
||||
@@ -123,12 +147,18 @@ export default function WeeklySchedule({
|
||||
{dayShifts.length === 0 && !onCreateShift ? (
|
||||
<div className="rounded-2xl border border-dashed border-(--color-border-light) py-10 text-center">
|
||||
<i className="fa-regular fa-calendar-xmark mb-2 text-3xl text-gray-200"></i>
|
||||
<p className="text-sm text-(--color-text-muted)">No shifts today</p>
|
||||
<p className="text-sm text-(--color-text-muted)">
|
||||
No shifts today
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2.5">
|
||||
{dayShifts.map((shift) => (
|
||||
<ShiftCard key={shift.id} shift={shift} onClick={onShiftClick} />
|
||||
<ShiftCard
|
||||
key={shift.id}
|
||||
shift={shift}
|
||||
onClick={onShiftClick}
|
||||
/>
|
||||
))}
|
||||
{onCreateShift && (
|
||||
<button
|
||||
@@ -169,18 +199,23 @@ export default function WeeklySchedule({
|
||||
: "bg-gray-50/80 font-semibold text-(--color-text-muted)"
|
||||
}`}
|
||||
>
|
||||
<span className="block text-[11px] font-semibold uppercase tracking-wider">
|
||||
<span className="block text-[11px] font-semibold tracking-wider uppercase">
|
||||
{DAY_LABELS[i]}
|
||||
</span>
|
||||
<span className={`mt-0.5 inline-flex h-7 w-7 items-center justify-center rounded-full text-sm font-bold ${
|
||||
today
|
||||
? "bg-(--color-primary) text-white shadow-sm"
|
||||
: "text-(--color-text-secondary)"
|
||||
}`}>
|
||||
<span
|
||||
className={`mt-0.5 inline-flex h-7 w-7 items-center justify-center rounded-full text-sm font-bold ${
|
||||
today
|
||||
? "bg-(--color-primary) text-white shadow-sm"
|
||||
: "text-(--color-text-secondary)"
|
||||
}`}
|
||||
>
|
||||
{date.getDate()}
|
||||
</span>
|
||||
<span className={`mt-0.5 block text-[10px] font-normal ${today ? "text-(--color-primary)/70" : "text-(--color-text-muted)"}`}>
|
||||
{(date.getMonth() + 1).toString().padStart(2, "0")}/{date.getFullYear()}
|
||||
<span
|
||||
className={`mt-0.5 block text-[10px] font-normal ${today ? "text-(--color-primary)/70" : "text-(--color-text-muted)"}`}
|
||||
>
|
||||
{(date.getMonth() + 1).toString().padStart(2, "0")}/
|
||||
{date.getFullYear()}
|
||||
</span>
|
||||
</th>
|
||||
);
|
||||
@@ -189,11 +224,16 @@ export default function WeeklySchedule({
|
||||
</thead>
|
||||
<tbody>
|
||||
{DEPARTMENTS.map((dept, deptIdx) => (
|
||||
<tr key={dept.id} className={deptIdx % 2 === 0 ? "bg-white" : "bg-gray-50/40"}>
|
||||
<tr
|
||||
key={dept.id}
|
||||
className={deptIdx % 2 === 0 ? "bg-white" : "bg-gray-50/40"}
|
||||
>
|
||||
<td className="border-r border-b border-(--color-border-light) px-4 py-3 align-top">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-(--color-primary)/10">
|
||||
<i className={`${dept.icon} text-xs text-(--color-primary)`}></i>
|
||||
<i
|
||||
className={`${dept.icon} text-xs text-(--color-primary)`}
|
||||
></i>
|
||||
</div>
|
||||
<span className="text-xs font-semibold text-(--color-text-secondary)">
|
||||
{dept.name}
|
||||
@@ -212,7 +252,12 @@ export default function WeeklySchedule({
|
||||
>
|
||||
<div className="flex min-h-[88px] flex-col gap-1.5">
|
||||
{shifts.map((shift) => (
|
||||
<ShiftCard key={shift.id} shift={shift} compact onClick={onShiftClick} />
|
||||
<ShiftCard
|
||||
key={shift.id}
|
||||
shift={shift}
|
||||
compact
|
||||
onClick={onShiftClick}
|
||||
/>
|
||||
))}
|
||||
{onCreateShift && (
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user