Files
frontend/components/molecules/cards/ShiftCard.tsx
T
2026-04-14 03:05:52 +00:00

120 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import type { ShiftSlot } from "@/lib/types";
import type { ShiftCardProps } from "./ShiftCard.types";
const STATUS_STYLES: Record<
ShiftSlot["status"],
{ bg: string; text: string; label: string }
> = {
available: {
bg: "bg-blue-50 border-blue-200",
text: "text-blue-700",
label: "Còn trống",
},
registered: {
bg: "bg-blue-100 border-blue-400",
text: "text-blue-900",
label: "Đã đăng ký",
},
approved_leave: {
bg: "bg-purple-50 border-purple-300",
text: "text-purple-700",
label: "Nghỉ phép",
},
absent: {
bg: "bg-red-50 border-red-300",
text: "text-red-700",
label: "Vắng mặt",
},
};
function formatWage(wage: number): string {
if (wage >= 1000) {
return `${(wage / 1000).toFixed(0)}k`;
}
return wage.toLocaleString("vi-VN");
}
export default function ShiftCard({
shift,
compact = false,
onClick,
}: ShiftCardProps) {
const style = STATUS_STYLES[shift.status];
if (compact) {
return (
<button
type="button"
onClick={() => onClick?.(shift)}
className={`w-full cursor-pointer rounded-lg border px-2 py-1.5 text-left text-xs transition-shadow hover:shadow-sm ${style.bg} ${style.text}`}
>
<p className="font-semibold">
{shift.startTime} {shift.endTime}
</p>
<p className="mt-0.5 opacity-75">
{shift.durationHours}h · {formatWage(shift.wage)}
</p>
</button>
);
}
return (
<button
type="button"
onClick={() => onClick?.(shift)}
className={`w-full cursor-pointer rounded-xl border p-3 text-left transition-shadow hover:shadow-md ${style.bg} ${style.text}`}
>
<div className="flex items-start justify-between">
<div>
<p className="text-sm font-bold">
{shift.startTime} {shift.endTime}
</p>
<p className="mt-1 text-xs opacity-75">
{shift.durationHours}h · {formatWage(shift.wage)} VND
</p>
</div>
<span
className={`rounded-full px-2 py-0.5 text-[10px] font-semibold ${
shift.status === "available"
? "bg-blue-200 text-blue-800"
: shift.status === "registered"
? "bg-blue-300 text-blue-900"
: shift.status === "approved_leave"
? "bg-purple-200 text-purple-800"
: "bg-red-200 text-red-800"
}`}
>
{style.label}
</span>
</div>
{shift.registeredStaff.length > 0 && (
<div className="mt-2 border-t border-current/10 pt-2">
<p className="text-[10px] font-medium tracking-wide uppercase opacity-60">
Nhân viên ({shift.registeredStaff.length}/{shift.maxStaff})
</p>
<div className="mt-1 flex flex-wrap gap-1">
{shift.registeredStaff.map((s) => (
<span
key={s.id}
className="rounded-full bg-white/60 px-2 py-0.5 text-[10px] font-medium"
>
{s.name}
</span>
))}
</div>
</div>
)}
{shift.status === "available" && shift.registeredStaff.length === 0 && (
<p className="mt-2 text-[10px] italic opacity-50">
{shift.maxStaff} vị trí còn trống
</p>
)}
</button>
);
}