Refactor: Update Vietnamese text to English across multiple components
Release package / release (pull_request) Successful in 3m23s

- 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.
This commit is contained in:
Thanh Quy - wolf
2026-05-14 21:09:18 +07:00
parent 36c63f9a08
commit 54d5d6150c
16 changed files with 144 additions and 146 deletions
+14 -15
View File
@@ -151,7 +151,7 @@ export default function ManagerSignupPage() {
href="/login" href="/login"
className="flex w-full items-center justify-center rounded-xl border-2 border-(--color-primary) bg-white py-3 font-semibold text-(--color-primary) no-underline transition-all duration-150 hover:bg-(--color-primary) hover:text-white" className="flex w-full items-center justify-center rounded-xl border-2 border-(--color-primary) bg-white py-3 font-semibold text-(--color-primary) no-underline transition-all duration-150 hover:bg-(--color-primary) hover:text-white"
> >
Quay lại đăng nhập Back to login
</Link> </Link>
</div> </div>
)} )}
@@ -164,10 +164,10 @@ export default function ManagerSignupPage() {
</div> </div>
<div className="text-center"> <div className="text-center">
<h2 className="mb-2 text-lg font-semibold text-(--color-text-primary)"> <h2 className="mb-2 text-lg font-semibold text-(--color-text-primary)">
Không thể kết nối Unable to connect
</h2> </h2>
<p className="text-sm text-(--color-text-muted)"> <p className="text-sm text-(--color-text-muted)">
Không thể kiểm tra trạng thái đăng . Vui lòng thử lại. Unable to check registration status. Please try again.
</p> </p>
</div> </div>
<Button <Button
@@ -185,13 +185,13 @@ export default function ManagerSignupPage() {
.catch(() => setPageState("error")); .catch(() => setPageState("error"));
}} }}
> >
Thử lại Retry
</Button> </Button>
<Link <Link
href="/login" href="/login"
className="text-sm text-(--color-primary) underline" className="text-sm text-(--color-primary) underline"
> >
Quay lại đăng nhập Back to login
</Link> </Link>
</div> </div>
)} )}
@@ -202,15 +202,15 @@ export default function ManagerSignupPage() {
{[ {[
{ {
id: "name", id: "name",
label: "Họ tên", label: "Full name",
icon: "fa-user", icon: "fa-user",
placeholder: "Nguyễn Văn A", placeholder: "John Doe",
field: "name" as const, field: "name" as const,
type: "text", type: "text",
}, },
{ {
id: "phone", id: "phone",
label: "Số điện thoại", label: "Phone number",
icon: "fa-phone", icon: "fa-phone",
placeholder: "0987654321", placeholder: "0987654321",
field: "phone" as const, field: "phone" as const,
@@ -218,15 +218,15 @@ export default function ManagerSignupPage() {
}, },
{ {
id: "password", id: "password",
label: "Mật khẩu", label: "Password",
icon: "fa-lock", icon: "fa-lock",
placeholder: "Ít nhất 6 ký tự", placeholder: "At least 6 characters",
field: "password" as const, field: "password" as const,
type: "password", type: "password",
}, },
{ {
id: "eateryName", id: "eateryName",
label: "Tên nhà hàng", label: "Restaurant name",
icon: "fa-store", icon: "fa-store",
placeholder: "Coffee & More", placeholder: "Coffee & More",
field: "eateryName" as const, field: "eateryName" as const,
@@ -280,18 +280,17 @@ export default function ManagerSignupPage() {
> >
{isLoading ? ( {isLoading ? (
<> <>
<i className="fa-solid fa-spinner fa-spin mr-2"></i>Đang xử <i className="fa-solid fa-spinner fa-spin mr-2"></i>Processing...
...
</> </>
) : ( ) : (
"Đăng ký" "Register"
)} )}
</Button> </Button>
<Link <Link
href="/login" href="/login"
className="flex w-full items-center justify-center rounded-xl border-2 border-(--color-primary) bg-white py-3 font-semibold text-(--color-primary) no-underline transition-all duration-150 hover:bg-(--color-primary) hover:text-white" className="flex w-full items-center justify-center rounded-xl border-2 border-(--color-primary) bg-white py-3 font-semibold text-(--color-primary) no-underline transition-all duration-150 hover:bg-(--color-primary) hover:text-white"
> >
Đã tài khoản? Đăng nhập Already have an account? Sign in
</Link> </Link>
</div> </div>
</form> </form>
+6 -6
View File
@@ -41,13 +41,13 @@ export default function PaymentPage() {
<div className="bg-card overflow-hidden rounded-2xl border border-(--color-border-light)"> <div className="bg-card overflow-hidden rounded-2xl border border-(--color-border-light)">
<div className="border-b border-(--color-border-light) px-4 py-3"> <div className="border-b border-(--color-border-light) px-4 py-3">
<h1 className="text-foreground text-lg font-bold md:text-xl"> <h1 className="text-foreground text-lg font-bold md:text-xl">
Trang thanh toán Payment
</h1> </h1>
</div> </div>
{items?.length === 0 ? ( {items?.length === 0 ? (
<div className="px-4 py-10 text-center text-(--color-text-muted)"> <div className="px-4 py-10 text-center text-(--color-text-muted)">
Chưa sản phẩm nào trong giỏ hàng. Your cart is empty.
</div> </div>
) : ( ) : (
<div className="overflow-x-auto"> <div className="overflow-x-auto">
@@ -102,7 +102,7 @@ export default function PaymentPage() {
<button <button
onClick={() => decreaseQty(id)} onClick={() => decreaseQty(id)}
className="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-(--color-border) hover:bg-(--color-border-light)" className="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-(--color-border) hover:bg-(--color-border-light)"
aria-label={`Giảm số lượng ${name}`} aria-label={`Decrease quantity of ${name}`}
> >
- -
</button> </button>
@@ -114,12 +114,12 @@ export default function PaymentPage() {
setQuantity(id, Number(e.target.value)) setQuantity(id, Number(e.target.value))
} }
className="h-8 w-16 rounded-lg border border-(--color-border) bg-transparent text-center" className="h-8 w-16 rounded-lg border border-(--color-border) bg-transparent text-center"
title="Nhập số lượng" title="Enter quantity"
/> />
<button <button
onClick={() => increaseQty(id)} onClick={() => increaseQty(id)}
className="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-(--color-border) hover:bg-(--color-border-light)" className="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-(--color-border) hover:bg-(--color-border-light)"
aria-label={`Tăng số lượng ${name}`} aria-label={`Increase quantity of ${name}`}
> >
+ +
</button> </button>
@@ -131,7 +131,7 @@ export default function PaymentPage() {
variant="danger" variant="danger"
size="md" size="md"
style="payment" style="payment"
aria-label={`Xóa ${name} khỏi giỏ hàng`} aria-label={`Remove ${name} from cart`}
> >
Delete product Delete product
</Button> </Button>
+20 -21
View File
@@ -27,13 +27,13 @@ export default function CreateStaffPage() {
const validate = () => { const validate = () => {
const next = { name: "", phone: "", password: "", submit: "" }; const next = { name: "", phone: "", password: "", submit: "" };
if (!form.name.trim()) next.name = "Vui lòng nhập họ tên"; if (!form.name.trim()) next.name = "Please enter full name";
if (!form.phone.trim()) next.phone = "Vui lòng nhập số điện thoại"; if (!form.phone.trim()) next.phone = "Please enter phone number";
else if (!validatePhone(form.phone)) else if (!validatePhone(form.phone))
next.phone = "Số điện thoại không hợp lệ (vd: 0987654321)"; next.phone = "Invalid phone number (e.g. 0987654321)";
if (!form.password.trim()) next.password = "Vui lòng nhập mật khẩu"; if (!form.password.trim()) next.password = "Please enter password";
else if (form.password.length < 6) else if (form.password.length < 6)
next.password = "Mật khẩu phải có ít nhất 6 ký tự"; next.password = "Password must be at least 6 characters";
setErrors(next); setErrors(next);
return !next.name && !next.phone && !next.password; return !next.name && !next.phone && !next.password;
}; };
@@ -55,17 +55,17 @@ export default function CreateStaffPage() {
} else { } else {
const errorCode = (await res.text().catch(() => "")).trim(); const errorCode = (await res.text().catch(() => "")).trim();
const errorMap: Record<string, string> = { const errorMap: Record<string, string> = {
ExistedUser: "Số điện thoại đã được đăng ký", ExistedUser: "Phone number already registered",
InvalidPhoneNumber: "Số điện thoại không hợp lệ", InvalidPhoneNumber: "Invalid phone number",
InvalidManager: "Không tìm thấy tài khoản manager", InvalidManager: "Manager account not found",
}; };
setErrors({ setErrors({
...errors, ...errors,
submit: errorMap[errorCode] ?? "Đã có lỗi xảy ra, vui lòng thử lại", submit: errorMap[errorCode] ?? "An error occurred, please try again",
}); });
} }
} catch { } catch {
setErrors({ ...errors, submit: "Không thể kết nối, vui lòng thử lại" }); setErrors({ ...errors, submit: "Unable to connect, please try again" });
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
@@ -79,10 +79,10 @@ export default function CreateStaffPage() {
<i className="fa-solid fa-user-plus text-2xl text-(--color-primary)"></i> <i className="fa-solid fa-user-plus text-2xl text-(--color-primary)"></i>
</div> </div>
<h1 className="mb-1 text-2xl font-bold text-(--color-primary-dark)"> <h1 className="mb-1 text-2xl font-bold text-(--color-primary-dark)">
Tạo tài khoản nhân viên Create Staff Account
</h1> </h1>
<p className="text-sm text-(--color-text-muted)"> <p className="text-sm text-(--color-text-muted)">
Thêm nhân viên mới cho nhà hàng Add a new staff member to the restaurant
</p> </p>
</div> </div>
@@ -90,15 +90,15 @@ export default function CreateStaffPage() {
{[ {[
{ {
id: "name", id: "name",
label: "Họ tên", label: "Full name",
icon: "fa-user", icon: "fa-user",
placeholder: "Nguyễn Văn A", placeholder: "John Doe",
field: "name" as const, field: "name" as const,
type: "text", type: "text",
}, },
{ {
id: "phone", id: "phone",
label: "Số điện thoại", label: "Phone number",
icon: "fa-phone", icon: "fa-phone",
placeholder: "0987654321", placeholder: "0987654321",
field: "phone" as const, field: "phone" as const,
@@ -106,9 +106,9 @@ export default function CreateStaffPage() {
}, },
{ {
id: "password", id: "password",
label: "Mật khẩu", label: "Password",
icon: "fa-lock", icon: "fa-lock",
placeholder: "Ít nhất 6 ký tự", placeholder: "At least 6 characters",
field: "password" as const, field: "password" as const,
type: "password", type: "password",
}, },
@@ -160,18 +160,17 @@ export default function CreateStaffPage() {
> >
{isLoading ? ( {isLoading ? (
<> <>
<i className="fa-solid fa-spinner fa-spin mr-2"></i>Đang xử <i className="fa-solid fa-spinner fa-spin mr-2"></i>Processing...
...
</> </>
) : ( ) : (
"Tạo tài khoản" "Create account"
)} )}
</Button> </Button>
<Link <Link
href="/manager" href="/manager"
className="flex w-full items-center justify-center rounded-xl border-2 border-(--color-primary) bg-white py-3 font-semibold text-(--color-primary) no-underline transition-all duration-150 hover:bg-(--color-primary) hover:text-white" className="flex w-full items-center justify-center rounded-xl border-2 border-(--color-primary) bg-white py-3 font-semibold text-(--color-primary) no-underline transition-all duration-150 hover:bg-(--color-primary) hover:text-white"
> >
Quay lại Dashboard Back to Dashboard
</Link> </Link>
</div> </div>
</form> </form>
+18 -18
View File
@@ -96,7 +96,7 @@ export default function StaffSchedulePage() {
<nav className="flex-1 space-y-0.5 overflow-y-auto p-3"> <nav className="flex-1 space-y-0.5 overflow-y-auto p-3">
{/* View toggle */} {/* View toggle */}
<p className="mb-1 px-3 pt-2 text-[10px] font-bold tracking-widest text-(--color-text-muted) uppercase"> <p className="mb-1 px-3 pt-2 text-[10px] font-bold tracking-widest text-(--color-text-muted) uppercase">
Chế đ xem View
</p> </p>
<button <button
type="button" type="button"
@@ -108,7 +108,7 @@ export default function StaffSchedulePage() {
}`} }`}
> >
<i className={`fa-solid fa-table-columns w-4 text-center ${view === "week" ? "text-(--color-primary)" : ""}`}></i> <i className={`fa-solid fa-table-columns w-4 text-center ${view === "week" ? "text-(--color-primary)" : ""}`}></i>
<span className="flex-1 text-left">Theo tuần</span> <span className="flex-1 text-left">Weekly</span>
{view === "week" && ( {view === "week" && (
<span className="h-1.5 w-1.5 rounded-full bg-(--color-primary)"></span> <span className="h-1.5 w-1.5 rounded-full bg-(--color-primary)"></span>
)} )}
@@ -123,7 +123,7 @@ export default function StaffSchedulePage() {
}`} }`}
> >
<i className={`fa-solid fa-calendar w-4 text-center ${view === "month" ? "text-(--color-primary)" : ""}`}></i> <i className={`fa-solid fa-calendar w-4 text-center ${view === "month" ? "text-(--color-primary)" : ""}`}></i>
<span className="flex-1 text-left">Theo tháng</span> <span className="flex-1 text-left">Monthly</span>
{view === "month" && ( {view === "month" && (
<span className="h-1.5 w-1.5 rounded-full bg-(--color-primary)"></span> <span className="h-1.5 w-1.5 rounded-full bg-(--color-primary)"></span>
)} )}
@@ -132,7 +132,7 @@ export default function StaffSchedulePage() {
{/* Navigation */} {/* Navigation */}
<div className="mt-2 pt-2"> <div className="mt-2 pt-2">
<p className="mb-1 px-3 text-[10px] font-bold tracking-widest text-(--color-text-muted) uppercase"> <p className="mb-1 px-3 text-[10px] font-bold tracking-widest text-(--color-text-muted) uppercase">
Điều hướng Navigation
</p> </p>
<button <button
type="button" type="button"
@@ -140,7 +140,7 @@ export default function StaffSchedulePage() {
className="flex w-full cursor-pointer items-center gap-3 rounded-xl border-none bg-transparent px-3 py-2.5 text-sm font-medium text-(--color-text-secondary) transition-all hover:bg-gray-50 hover:text-(--color-primary-dark)" className="flex w-full cursor-pointer items-center gap-3 rounded-xl border-none bg-transparent px-3 py-2.5 text-sm font-medium text-(--color-text-secondary) transition-all hover:bg-gray-50 hover:text-(--color-primary-dark)"
> >
<i className="fa-solid fa-crosshairs w-4 text-center"></i> <i className="fa-solid fa-crosshairs w-4 text-center"></i>
<span className="flex-1 text-left">Hôm nay</span> <span className="flex-1 text-left">Today</span>
</button> </button>
</div> </div>
@@ -148,7 +148,7 @@ export default function StaffSchedulePage() {
{isManager && ( {isManager && (
<div className="mt-2 pt-2"> <div className="mt-2 pt-2">
<p className="mb-1 px-3 text-[10px] font-bold tracking-widest text-(--color-text-muted) uppercase"> <p className="mb-1 px-3 text-[10px] font-bold tracking-widest text-(--color-text-muted) uppercase">
Quản Manager
</p> </p>
<Link <Link
href="/manager" href="/manager"
@@ -165,7 +165,7 @@ export default function StaffSchedulePage() {
className="mt-1 flex w-full cursor-pointer items-center gap-3 rounded-xl border-none bg-(--color-primary)/10 px-3 py-2.5 text-sm font-semibold text-(--color-primary) transition-all hover:bg-(--color-primary)/20" className="mt-1 flex w-full cursor-pointer items-center gap-3 rounded-xl border-none bg-(--color-primary)/10 px-3 py-2.5 text-sm font-semibold text-(--color-primary) transition-all hover:bg-(--color-primary)/20"
> >
<i className="fa-solid fa-plus w-4 text-center"></i> <i className="fa-solid fa-plus w-4 text-center"></i>
<span className="flex-1 text-left">Tạo ca mới</span> <span className="flex-1 text-left">Create shift</span>
</button> </button>
</div> </div>
)} )}
@@ -175,7 +175,7 @@ export default function StaffSchedulePage() {
{isManager && ( {isManager && (
<div className="rounded-xl bg-gradient-to-br from-(--color-primary)/10 to-(--color-primary)/5 p-3"> <div className="rounded-xl bg-gradient-to-br from-(--color-primary)/10 to-(--color-primary)/5 p-3">
<p className="text-[10px] font-bold tracking-wider text-(--color-text-muted) uppercase"> <p className="text-[10px] font-bold tracking-wider text-(--color-text-muted) uppercase">
Ngân sách tuần Weekly budget
</p> </p>
<p className="mt-1 text-xl font-bold text-(--color-primary)"> <p className="mt-1 text-xl font-bold text-(--color-primary)">
{weeklyBudget >= 1_000_000 {weeklyBudget >= 1_000_000
@@ -187,12 +187,12 @@ export default function StaffSchedulePage() {
)} )}
<div className="rounded-xl bg-gray-50 p-3"> <div className="rounded-xl bg-gray-50 p-3">
<p className="text-[10px] font-bold tracking-wider text-(--color-text-muted) uppercase"> <p className="text-[10px] font-bold tracking-wider text-(--color-text-muted) uppercase">
Ca tuần này Shifts this week
</p> </p>
<p className="mt-1 text-xl font-bold text-(--color-text-secondary)"> <p className="mt-1 text-xl font-bold text-(--color-text-secondary)">
{totalShiftsThisWeek} {totalShiftsThisWeek}
</p> </p>
<p className="text-[10px] text-(--color-text-muted)">ca làm</p> <p className="text-[10px] text-(--color-text-muted)">shifts</p>
</div> </div>
</div> </div>
</nav> </nav>
@@ -242,12 +242,12 @@ export default function StaffSchedulePage() {
{/* Title */} {/* Title */}
<div> <div>
<h1 className="text-foreground text-base font-bold md:text-lg"> <h1 className="text-foreground text-base font-bold md:text-lg">
{isManager ? "Quản lý ca làm" : "Đăng ký ca làm"} {isManager ? "Manage Shifts" : "Register Shifts"}
</h1> </h1>
<p className="text-xs text-(--color-text-muted)"> <p className="text-xs text-(--color-text-muted)">
{view === "week" {view === "week"
? `Tuần: ${weekLabel}` ? `Week: ${weekLabel}`
: `Tháng: ${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getFullYear()}`} : `Month: ${MONTH_NAMES[currentDate.getMonth()]} ${currentDate.getFullYear()}`}
</p> </p>
</div> </div>
@@ -263,7 +263,7 @@ export default function StaffSchedulePage() {
: "bg-transparent text-(--color-text-secondary)" : "bg-transparent text-(--color-text-secondary)"
}`} }`}
> >
Tuần Week
</button> </button>
<button <button
type="button" type="button"
@@ -274,7 +274,7 @@ export default function StaffSchedulePage() {
: "bg-transparent text-(--color-text-secondary)" : "bg-transparent text-(--color-text-secondary)"
}`} }`}
> >
Tháng Month
</button> </button>
</div> </div>
@@ -293,7 +293,7 @@ export default function StaffSchedulePage() {
onClick={goToToday} onClick={goToToday}
className="cursor-pointer rounded-lg border border-(--color-border-light) bg-white px-3 py-1.5 text-xs font-semibold text-(--color-text-secondary) transition hover:border-(--color-primary)/30 hover:bg-(--color-primary)/5 hover:text-(--color-primary)" className="cursor-pointer rounded-lg border border-(--color-border-light) bg-white px-3 py-1.5 text-xs font-semibold text-(--color-text-secondary) transition hover:border-(--color-primary)/30 hover:bg-(--color-primary)/5 hover:text-(--color-primary)"
> >
Hôm nay Today
</button> </button>
<button <button
title="Next" title="Next"
@@ -313,7 +313,7 @@ export default function StaffSchedulePage() {
className="hidden cursor-pointer items-center gap-2 rounded-xl border-none bg-(--color-primary) px-4 py-2 text-xs font-bold text-white shadow-sm transition hover:opacity-90 md:flex" className="hidden cursor-pointer items-center gap-2 rounded-xl border-none bg-(--color-primary) px-4 py-2 text-xs font-bold text-white shadow-sm transition hover:opacity-90 md:flex"
> >
<i className="fa-solid fa-plus"></i> <i className="fa-solid fa-plus"></i>
Tạo ca mới Create shift
</button> </button>
)} )}
@@ -373,7 +373,7 @@ export default function StaffSchedulePage() {
{/* FAB for mobile manager */} {/* FAB for mobile manager */}
{isManager && ( {isManager && (
<button <button
title="Tạo ca mới" title="Create shift"
type="button" type="button"
onClick={() => { setCreateDate(undefined); setCreateOpen(true); }} onClick={() => { setCreateDate(undefined); setCreateOpen(true); }}
className="fixed right-4 bottom-6 z-30 flex h-14 w-14 cursor-pointer items-center justify-center rounded-full border-none bg-(--color-primary) text-white shadow-xl transition hover:opacity-90 md:hidden" className="fixed right-4 bottom-6 z-30 flex h-14 w-14 cursor-pointer items-center justify-center rounded-full border-none bg-(--color-primary) text-white shadow-xl transition hover:opacity-90 md:hidden"
+2 -2
View File
@@ -83,14 +83,14 @@ export default function ShiftCard({ shift, compact = false, onClick }: ShiftCard
</p> </p>
</div> </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} người {registeredCount}/{shift.maxStaff} staff
</span> </span>
</div> </div>
{registeredCount > 0 && ( {registeredCount > 0 && (
<div className="mt-3 border-t border-gray-100 pt-3"> <div className="mt-3 border-t border-gray-100 pt-3">
<p className="mb-2 text-[10px] font-semibold tracking-wider text-(--color-text-muted) uppercase"> <p className="mb-2 text-[10px] font-semibold tracking-wider text-(--color-text-muted) uppercase">
Nhân viên đăng Registered staff
</p> </p>
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
{shift.registeredStaff!.map((staff) => ( {shift.registeredStaff!.map((staff) => (
@@ -6,7 +6,7 @@ export default function SearchBar({
value, value,
onChange, onChange,
onClear, onClear,
placeholder = "Tìm kiếm...", placeholder = "Search...",
className = "", className = "",
}: SearchBarProps) { }: SearchBarProps) {
return ( return (
@@ -17,14 +17,14 @@ export default function SearchBar({
value={value} value={value}
onChange={(e) => onChange(e.target.value)} onChange={(e) => onChange(e.target.value)}
placeholder={placeholder} placeholder={placeholder}
aria-label="Tìm kiếm món ăn" aria-label="Search items"
className="bg-card text-foreground border-border placeholder:text-muted-foreground focus:border-primary focus:ring-primary focus:ring-opacity-20 w-full rounded-xl border py-2 pr-9 pl-9 text-sm transition-all duration-150 outline-none focus:ring-2" className="bg-card text-foreground border-border placeholder:text-muted-foreground focus:border-primary focus:ring-primary focus:ring-opacity-20 w-full rounded-xl border py-2 pr-9 pl-9 text-sm transition-all duration-150 outline-none focus:ring-2"
/> />
{value && ( {value && (
<button <button
onClick={onClear} onClick={onClear}
title="Xóa tìm kiếm" title="Clear search"
aria-label="Xóa tìm kiếm" aria-label="Clear search"
className="absolute top-1/2 right-3 -translate-y-1/2 cursor-pointer border-none bg-transparent p-0 text-(--color-text-muted) transition-colors duration-150 hover:text-(--color-primary)" className="absolute top-1/2 right-3 -translate-y-1/2 cursor-pointer border-none bg-transparent p-0 text-(--color-text-muted) transition-colors duration-150 hover:text-(--color-primary)"
> >
<i className="fa-solid fa-xmark text-sm"></i> <i className="fa-solid fa-xmark text-sm"></i>
@@ -17,9 +17,9 @@ export default function DeleteConfirm({
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-red-100"> <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> <i className="fa-solid fa-trash-can text-xl text-red-500"></i>
</div> </div>
<h3 className="text-foreground text-base font-bold">Xóa "{name}"?</h3> <h3 className="text-foreground text-base font-bold">Delete "{name}"?</h3>
<p className="text-sm text-(--color-text-muted)"> <p className="text-sm text-(--color-text-muted)">
Hành đng này không thể hoàn tác. This action cannot be undone.
</p> </p>
</div> </div>
<div className="flex gap-3"> <div className="flex gap-3">
@@ -27,13 +27,13 @@ export default function DeleteConfirm({
onClick={onClose} onClick={onClose}
className="flex-1 cursor-pointer rounded-xl border border-(--color-border) bg-white px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-(--color-border-light)" className="flex-1 cursor-pointer rounded-xl border border-(--color-border) bg-white px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-(--color-border-light)"
> >
Hủy Cancel
</button> </button>
<button <button
onClick={onConfirm} onClick={onConfirm}
className="flex-1 cursor-pointer rounded-xl border-none bg-red-500 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-red-600 active:scale-95" className="flex-1 cursor-pointer rounded-xl border-none bg-red-500 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-red-600 active:scale-95"
> >
Xóa Delete
</button> </button>
</div> </div>
</div> </div>
+18 -18
View File
@@ -39,7 +39,7 @@ export default function ProductModal({
}); });
if (!res.ok) { if (!res.ok) {
throw new Error("Upload ảnh thất bại."); throw new Error("Image upload failed.");
} }
const raw = await res.text(); const raw = await res.text();
@@ -54,12 +54,12 @@ export default function ProductModal({
} }
if (!filename) { if (!filename) {
throw new Error("Không nhận được filename ảnh từ server."); throw new Error("No image filename received from server.");
} }
setForm((prev) => ({ ...prev, imageUrl: filename })); setForm((prev) => ({ ...prev, imageUrl: filename }));
} catch (error: any) { } catch (error: any) {
setUploadError(error?.message || "Không thể upload ảnh."); setUploadError(error?.message || "Unable to upload image.");
} finally { } finally {
setUploading(false); setUploading(false);
} }
@@ -93,11 +93,11 @@ export default function ProductModal({
<div className="w-full max-w-lg rounded-2xl bg-white shadow-2xl"> <div className="w-full max-w-lg rounded-2xl bg-white shadow-2xl">
<div className="flex items-center justify-between border-b border-(--color-border-light) px-6 py-4"> <div className="flex items-center justify-between border-b border-(--color-border-light) px-6 py-4">
<h2 className="text-foreground text-lg font-bold"> <h2 className="text-foreground text-lg font-bold">
{isEdit ? "Chỉnh sửa món" : "Thêm món mới"} {isEdit ? "Edit item" : "Add new item"}
</h2> </h2>
<button <button
onClick={onClose} onClick={onClose}
title="Đóng" title="Close"
className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full border-none bg-transparent text-(--color-text-muted) transition-colors hover:bg-(--color-border-light) hover:text-(--color-primary)" className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full border-none bg-transparent text-(--color-text-muted) transition-colors hover:bg-(--color-border-light) hover:text-(--color-primary)"
> >
<i className="fa-solid fa-xmark"></i> <i className="fa-solid fa-xmark"></i>
@@ -107,7 +107,7 @@ export default function ProductModal({
<form onSubmit={handleSubmit} className="space-y-4 px-6 py-5"> <form onSubmit={handleSubmit} className="space-y-4 px-6 py-5">
<div> <div>
<label className="mb-1 block text-sm font-medium text-(--color-text-secondary)"> <label className="mb-1 block text-sm font-medium text-(--color-text-secondary)">
Tên món <span className="text-red-500">*</span> Item name <span className="text-red-500">*</span>
</label> </label>
<input <input
required required
@@ -115,13 +115,13 @@ export default function ProductModal({
value={form.name} value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })} onChange={(e) => setForm({ ...form, name: e.target.value })}
className={inputCls} className={inputCls}
placeholder="Ví dụ: Cà Phê Đen" placeholder="e.g. Black Coffee"
/> />
</div> </div>
<div> <div>
<label className="mb-1 block text-sm font-medium text-(--color-text-secondary)"> <label className="mb-1 block text-sm font-medium text-(--color-text-secondary)">
Giá (đ) <span className="text-red-500">*</span> Price () <span className="text-red-500">*</span>
</label> </label>
<input <input
required required
@@ -139,7 +139,7 @@ export default function ProductModal({
<div> <div>
<label className="mb-1 block text-sm font-medium text-(--color-text-secondary)"> <label className="mb-1 block text-sm font-medium text-(--color-text-secondary)">
nh món Item image
</label> </label>
<div className="space-y-2"> <div className="space-y-2">
@@ -154,7 +154,7 @@ export default function ProductModal({
{uploading && ( {uploading && (
<p className="text-sm text-(--color-text-muted)"> <p className="text-sm text-(--color-text-muted)">
<i className="fa-solid fa-spinner mr-1 animate-spin"></i> <i className="fa-solid fa-spinner mr-1 animate-spin"></i>
Đang upload nh... Uploading image...
</p> </p>
)} )}
</div> </div>
@@ -163,7 +163,7 @@ export default function ProductModal({
<div className="mt-2"> <div className="mt-2">
<img <img
src={previewUrl} src={previewUrl}
alt="Preview ảnh món" alt="Item image preview"
className="h-24 w-24 rounded-lg border border-(--color-border-light) object-cover" className="h-24 w-24 rounded-lg border border-(--color-border-light) object-cover"
/> />
</div> </div>
@@ -179,7 +179,7 @@ export default function ProductModal({
<div> <div>
<label className="mb-1 block text-sm font-medium text-(--color-text-secondary)"> <label className="mb-1 block text-sm font-medium text-(--color-text-secondary)">
tả Description
</label> </label>
<textarea <textarea
rows={3} rows={3}
@@ -188,19 +188,19 @@ export default function ProductModal({
setForm({ ...form, description: e.target.value }) setForm({ ...form, description: e.target.value })
} }
className={`${inputCls} resize-none`} className={`${inputCls} resize-none`}
placeholder="Mô tả ngắn về món..." placeholder="Short description..."
/> />
</div> </div>
<div className="bg-background flex items-center justify-between rounded-xl border border-(--color-border-light) px-4 py-3"> <div className="bg-background flex items-center justify-between rounded-xl border border-(--color-border-light) px-4 py-3">
<div> <div>
<p className="text-foreground text-sm font-medium">Trạng thái</p> <p className="text-foreground text-sm font-medium">Status</p>
<p className="text-xs text-(--color-text-muted)"> <p className="text-xs text-(--color-text-muted)">
{form.available ? "Còn hàng" : "Tạm hết"} {form.available ? "In stock" : "Out of stock"}
</p> </p>
</div> </div>
<button <button
title="Chuyển trạng thái" title="Toggle status"
type="button" type="button"
onClick={() => setForm({ ...form, available: !form.available })} onClick={() => setForm({ ...form, available: !form.available })}
className={`relative h-6 w-11 cursor-pointer rounded-full border-none transition-colors duration-200 ${ className={`relative h-6 w-11 cursor-pointer rounded-full border-none transition-colors duration-200 ${
@@ -221,14 +221,14 @@ export default function ProductModal({
onClick={onClose} onClick={onClose}
className="flex-1 cursor-pointer rounded-xl border border-(--color-border) bg-white px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-(--color-border-light)" className="flex-1 cursor-pointer rounded-xl border border-(--color-border) bg-white px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-(--color-border-light)"
> >
Hủy Cancel
</button> </button>
<button <button
type="submit" type="submit"
disabled={uploading} disabled={uploading}
className="flex-1 cursor-pointer rounded-xl border-none bg-(--color-primary) px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-(--color-primary-dark) active:scale-95 disabled:opacity-60" className="flex-1 cursor-pointer rounded-xl border-none bg-(--color-primary) px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-(--color-primary-dark) active:scale-95 disabled:opacity-60"
> >
{isEdit ? "Lưu thay đổi" : "Thêm món"} {isEdit ? "Save changes" : "Add item"}
</button> </button>
</div> </div>
</form> </form>
+19 -19
View File
@@ -59,12 +59,12 @@ export default function ProductsTab() {
type="text" type="text"
value={search} value={search}
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
placeholder="Tìm kiếm món..." placeholder="Search items..."
className="text-foreground w-full rounded-xl border border-(--color-border) bg-white py-2 pr-9 pl-9 text-sm transition outline-none focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary)/20" className="text-foreground w-full rounded-xl border border-(--color-border) bg-white py-2 pr-9 pl-9 text-sm transition outline-none focus:border-(--color-primary) focus:ring-2 focus:ring-(--color-primary)/20"
/> />
{search && ( {search && (
<button <button
title="Xóa tìm kiếm" title="Clear search"
onClick={() => setSearch("")} onClick={() => setSearch("")}
className="absolute top-1/2 right-3 -translate-y-1/2 cursor-pointer border-none bg-transparent text-(--color-text-muted) hover:text-(--color-primary)" className="absolute top-1/2 right-3 -translate-y-1/2 cursor-pointer border-none bg-transparent text-(--color-text-muted) hover:text-(--color-primary)"
> >
@@ -81,26 +81,26 @@ export default function ProductsTab() {
) )
} }
className="text-foreground cursor-pointer rounded-xl border border-(--color-border) bg-white px-3 py-2 text-sm transition outline-none focus:border-(--color-primary)" className="text-foreground cursor-pointer rounded-xl border border-(--color-border) bg-white px-3 py-2 text-sm transition outline-none focus:border-(--color-primary)"
title="Lọc theo trạng thái" title="Filter by status"
> >
<option value="all">Tất cả trạng thái</option> <option value="all">All statuses</option>
<option value="available">Còn hàng</option> <option value="available">In stock</option>
<option value="unavailable">Tạm hết</option> <option value="unavailable">Out of stock</option>
</select> </select>
<button <button
title="Thêm món" title="Add item"
onClick={() => setModalProduct("new")} onClick={() => setModalProduct("new")}
className="flex cursor-pointer items-center gap-2 rounded-xl border-none bg-(--color-primary) px-4 py-2 text-sm font-semibold text-white transition hover:bg-(--color-primary-dark) active:scale-95" className="flex cursor-pointer items-center gap-2 rounded-xl border-none bg-(--color-primary) px-4 py-2 text-sm font-semibold text-white transition hover:bg-(--color-primary-dark) active:scale-95"
> >
<i className="fa-solid fa-plus"></i> <i className="fa-solid fa-plus"></i>
<span className="hidden sm:inline">Thêm món</span> <span className="hidden sm:inline">Add item</span>
</button> </button>
</div> </div>
<p className="text-sm text-(--color-text-muted)"> <p className="text-sm text-(--color-text-muted)">
Hiển thị <strong className="text-foreground">{filtered.length}</strong>{" "} Showing <strong className="text-foreground">{filtered.length}</strong>{" "}
/ {products.length} món / {products.length} items
</p> </p>
{/* Table */} {/* Table */}
@@ -109,19 +109,19 @@ export default function ProductsTab() {
<thead className="bg-background"> <thead className="bg-background">
<tr> <tr>
<th className="px-4 py-3 text-left font-semibold text-(--color-text-secondary)"> <th className="px-4 py-3 text-left font-semibold text-(--color-text-secondary)">
nh Image
</th> </th>
<th className="px-4 py-3 text-left font-semibold text-(--color-text-secondary)"> <th className="px-4 py-3 text-left font-semibold text-(--color-text-secondary)">
Tên món Item name
</th> </th>
<th className="px-4 py-3 text-right font-semibold text-(--color-text-secondary)"> <th className="px-4 py-3 text-right font-semibold text-(--color-text-secondary)">
Giá Price
</th> </th>
<th className="px-4 py-3 text-center font-semibold text-(--color-text-secondary)"> <th className="px-4 py-3 text-center font-semibold text-(--color-text-secondary)">
Trạng thái Status
</th> </th>
<th className="px-4 py-3 text-center font-semibold text-(--color-text-secondary)"> <th className="px-4 py-3 text-center font-semibold text-(--color-text-secondary)">
Thao tác Actions
</th> </th>
</tr> </tr>
</thead> </thead>
@@ -133,7 +133,7 @@ export default function ProductsTab() {
className="py-12 text-center text-(--color-text-muted)" className="py-12 text-center text-(--color-text-muted)"
> >
<i className="fa-solid fa-mug-hot mb-2 block text-3xl opacity-30"></i> <i className="fa-solid fa-mug-hot mb-2 block text-3xl opacity-30"></i>
<span className="ml-3">Không tìm thấy món nào</span> <span className="ml-3">No items found</span>
</td> </td>
</tr> </tr>
) : ( ) : (
@@ -165,7 +165,7 @@ export default function ProductsTab() {
<td className="px-4 py-3 text-center"> <td className="px-4 py-3 text-center">
<button <button
onClick={() => toggleProductAvailability(p)} onClick={() => toggleProductAvailability(p)}
title="Nhấn để đổi trạng thái" title="Click to toggle status"
className="cursor-pointer border-none bg-transparent" className="cursor-pointer border-none bg-transparent"
> >
<StatusBadge available={p.available ?? true} /> <StatusBadge available={p.available ?? true} />
@@ -175,14 +175,14 @@ export default function ProductsTab() {
<div className="flex items-center justify-center gap-1.5"> <div className="flex items-center justify-center gap-1.5">
<button <button
onClick={() => setModalProduct(p)} onClick={() => setModalProduct(p)}
title="Chỉnh sửa" title="Edit"
className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg border border-(--color-border-light) bg-transparent text-(--color-text-muted) transition hover:border-(--color-primary-light) hover:text-(--color-primary)" className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg border border-(--color-border-light) bg-transparent text-(--color-text-muted) transition hover:border-(--color-primary-light) hover:text-(--color-primary)"
> >
<i className="fa-solid fa-pen text-xs"></i> <i className="fa-solid fa-pen text-xs"></i>
</button> </button>
<button <button
onClick={() => setDeleteTarget(p)} onClick={() => setDeleteTarget(p)}
title="Xóa" title="Delete"
className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-transparent text-(--color-text-muted) transition hover:border-red-200 hover:bg-red-50 hover:text-red-500" className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-transparent text-(--color-text-muted) transition hover:border-red-200 hover:bg-red-50 hover:text-red-500"
> >
<i className="fa-solid fa-trash text-xs"></i> <i className="fa-solid fa-trash text-xs"></i>
@@ -37,14 +37,14 @@ export default function CategorySidebar({
className={`text-xs font-bold tracking-widest whitespace-nowrap text-(--color-text-muted) uppercase ${isOpen ? "block" : "hidden"} xl:block`} className={`text-xs font-bold tracking-widest whitespace-nowrap text-(--color-text-muted) uppercase ${isOpen ? "block" : "hidden"} xl:block`}
> >
<i className="fa-solid fa-utensils mr-2 text-(--color-primary)"></i> <i className="fa-solid fa-utensils mr-2 text-(--color-primary)"></i>
Thực Đơn Menu
</span> </span>
{/* Toggle button — hidden on xl+ (sidebar is always expanded there) */} {/* Toggle button — hidden on xl+ (sidebar is always expanded there) */}
<button <button
onClick={onToggle} onClick={onToggle}
title={isOpen ? "Thu gọn menu" : "Mở rộng menu"} title={isOpen ? "Collapse menu" : "Expand menu"}
aria-label={isOpen ? "Thu gọn menu" : "Mở rộng menu"} aria-label={isOpen ? "Collapse menu" : "Expand menu"}
className="flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-lg border-none bg-transparent text-(--color-text-muted) transition-colors duration-150 hover:bg-(--color-border-light) hover:text-(--color-primary) xl:hidden" className="flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-lg border-none bg-transparent text-(--color-text-muted) transition-colors duration-150 hover:bg-(--color-border-light) hover:text-(--color-primary) xl:hidden"
> >
<i <i
@@ -144,15 +144,15 @@ export default function MobileShiftView({ onShiftClick }: MobileShiftViewProps)
? "bg-(--color-primary)/10 text-(--color-primary)" ? "bg-(--color-primary)/10 text-(--color-primary)"
: "bg-gray-100 text-(--color-text-muted)" : "bg-gray-100 text-(--color-text-muted)"
}`}> }`}>
{selectedShifts.length} ca làm {selectedShifts.length} shifts
</span> </span>
</div> </div>
{selectedShifts.length === 0 ? ( {selectedShifts.length === 0 ? (
<div className="rounded-2xl border border-dashed border-(--color-border-light) py-10 text-center"> <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> <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)">Không ca làm</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)">Chưa ca nào đưc tạo hôm này</p> <p className="mt-0.5 text-xs text-(--color-text-muted)">No shifts created for this day</p>
</div> </div>
) : ( ) : (
<div className="space-y-2.5"> <div className="space-y-2.5">
@@ -102,7 +102,7 @@ export default function MonthlyCalendar({ onShiftClick, onDateSelect }: MonthlyC
<div className="mt-1.5 space-y-1"> <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)"> <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> <i className="fa-solid fa-clock text-[8px]"></i>
{shiftCount} ca {shiftCount} shifts
</span> </span>
</div> </div>
)} )}
@@ -110,7 +110,7 @@ export default function MonthlyCalendar({ onShiftClick, onDateSelect }: MonthlyC
{/* Add indicator for manager */} {/* Add indicator for manager */}
{shiftCount === 0 && onDateSelect && ( {shiftCount === 0 && onDateSelect && (
<p className="mt-1 text-[10px] text-(--color-text-muted) opacity-0 transition group-hover:opacity-100"> <p className="mt-1 text-[10px] text-(--color-text-muted) opacity-0 transition group-hover:opacity-100">
+ Thêm ca + Add shift
</p> </p>
)} )}
</button> </button>
@@ -185,7 +185,7 @@ export default function ShiftCreateModal({
{/* Department */} {/* Department */}
<div> <div>
<label className="mb-1 block text-xs font-semibold text-(--color-text-secondary)"> <label className="mb-1 block text-xs font-semibold text-(--color-text-secondary)">
Bộ phận Department
</label> </label>
<select <select
title="Department" title="Department"
@@ -205,7 +205,7 @@ export default function ShiftCreateModal({
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div> <div>
<label className="mb-1 block text-xs font-semibold text-(--color-text-secondary)"> <label className="mb-1 block text-xs font-semibold text-(--color-text-secondary)">
Số nhân viên tối đa Max staff
</label> </label>
<input <input
title="Max Staff" title="Max Staff"
@@ -219,7 +219,7 @@ export default function ShiftCreateModal({
</div> </div>
<div> <div>
<label className="mb-1 block text-xs font-semibold text-(--color-text-secondary)"> <label className="mb-1 block text-xs font-semibold text-(--color-text-secondary)">
Lương ca (VND) Shift wage (VND)
</label> </label>
<input <input
title="Wage" title="Wage"
@@ -248,14 +248,14 @@ export default function ShiftCreateModal({
className="flex-1 cursor-pointer rounded-xl border-none bg-(--color-primary) px-4 py-2.5 text-sm font-semibold text-white transition hover:opacity-90" className="flex-1 cursor-pointer rounded-xl border-none bg-(--color-primary) px-4 py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
> >
<i className="fa-solid fa-plus mr-2"></i> <i className="fa-solid fa-plus mr-2"></i>
Tạo ca làm Create shift
</button> </button>
<button <button
type="button" type="button"
onClick={onClose} onClick={onClose}
className="cursor-pointer rounded-xl border border-(--color-border-light) bg-transparent px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-gray-50" className="cursor-pointer rounded-xl border border-(--color-border-light) bg-transparent px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-gray-50"
> >
Hủy Cancel
</button> </button>
</div> </div>
</form> </form>
@@ -41,10 +41,10 @@ export default function ShiftDetailModal({
setSuccess(null); setSuccess(null);
const result = await registerShift(shift.id, user.id, user.name); const result = await registerShift(shift.id, user.id, user.name);
if (result.success) { if (result.success) {
setSuccess("Đăng ký ca thành công!"); setSuccess("Shift registered successfully!");
setTimeout(onClose, 1200); setTimeout(onClose, 1200);
} else { } else {
setError(result.error ?? "Có lỗi xảy ra."); setError(result.error ?? "An error occurred.");
} }
}; };
@@ -52,13 +52,13 @@ export default function ShiftDetailModal({
if (!user) return; if (!user) return;
setError(null); setError(null);
unregisterShift(shift.id, user.id); unregisterShift(shift.id, user.id);
setSuccess("Đã hủy đăng ký ca."); setSuccess("Shift registration cancelled.");
setTimeout(onClose, 1200); setTimeout(onClose, 1200);
}; };
const handleManagerUnregister = (staffId: string) => { const handleManagerUnregister = (staffId: string) => {
unregisterShift(shift.id, staffId); unregisterShift(shift.id, staffId);
setSuccess("Đã xóa nhân viên khỏi ca."); setSuccess("Staff removed from shift.");
}; };
const handleDelete = async () => { const handleDelete = async () => {
@@ -67,10 +67,10 @@ export default function ShiftDetailModal({
}; };
const statusLabel = { const statusLabel = {
available: "Còn trống", available: "Available",
registered: "Đã đăng ký", registered: "Registered",
approved_leave: "Nghỉ phép", approved_leave: "On leave",
absent: "Vắng mặt", absent: "Absent",
}; };
const statusColor = { const statusColor = {
@@ -93,7 +93,7 @@ export default function ShiftDetailModal({
{/* {dept && <i className={`${dept.icon} text-(--color-primary)`}></i>} */} {/* {dept && <i className={`${dept.icon} text-(--color-primary)`}></i>} */}
<div> <div>
<h2 className="text-foreground text-base font-bold"> <h2 className="text-foreground text-base font-bold">
Chi tiết ca làm Shift Details
</h2> </h2>
{/* <p className="text-xs text-(--color-text-muted)">{dept?.name}</p> */} {/* <p className="text-xs text-(--color-text-muted)">{dept?.name}</p> */}
</div> </div>
@@ -123,7 +123,7 @@ export default function ShiftDetailModal({
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="rounded-xl bg-gray-50 p-3"> <div className="rounded-xl bg-gray-50 p-3">
<p className="text-[10px] font-semibold text-(--color-text-muted) uppercase"> <p className="text-[10px] font-semibold text-(--color-text-muted) uppercase">
Ngày Date
</p> </p>
<p className="text-foreground mt-1 text-sm font-bold"> <p className="text-foreground mt-1 text-sm font-bold">
{parseShiftDate(shift.date as Date | string | undefined)?.toLocaleDateString( {parseShiftDate(shift.date as Date | string | undefined)?.toLocaleDateString(
@@ -139,7 +139,7 @@ export default function ShiftDetailModal({
</div> </div>
<div className="rounded-xl bg-gray-50 p-3"> <div className="rounded-xl bg-gray-50 p-3">
<p className="text-[10px] font-semibold text-(--color-text-muted) uppercase"> <p className="text-[10px] font-semibold text-(--color-text-muted) uppercase">
Giờ làm Working hours
</p> </p>
<p className="text-foreground mt-1 text-sm font-bold"> <p className="text-foreground mt-1 text-sm font-bold">
{shift.startTime} {shift.endTime} {shift.startTime} {shift.endTime}
@@ -147,13 +147,13 @@ export default function ShiftDetailModal({
</div> </div>
<div className="rounded-xl bg-gray-50 p-3"> <div className="rounded-xl bg-gray-50 p-3">
<p className="text-[10px] font-semibold text-(--color-text-muted) uppercase"> <p className="text-[10px] font-semibold text-(--color-text-muted) uppercase">
Thời lượng Duration
</p> </p>
<p className="text-foreground mt-1 text-sm font-bold">{""} giờ</p> <p className="text-foreground mt-1 text-sm font-bold">{""} giờ</p>
</div> </div>
<div className="rounded-xl bg-gray-50 p-3"> <div className="rounded-xl bg-gray-50 p-3">
<p className="text-[10px] font-semibold text-(--color-text-muted) uppercase"> <p className="text-[10px] font-semibold text-(--color-text-muted) uppercase">
Lương ca Shift wage
</p> </p>
<p className="mt-1 text-sm font-bold text-(--color-primary)"> <p className="mt-1 text-sm font-bold text-(--color-primary)">
{(shift.wage ?? 0).toLocaleString("vi-VN")} VND {(shift.wage ?? 0).toLocaleString("vi-VN")} VND
@@ -164,12 +164,12 @@ export default function ShiftDetailModal({
{/* Registered staff */} {/* Registered staff */}
<div> <div>
<p className="mb-2 text-xs font-semibold text-(--color-text-secondary)"> <p className="mb-2 text-xs font-semibold text-(--color-text-secondary)">
Nhân viên đã đăng ({registeredStaff.length}/ Registered staff ({registeredStaff.length}/
{shift.maxStaff}) {shift.maxStaff})
</p> </p>
{registeredStaff.length === 0 ? ( {registeredStaff.length === 0 ? (
<p className="text-xs text-(--color-text-muted) italic"> <p className="text-xs text-(--color-text-muted) italic">
Chưa ai đăng No staff registered yet
</p> </p>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
@@ -193,7 +193,7 @@ export default function ShiftDetailModal({
className="cursor-pointer rounded-lg border-none bg-transparent px-2 py-1 text-xs text-red-500 transition hover:bg-red-50" className="cursor-pointer rounded-lg border-none bg-transparent px-2 py-1 text-xs text-red-500 transition hover:bg-red-50"
> >
<i className="fa-solid fa-user-minus mr-1"></i> <i className="fa-solid fa-user-minus mr-1"></i>
Xóa Remove
</button> </button>
)} )}
</div> </div>
@@ -226,7 +226,7 @@ export default function ShiftDetailModal({
className="flex-1 cursor-pointer rounded-xl border-none bg-(--color-primary) px-4 py-2.5 text-sm font-semibold text-white transition hover:opacity-90" className="flex-1 cursor-pointer rounded-xl border-none bg-(--color-primary) px-4 py-2.5 text-sm font-semibold text-white transition hover:opacity-90"
> >
<i className="fa-solid fa-calendar-plus mr-2"></i> <i className="fa-solid fa-calendar-plus mr-2"></i>
Đăng ca Register
</button> </button>
)} )}
{isRegistered && ( {isRegistered && (
@@ -236,7 +236,7 @@ export default function ShiftDetailModal({
className="flex-1 cursor-pointer rounded-xl border border-red-200 bg-transparent px-4 py-2.5 text-sm font-semibold text-red-600 transition hover:bg-red-50" className="flex-1 cursor-pointer rounded-xl border border-red-200 bg-transparent px-4 py-2.5 text-sm font-semibold text-red-600 transition hover:bg-red-50"
> >
<i className="fa-solid fa-calendar-minus mr-2"></i> <i className="fa-solid fa-calendar-minus mr-2"></i>
Hủy đăng Unregister
</button> </button>
)} )}
{isManager && ( {isManager && (
@@ -246,7 +246,7 @@ export default function ShiftDetailModal({
className="cursor-pointer rounded-xl border border-red-200 bg-transparent px-4 py-2.5 text-sm font-semibold text-red-600 transition hover:bg-red-50" className="cursor-pointer rounded-xl border border-red-200 bg-transparent px-4 py-2.5 text-sm font-semibold text-red-600 transition hover:bg-red-50"
> >
<i className="fa-solid fa-trash mr-2"></i> <i className="fa-solid fa-trash mr-2"></i>
Xóa ca Delete shift
</button> </button>
)} )}
<button <button
@@ -254,7 +254,7 @@ export default function ShiftDetailModal({
onClick={onClose} onClick={onClose}
className="cursor-pointer rounded-xl border border-(--color-border-light) bg-transparent px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-gray-50" className="cursor-pointer rounded-xl border border-(--color-border-light) bg-transparent px-4 py-2.5 text-sm font-medium text-(--color-text-secondary) transition hover:bg-gray-50"
> >
Đóng Close
</button> </button>
</div> </div>
</div> </div>
@@ -116,14 +116,14 @@ export default function WeeklySchedule({
{formatDateShort(new Date(selectedDateRef))} {formatDateShort(new Date(selectedDateRef))}
</h3> </h3>
<span className="rounded-full bg-(--color-primary)/10 px-2.5 py-1 text-xs font-semibold text-(--color-primary)"> <span className="rounded-full bg-(--color-primary)/10 px-2.5 py-1 text-xs font-semibold text-(--color-primary)">
{dayShifts.length} ca {dayShifts.length} shifts
</span> </span>
</div> </div>
{dayShifts.length === 0 && !onCreateShift ? ( {dayShifts.length === 0 && !onCreateShift ? (
<div className="rounded-2xl border border-dashed border-(--color-border-light) py-10 text-center"> <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> <i className="fa-regular fa-calendar-xmark mb-2 text-3xl text-gray-200"></i>
<p className="text-sm text-(--color-text-muted)">Không ca làm hôm nay</p> <p className="text-sm text-(--color-text-muted)">No shifts today</p>
</div> </div>
) : ( ) : (
<div className="space-y-2.5"> <div className="space-y-2.5">
@@ -137,7 +137,7 @@ export default function WeeklySchedule({
className="flex w-full cursor-pointer items-center justify-center gap-2 rounded-2xl border border-dashed border-(--color-border-light) bg-transparent py-3 text-sm font-medium text-(--color-text-muted) transition hover:border-(--color-primary) hover:text-(--color-primary)" className="flex w-full cursor-pointer items-center justify-center gap-2 rounded-2xl border border-dashed border-(--color-border-light) bg-transparent py-3 text-sm font-medium text-(--color-text-muted) transition hover:border-(--color-primary) hover:text-(--color-primary)"
> >
<i className="fa-solid fa-plus"></i> <i className="fa-solid fa-plus"></i>
Thêm ca làm Add shift
</button> </button>
)} )}
</div> </div>
@@ -156,7 +156,7 @@ export default function WeeklySchedule({
<thead> <thead>
<tr> <tr>
<th className="w-28 border-r border-b border-(--color-border-light) bg-gray-50/80 px-4 py-3 text-left text-[11px] font-semibold tracking-wider text-(--color-text-muted) uppercase"> <th className="w-28 border-r border-b border-(--color-border-light) bg-gray-50/80 px-4 py-3 text-left text-[11px] font-semibold tracking-wider text-(--color-text-muted) uppercase">
Bộ phận Department
</th> </th>
{weekDates.map((date, i) => { {weekDates.map((date, i) => {
const today = isToday(new Date(date)); const today = isToday(new Date(date));
@@ -221,7 +221,7 @@ export default function WeeklySchedule({
className="mt-auto flex cursor-pointer items-center justify-center gap-1 rounded-lg border border-dashed border-gray-200 bg-transparent py-1.5 text-[10px] font-medium text-gray-300 transition hover:border-(--color-primary)/50 hover:text-(--color-primary)" className="mt-auto flex cursor-pointer items-center justify-center gap-1 rounded-lg border border-dashed border-gray-200 bg-transparent py-1.5 text-[10px] font-medium text-gray-300 transition hover:border-(--color-primary)/50 hover:text-(--color-primary)"
> >
<i className="fa-solid fa-plus"></i> <i className="fa-solid fa-plus"></i>
Thêm Add
</button> </button>
)} )}
</div> </div>
+1 -1
View File
@@ -47,7 +47,7 @@ export default function Header() {
<div className="relative h-10 w-10 shrink-0 md:h-11 md:w-11"> <div className="relative h-10 w-10 shrink-0 md:h-11 md:w-11">
<Image <Image
src={SHOP_INFO.logo} src={SHOP_INFO.logo}
alt={`Logo ${SHOP_INFO.name}`} alt={`${SHOP_INFO.name} logo`}
fill fill
className="object-contain transition-transform duration-200 group-hover:scale-105" className="object-contain transition-transform duration-200 group-hover:scale-105"
sizes="44px" sizes="44px"