TN
T_ NhuBh

Manage Salary Page - Copy this React, Tailwind Component to your project

"use client"; import React, { useState, useEffect } from "react"; import { GridColDef } from "@mui/x data grid"; import { Edit2, Trash2, CircleAlert } from 'lucide react'; import ButtonDefault from "@/components/Buttons/ButtonDefault"; import toast from "react hot toast"; import { Dialog, DialogContent, DialogTitle, Radio, RadioGroup, FormControlLabel } from "@mui/material"; import DataTable from "../DataTable"; import { MonthPicker } from "@/components/FormElements/DatePicker/MonthPicker"; import { CustomSelect } from "@/components/CustomSelect"; interface SalaryRow { id: string; stt: number; employeeName: string; baseSalary: number; totalDays: number; workedDays: number; dailySalary: number; bonus: number; overtimeOrLate: number; } interface Employee { id: string; name: string; } const ManageSalaryPage: React.FC = () => { const [data, setData] = useState<SalaryRow[]>([]); const [isLoading, setIsLoading] = useState(true); const [isModalOpen, setIsModalOpen] = useState(false); const [viewOption, setViewOption] = useState<"all" | "employee">("all"); const [selectedMonth, setSelectedMonth] = useState("01 2025"); const [isMonthPickerOpen, setIsMonthPickerOpen] = useState(false); const [isDarkMode, setIsDarkMode] = useState(false); const [selectedEmployee, setSelectedEmployee] = useState<string>(""); const employees: Employee[] = [ { id: "1", name: "Nguyen Van A" }, { id: "2", name: "Tran Thi B" }, { id: "3", name: "Le Van C" }, { id: "4", name: "Pham Thi D" }, ]; const employeeOptions = employees.map(emp => ({ value: emp.id, label: emp.name })); useEffect(() => { fetchData(); }, []); const fetchData = async () => { setIsLoading(true); try { // Simulating API call await new Promise(resolve => setTimeout(resolve, 1000)); const mockData: SalaryRow[] = [ { id: "1", stt: 1, employeeName: "Nguyen Van A", baseSalary: 10000000, totalDays: 22, workedDays: 20, dailySalary: 909090, bonus: 500000, overtimeOrLate: 200000, }, { id: "2", stt: 2, employeeName: "Tran Thi B", baseSalary: 12000000, totalDays: 22, workedDays: 22, dailySalary: 1090909, bonus: 1000000, overtimeOrLate: 0, }, ]; setData(mockData); } catch (error) { console.error("Failed to fetch salary data:", error); toast.error("Lỗi khi tải dữ liệu lương!"); } finally { setIsLoading(false); } }; const handleSearch = () => { if (viewOption === "employee" && !selectedEmployee) { toast.error("Vui lòng chọn nhân viên để xem!"); return; } setIsLoading(true); setTimeout(() => { if (viewOption === "employee") { const employee = employees.find(emp => emp.id === selectedEmployee); setData([ { id: selectedEmployee, stt: 1, employeeName: employee?.name || "", baseSalary: 10000000, totalDays: 22, workedDays: 20, dailySalary: 909090, bonus: 500000, overtimeOrLate: 200000, }, ]); } else { setData([ { id: "1", stt: 1, employeeName: "Nguyen Van A", baseSalary: 10000000, totalDays: 22, workedDays: 20, dailySalary: 909090, bonus: 500000, overtimeOrLate: 200000, }, { id: "2", stt: 2, employeeName: "Tran Thi B", baseSalary: 12000000, totalDays: 22, workedDays: 22, dailySalary: 1090909, bonus: 1000000, overtimeOrLate: 0, }, ]); } setIsLoading(false); toast.success("Tìm kiếm thành công!"); }, 1000); }; const handleEdit = (id: string) => { console.log("Edit salary for id:", id); toast.success("Chức năng chỉnh sửa sẽ được triển khai sau!"); }; const handleDelete = (id: string) => { console.log("Delete salary for id:", id); toast.success("Chức năng xóa sẽ được triển khai sau!"); }; const columns: GridColDef[] = [ { field: "id", headerName: "STT", width: 70, headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 0, }, { field: "employeeName", headerName: "TÊN NHÂN VIÊN", headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 1, }, { field: "baseSalary", headerName: "LƯƠNG CƠ BẢN", headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 1, }, { field: "totalDays", headerName: "TỔNG NGÀY CÔNG", headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 1, }, { field: "workedDays", headerName: "SỐ NGÀY ĐÃ LÀM", headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 1, }, { field: "dailySalary", headerName: "LƯƠNG THEO NGÀY LÀM VIỆC (VND)", headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 1.5, }, { field: "bonus", headerName: "LƯƠNG THƯỞNG (VND)", headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 1, }, { field: "overtimeOrLate", headerName: "TĂNG CA/ĐI TRỄ (VND)", headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 1, }, { field: "actions", headerName: "THAO TÁC", width: 120, headerClassName: "bg gray 200 dark:bg gray 900 text gray 900 dark:text gray 100 uppercase", headerAlign: "center", align: "center", flex: 0, renderCell: (params) => ( <div className="flex justify center items center gap 2 w full"> <button className="p 2 text blue 500 hover:text blue 600 hover:bg blue 100 dark:hover:bg blue 900 rounded full transition all duration 200 transform hover:scale 110" onClick={() => handleEdit(params.row.id)} > <Edit2 className="w 5 h 5" /> </button> <button className="p 2 text red 500 hover:text red 600 hover:bg red 100 dark:hover:bg red 900 rounded full transition all duration 200 transform hover:scale 110" onClick={() => handleDelete(params.row.id)} > <Trash2 className="w 5 h 5" /> </button> </div> ), }, ]; return ( <> <style jsx global>{` @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap'); * { font family: 'Montserrat', sans serif !important; } .MuiFormControlLabel label { font family: 'Montserrat', sans serif !important; } `}</style> <div className="space y 3 p 4 max w 7xl mx auto"> <div className="flex justify between items center"> <h1 className="text 2xl sm:text 3xl font bold text black dark:text white"> Thông tin lương nhân viên </h1> </div> <div className="bg white dark:bg gray 800 rounded xl p 4 sm:p 6 shadow sm"> <div className="grid grid cols 1 sm:grid cols 2 lg:grid cols 3 gap 4"> <div className="col span 1 sm:col span 2 lg:col span 3"> <RadioGroup row value={viewOption} onChange={(e) => { setViewOption(e.target.value as "all" | "employee"); if (e.target.value === "all") { setSelectedEmployee(""); } }} > <FormControlLabel value="all" control={<Radio className="text blue 600 dark:text blue 400" />} label="Xem tất cả" className="text inherit" /> <FormControlLabel value="employee" control={<Radio className="text blue 600 dark:text blue 400" />} label="Xem theo nhân viên" className="text inherit" /> </RadioGroup> </div> <div className="flex flex col w full"> <span className="text sm mb 1">Chọn tháng:</span> <div className="relative"> <button onClick={() => setIsMonthPickerOpen(!isMonthPickerOpen)} className="w full px 3 py 2 text left border rounded md bg white dark:bg gray 700 hover:border blue 500 dark:hover:border blue 400 transition colors" > {selectedMonth} </button> <MonthPicker isOpen={isMonthPickerOpen} selectedDate={selectedMonth} onSelect={setSelectedMonth} onClose={() => setIsMonthPickerOpen(false)} /> </div> </div> {viewOption === "employee" && ( <div className="flex flex col w full"> <span className="text sm mb 1">Tên nhân viên:</span> <CustomSelect options={employeeOptions} value={selectedEmployee} onChange={setSelectedEmployee} placeholder="Chọn nhân viên" /> </div> )} <div className="flex items end w full"> <ButtonDefault onClick={handleSearch} label="Tìm kiếm" customClasses={` w full px 6 py 2 text sm font medium rounded xl transition duration 200 font semibold ${isDarkMode ? 'bg blue 600 hover:bg blue 700 text white' : 'bg [#4318FF] hover:bg [#4318FF]/80 text white' } `} /> </div> </div> </div> <div className="overflow x auto rounded xl"> {viewOption === "employee" && !selectedEmployee ? ( <div className="flex items center bg red 50 border border red 200 text red 600 px 4 py 3 rounded xl" role="alert"> <div className="flex shrink 0"> <CircleAlert className="w 5 h 5 text red 600" /> </div> <span className="ml 3 text sm font medium">Vui lòng chọn nhân viên để xem!</span> </div> ) : ( <DataTable rows={data} columns={columns} isLoading={isLoading} /> )} </div> </div> <Dialog open={isModalOpen} onClose={() => setIsModalOpen(false)} maxWidth="sm" fullWidth PaperProps={{ className: 'bg white dark:bg gray 800 rounded xl', }} > <DialogTitle className="text xl font bold text gray 800 dark:text white"> Thêm thông tin lương mới </DialogTitle> <DialogContent> <p className="text gray 600 dark:text gray 400">Form thêm mới sẽ được triển khai sau.</p> </DialogContent> </Dialog> </> ); }; export default ManageSalaryPage; hãy cải tiến code sau của tôi để khi click chọn tên nhân viên như Nguyễn Văn A nó sẽ không hiển thị datatable mà hiển thị component sau:Bố cục chung: Giao diện chia thành hai phần chính: Phần thông tin cơ bản về nhân viên và lương nằm ở phía trên. Phần chi tiết các mục liên quan (thưởng, phạt, tăng ca) nằm ở phía dưới. Thông tin cơ bản: Gồm các mục: Nhân viên: Một ô nhập liệu hoặc hiển thị tên nhân viên (tam nhu) với khung bo tròn nhẹ. Thời gian: Một trường hiển thị thời gian dưới dạng tháng và năm (Tháng 01 năm 2025). Phía bên phải là các tùy chọn: Radio button với hai lựa chọn: Xem tất cả và Xem theo nhân viên. Một trường hiển thị tên nhân viên được chọn (tam nhu). Tùy chọn Chọn tháng hiển thị giá trị dưới dạng MM YYYY (01 2025). Thông tin lương nhân viên: Các thông tin hiển thị dưới dạng thẻ với nội dung: Lương cơ bản (VND): Hiển thị giá trị là 200.000. Chi tiết ngày làm việc tháng này: Một thanh tiến trình ngang (progress bar) biểu thị tổng số ngày làm việc. Bên dưới hiển thị số ngày đã làm (0.5 ngày) bằng màu xanh, số ngày còn lại (27.5 ngày) bằng màu đỏ. Lương theo ngày làm việc (VND): Hiển thị giá trị là 3.571. Lương thưởng (VND): Hiển thị giá trị là 0. Lương tăng ca/đi trễ: Hiển thị giá trị là 0. Lương thưởng/phạt: Hiển thị giá trị là 0. Tổng thu nhập tháng này (VND): Hiển thị giá trị là 3.571 với biểu tượng $ bên cạnh. Chi tiết các bộ phận khác và tiền thưởng/phạt tương ứng: Bảng hiển thị gồm các cột: STT: Hiển thị số thứ tự. Đơn hàng (Thời gian và Tên khách hàng). Bộ phận. Tên sản phẩm. Loại sản phẩm. Số lượng. Bảng có thiết kế hiện đại, các cột được căn đều với đường kẻ phân cách mỏng. Tab điều hướng trong bảng: Hai tab ở đầu bảng: Tăng ca/Đi trễ. Thưởng/Phạt. Tab được bo tròn với màu sắc nhấn nhẹ.

Prompt
Component Preview

About

ManageSalaryPage - View and manage employee salaries with detailed info, month selection, and modern UI. Built with React and Tailwin. Copy component code!

Share

Last updated 1 month ago