smart-attendance-api / frontend /src /components /teacher /DeviceChangeTable.tsx
Pratham200Rajbhar
first commit
78013c4
Raw
History Blame Contribute Delete
4.88 kB
"use client";
import React, { useState } from "react";
import { CheckCircle2, XCircle, Smartphone, AlertCircle } from "lucide-react";
import GlassTable, { TableColumn } from "@/components/ui/GlassTable";
import api, { getApiErrorMessage } from "@/lib/api";
import { toast } from "react-hot-toast";
interface DeviceChangeRequest {
id: string;
student_id: string;
student_name: string;
enrollment_number: string;
new_device_uuid: string;
reason: string | null;
status: string;
created_at: string;
}
interface DeviceChangeTableProps {
requests: DeviceChangeRequest[];
onActionComplete: () => void;
}
export default function DeviceChangeTable({ requests, onActionComplete }: DeviceChangeTableProps): React.ReactElement {
const [processing, setProcessing] = useState<string | null>(null);
const formatDate = (dateStr: string): string => {
try {
const date = new Date(dateStr);
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
} catch {
return dateStr;
}
};
const formatTime = (dateStr: string): string => {
try {
const date = new Date(dateStr);
return date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: true });
} catch {
return "";
}
};
const handleAction = async (id: string, status: "APPROVED" | "REJECTED"): Promise<void> => {
try {
setProcessing(id);
await api.put(`/teacher/device-changes/${id}/approve`, { status });
toast.success(`Request ${status.toLowerCase()} successfully`);
onActionComplete();
} catch (err: unknown) {
toast.error(getApiErrorMessage(err, "Action failed"));
} finally {
setProcessing(null);
}
};
const columns: TableColumn<DeviceChangeRequest & Record<string, unknown>>[] = [
{
key: "student",
header: "Student Details",
render: (req) => (
<div>
<p className="text-sm font-bold text-slate-200">{req.student_name}</p>
<p className="text-xs text-slate-400 mt-0.5">{req.enrollment_number}</p>
</div>
),
},
{
key: "request",
header: "Request Info",
render: (req) => (
<div>
<div className="flex items-center gap-2">
<Smartphone size={14} className="text-blue-400" />
<span className="text-xs font-mono text-slate-300">
{req.new_device_uuid.substring(0, 8)}...
</span>
</div>
{req.reason && (
<div className="flex items-center gap-1 mt-1 text-xs text-amber-200/80 bg-amber-500/10 w-fit px-2 py-0.5 rounded">
<AlertCircle size={10} />
<span className="truncate max-w-[200px]">{req.reason}</span>
</div>
)}
</div>
),
},
{
key: "date",
header: "Date Submitted",
render: (req) => (
<div className="text-sm text-slate-300">
{formatDate(req.created_at)}
<div className="text-xs text-slate-500">{formatTime(req.created_at)}</div>
</div>
),
},
{
key: "actions",
header: "Actions",
render: (req) => (
<div className="flex items-center justify-end gap-2">
<button
onClick={() => handleAction(req.id, "REJECTED")}
disabled={processing !== null}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-red-500/10 text-red-400 hover:bg-red-500/20 disabled:opacity-50 transition-colors text-sm font-semibold"
>
<XCircle size={16} />
Reject
</button>
<button
onClick={() => handleAction(req.id, "APPROVED")}
disabled={processing !== null}
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20 disabled:opacity-50 transition-colors text-sm font-semibold shadow-[0_0_10px_rgba(16,185,129,0.1)]"
>
<CheckCircle2 size={16} />
Approve
</button>
</div>
),
},
];
if (requests.length === 0) {
return (
<div className="flex flex-col items-center justify-center p-12 text-center border rounded-2xl bg-white/5 border-white/10">
<div className="w-16 h-16 rounded-full bg-white/5 flex items-center justify-center mb-4">
<Smartphone className="text-slate-400" size={32} />
</div>
<h3 className="text-xl font-bold text-white mb-2 font-[Outfit]">No Pending Requests</h3>
<p className="text-slate-400 max-w-sm">
All device change requests have been processed. Students can request device changes from their mobile app.
</p>
</div>
);
}
return <GlassTable columns={columns} data={requests as (DeviceChangeRequest & Record<string, unknown>)[]} />;
}