import type { ActionFunctionArgs, LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; import { json, redirect } from "@remix-run/node"; import { Form, useActionData, useLoaderData, useNavigation } from "@remix-run/react"; import { requireAuthLevel } from "~/utils/auth.server"; import DashboardLayout from "~/components/DashboardLayout"; import FormModal from "~/components/FormModal"; import Toast from "~/components/Toast"; import { useState, useEffect } from "react"; import bcrypt from "bcryptjs"; import { prisma } from "~/utils/db.server"; export const meta: MetaFunction = () => [{ title: "Employee Management - Phosphat Report" }]; export const loader = async ({ request }: LoaderFunctionArgs) => { const user = await requireAuthLevel(request, 2); // If user is level 2 (Admin), they can only see employees with level <= 2 // If user is level 3 (Super Admin), they can see all employees const whereClause = user.authLevel === 2 ? { authLevel: { lte: 2 } } // Level 2 users can only see level 1 and 2 : {}; // Level 3 users can see all levels const employees = await prisma.employee.findMany({ where: whereClause, orderBy: [{ authLevel: 'asc' }, { name: 'asc' }], include: { _count: { select: { reports: true } } } }); return json({ user, employees }); }; export const action = async ({ request }: ActionFunctionArgs) => { const user = await requireAuthLevel(request, 2); const formData = await request.formData(); const intent = formData.get("intent"); const id = formData.get("id"); const name = formData.get("name"); const username = formData.get("username"); const email = formData.get("email"); const password = formData.get("password"); const authLevel = formData.get("authLevel"); const status = formData.get("status"); if (intent === "create") { if (typeof name !== "string" || name.length === 0) { return json({ errors: { name: "Name is required" } }, { status: 400 }); } if (typeof username !== "string" || username.length === 0) { return json({ errors: { username: "Username is required" } }, { status: 400 }); } if (typeof email !== "string" || email.length === 0) { return json({ errors: { email: "Email is required" } }, { status: 400 }); } // Basic email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return json({ errors: { email: "Please enter a valid email address" } }, { status: 400 }); } if (typeof password !== "string" || password.length < 6) { return json({ errors: { password: "Password must be at least 6 characters" } }, { status: 400 }); } if (typeof authLevel !== "string" || !["1", "2", "3"].includes(authLevel)) { return json({ errors: { authLevel: "Valid auth level is required (1, 2, or 3)" } }, { status: 400 }); } // Level 2 users cannot create Level 3 employees if (user.authLevel === 2 && parseInt(authLevel) === 3) { return json({ errors: { authLevel: "You don't have permission to create Super Admin users" } }, { status: 403 }); } try { const hashedPassword = bcrypt.hashSync(password, 10); await prisma.employee.create({ data: { name, username, email, password: hashedPassword, authLevel: parseInt(authLevel), status: 'active' } }); return json({ success: "Employee created successfully!" }); } catch (error) { return json({ errors: { form: "Username or email already exists" } }, { status: 400 }); } } if (intent === "update") { if (typeof name !== "string" || name.length === 0) { return json({ errors: { name: "Name is required" } }, { status: 400 }); } if (typeof username !== "string" || username.length === 0) { return json({ errors: { username: "Username is required" } }, { status: 400 }); } if (typeof email !== "string" || email.length === 0) { return json({ errors: { email: "Email is required" } }, { status: 400 }); } // Basic email validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return json({ errors: { email: "Please enter a valid email address" } }, { status: 400 }); } if (typeof authLevel !== "string" || !["1", "2", "3"].includes(authLevel)) { return json({ errors: { authLevel: "Valid auth level is required (1, 2, or 3)" } }, { status: 400 }); } if (typeof id !== "string") { return json({ errors: { form: "Invalid employee ID" } }, { status: 400 }); } // Check if the employee being updated exists and if current user can edit them const existingEmployee = await prisma.employee.findUnique({ where: { id: parseInt(id) }, select: { authLevel: true } }); if (!existingEmployee) { return json({ errors: { form: "Employee not found" } }, { status: 404 }); } // Level 2 users cannot edit Level 3 employees if (user.authLevel === 2 && existingEmployee.authLevel === 3) { return json({ errors: { form: "You don't have permission to edit Super Admin users" } }, { status: 403 }); } // Level 2 users cannot promote someone to Level 3 if (user.authLevel === 2 && parseInt(authLevel) === 3) { return json({ errors: { authLevel: "You don't have permission to create Super Admin users" } }, { status: 403 }); } try { const updateData: any = { name, username, email, authLevel: parseInt(authLevel) }; // Add status if provided (but prevent users from changing their own status) if (typeof status === "string" && ["active", "inactive"].includes(status)) { if (parseInt(id) !== user.id) { updateData.status = status; } } // Only update password if provided if (typeof password === "string" && password.length >= 6) { updateData.password = bcrypt.hashSync(password, 10); } else if (typeof password === "string" && password.length > 0 && password.length < 6) { return json({ errors: { password: "Password must be at least 6 characters" } }, { status: 400 }); } await prisma.employee.update({ where: { id: parseInt(id) }, data: updateData }); return json({ success: "Employee updated successfully!" }); } catch (error) { return json({ errors: { form: "Username or email already exists" } }, { status: 400 }); } } if (intent === "toggleStatus") { if (typeof id !== "string") { return json({ errors: { form: "Invalid employee ID" } }, { status: 400 }); } const employeeId = parseInt(id); // Prevent users from changing their own status if (employeeId === user.id) { return json({ errors: { form: "You cannot change your own status" } }, { status: 403 }); } // Check if the employee exists and if current user can edit them const existingEmployee = await prisma.employee.findUnique({ where: { id: employeeId }, select: { authLevel: true, status: true } }); if (!existingEmployee) { return json({ errors: { form: "Employee not found" } }, { status: 404 }); } // Level 2 users cannot edit Level 3 employees if (user.authLevel === 2 && existingEmployee.authLevel === 3) { return json({ errors: { form: "You don't have permission to edit Super Admin users" } }, { status: 403 }); } try { const newStatus = existingEmployee.status === 'active' ? 'inactive' : 'active'; await prisma.employee.update({ where: { id: employeeId }, data: { status: newStatus } }); return json({ success: `Employee status changed to ${newStatus}!` }); } catch (error) { return json({ errors: { form: "Failed to update employee status" } }, { status: 400 }); } } if (intent === "delete") { if (typeof id !== "string") { return json({ errors: { form: "Invalid employee ID" } }, { status: 400 }); } // Check if the employee being deleted exists and if current user can delete them const existingEmployee = await prisma.employee.findUnique({ where: { id: parseInt(id) }, select: { authLevel: true } }); if (!existingEmployee) { return json({ errors: { form: "Employee not found" } }, { status: 404 }); } // Level 2 users cannot delete Level 3 employees if (user.authLevel === 2 && existingEmployee.authLevel === 3) { return json({ errors: { form: "You don't have permission to delete Super Admin users" } }, { status: 403 }); } try { await prisma.employee.delete({ where: { id: parseInt(id) } }); return json({ success: "Employee deleted successfully!" }); } catch (error) { return json({ errors: { form: "Cannot delete employee with existing reports" } }, { status: 400 }); } } return json({ errors: { form: "Invalid action" } }, { status: 400 }); }; export default function Employees() { const { user, employees } = useLoaderData(); const actionData = useActionData(); const navigation = useNavigation(); const [editingEmployee, setEditingEmployee] = useState<{ id: number; name: string; username: string; email: string; authLevel: number; status: string } | null>(null); const [showModal, setShowModal] = useState(false); const [toast, setToast] = useState<{ message: string; type: "success" | "error" } | null>(null); const isSubmitting = navigation.state === "submitting"; const isEditing = editingEmployee !== null; // Handle success/error messages useEffect(() => { if (actionData?.success) { setToast({ message: actionData.success, type: "success" }); setShowModal(false); setEditingEmployee(null); } else if (actionData?.errors?.form) { setToast({ message: actionData.errors.form, type: "error" }); } }, [actionData]); const handleEdit = (employee: { id: number; name: string; username: string; email: string; authLevel: number; status: string }) => { setEditingEmployee(employee); setShowModal(true); }; const handleAdd = () => { setEditingEmployee(null); setShowModal(true); }; const handleCloseModal = () => { setShowModal(false); setEditingEmployee(null); }; const getAuthLevelBadge = (authLevel: number) => { const colors = { 1: "bg-yellow-100 text-yellow-800", 2: "bg-green-100 text-green-800", 3: "bg-blue-100 text-blue-800" }; return colors[authLevel as keyof typeof colors] || "bg-gray-100 text-gray-800"; }; const getAuthLevelText = (authLevel: number) => { const levels = { 1: "User", 2: "Admin", 3: "Super Admin" }; return levels[authLevel as keyof typeof levels] || "Unknown"; }; const getAuthLevelIcon = (authLevel: number) => { switch (authLevel) { case 1: return ( ); case 2: return ( ); case 3: return ( ); default: return ( ); } }; const getStatusBadge = (status: string) => { return status === 'active' ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"; }; const getStatusIcon = (status: string) => { return status === 'active' ? ( ) : ( ); }; return (

Employee Management

Manage system users and their access levels

{/* Employees Table - Desktop */}
{employees.map((employee) => ( ))}
Employee Username Email Access Level Status Reports Count Actions
{getAuthLevelIcon(employee.authLevel)}
{employee.name}
{/*
ID #{employee.id}
*/}
{employee.username}
{employee.email}
Level {employee.authLevel} - {getAuthLevelText(employee.authLevel)} {getStatusIcon(employee.status)} {employee.status} {employee._count.reports} reports
{employee.id !== user.id && ( <>
)}
{/* Employees Cards - Mobile */}
{employees.map((employee) => (
{getAuthLevelIcon(employee.authLevel)}
{employee.name}
{employee.username}
{getStatusIcon(employee.status)} {employee.status}
Email: {employee.email}
Access Level: Level {employee.authLevel} - {getAuthLevelText(employee.authLevel)}
Reports: {employee._count.reports} reports
{employee.id !== user.id && (
)}
))}
{employees.length === 0 && (

No employees

Get started by adding your first employee.

)}
{/* Form Modal */} {/* Toast Notifications */} {toast && ( setToast(null)} /> )}
); }