Added assigmnet api endpoint and ui table.
This commit is contained in:
parent
a2a12a0f00
commit
c9dc3eda2c
@ -2,6 +2,7 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { account } from "../lib/appwrite";
|
||||
import { useTheme } from "../context/ThemeContext";
|
||||
import { useSidebar } from "../context/SidebarContext"; // Add this import
|
||||
|
||||
const Navbar = () => {
|
||||
const { darkMode, toggleDarkMode } = useTheme();
|
||||
@ -12,6 +13,7 @@ const { darkMode, toggleDarkMode } = useTheme();
|
||||
const [notifying, setNotifying] = useState(true); // assuming initial notifying state is true
|
||||
const [user, setUser] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { isOpen, toggleSidebar } = useSidebar(); // Use the sidebar context
|
||||
|
||||
// ------fetch user-----------------
|
||||
useEffect(() => {
|
||||
@ -80,7 +82,7 @@ useEffect(() => {
|
||||
{/* Left side - Menu Button */}
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={() => setMenuOpen(!isMenuOpen)}
|
||||
onClick={toggleSidebar}
|
||||
className={`z-99999 flex h-10 w-10 items-center justify-center rounded-lg border-gray-200 text-gray-500 dark:border-gray-800 dark:text-gray-400 lg:h-11 lg:w-11 lg:border ${isMenuOpen
|
||||
? 'lg:bg-transparent dark:lg:bg-transparent bg-gray-100 dark:bg-gray-800'
|
||||
: ''
|
||||
|
@ -1,58 +1,73 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { useTheme } from "../context/ThemeContext";
|
||||
import { useSidebar } from "../context/SidebarContext";
|
||||
|
||||
const Sidebar = () => {
|
||||
const [showFormsSubmenu, setShowFormsSubmenu] = useState(false);
|
||||
const { darkMode } = useTheme();
|
||||
const { isCollapsed } = useSidebar();
|
||||
|
||||
return (
|
||||
<aside className={`w-[290px] h-full border-r flex flex-col ${darkMode ? 'bg-gray-900 border-gray-800' : 'bg-white border-gray-200'
|
||||
}`}>
|
||||
{/* Logo */}
|
||||
<div className="py-8 flex justify-start">
|
||||
<Link href="/">
|
||||
<Image
|
||||
src={darkMode ? "/images/logo/logo-dark.svg" : "/images/logo/logo.svg"}
|
||||
alt="Logo"
|
||||
width={150}
|
||||
height={40}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<aside className={`h-full border-r flex flex-col transition-all duration-200 ease-in-out
|
||||
${isCollapsed ? 'w-20' : 'w-[290px]'}
|
||||
${darkMode ? 'bg-gray-900 border-gray-800' : 'bg-white border-gray-200'}`}>
|
||||
|
||||
{/* Logo - Only shown when expanded */}
|
||||
{!isCollapsed && (
|
||||
<div className="py-8 flex justify-start px-4">
|
||||
<Link href="/">
|
||||
<Image
|
||||
src={darkMode ? "/images/logo/logo-dark.svg" : "/images/logo/logo.svg"}
|
||||
alt="Logo"
|
||||
width={150}
|
||||
height={40}
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex flex-col overflow-y-auto no-scrollbar">
|
||||
<nav className="mb-6">
|
||||
<div className="flex flex-col gap-4">
|
||||
<h2 className="mb-4 text-xs uppercase leading-[20px] text-gray-400 dark:text-gray-500">
|
||||
<nav className="mb-6 px-2">
|
||||
{/* Menu heading - Only shown when expanded */}
|
||||
{!isCollapsed && (
|
||||
<h2 className="mb-4 text-xs uppercase leading-[20px] text-gray-400 dark:text-gray-500 px-2">
|
||||
Menu
|
||||
</h2>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
{/* Dashboard */}
|
||||
<div>
|
||||
<Link
|
||||
href="/pages/dashboard"
|
||||
className={`flex items-center w-full p-2 rounded-lg ${darkMode
|
||||
? 'text-gray-300 hover:text-white hover:bg-gray-800'
|
||||
: 'text-gray-600 hover:text-brand-500 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src="/images/icons/grid.svg"
|
||||
alt="Dashboard"
|
||||
className={`w-5 h-5 mr-3 ${darkMode ? 'filter invert' : ''}`}
|
||||
/>
|
||||
<span className="flex-1 text-left">Dashboard</span>
|
||||
</Link>
|
||||
</div>
|
||||
<Link
|
||||
href="/pages/dashboard"
|
||||
className={`flex items-center w-full p-2 rounded-lg group
|
||||
${darkMode
|
||||
? 'text-gray-300 hover:text-white hover:bg-gray-800'
|
||||
: 'text-gray-600 hover:text-brand-500 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src="/images/icons/grid.svg"
|
||||
alt="Dashboard"
|
||||
className={`w-5 h-5 ${isCollapsed ? 'mx-auto' : 'mr-3'} ${darkMode ? 'filter invert' : ''}`}
|
||||
/>
|
||||
{!isCollapsed && <span className="flex-1 text-left">Dashboard</span>}
|
||||
{isCollapsed && (
|
||||
<span className={`absolute left-full ml-2 px-2 py-1 text-xs rounded-md shadow-lg
|
||||
${darkMode ? 'bg-gray-800 text-white' : 'bg-white text-gray-900'}
|
||||
opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap`}>
|
||||
Dashboard
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
{/* -------------table01 page--------------- */}
|
||||
{/* table01 page */}
|
||||
<Link
|
||||
href="/pages/table01"
|
||||
className={`flex items-center w-full p-2 rounded-lg ${darkMode
|
||||
className={`flex items-center w-full p-2 rounded-lg group
|
||||
${darkMode
|
||||
? 'text-gray-300 hover:text-white hover:bg-gray-800'
|
||||
: 'text-gray-600 hover:text-brand-500 hover:bg-gray-100'
|
||||
}`}
|
||||
@ -60,14 +75,23 @@ const Sidebar = () => {
|
||||
<img
|
||||
src="/file.svg"
|
||||
alt="table01"
|
||||
className={`w-5 h-5 mr-3 ${darkMode ? 'filter invert' : ''}`}
|
||||
className={`w-5 h-5 ${isCollapsed ? 'mx-auto' : 'mr-3'} ${darkMode ? 'filter invert' : ''}`}
|
||||
/>
|
||||
<span className="flex-1 text-left">table01</span>
|
||||
{!isCollapsed && <span className="flex-1 text-left">table01</span>}
|
||||
{isCollapsed && (
|
||||
<span className={`absolute left-full ml-2 px-2 py-1 text-xs rounded-md shadow-lg
|
||||
${darkMode ? 'bg-gray-800 text-white' : 'bg-white text-gray-900'}
|
||||
opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap`}>
|
||||
table01
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
{/* ---------------TemporaryTable-------------- */}
|
||||
|
||||
{/* register employee */}
|
||||
<Link
|
||||
href="/pages/register-employee"
|
||||
className={`flex items-center w-full p-2 rounded-lg ${darkMode
|
||||
className={`flex items-center w-full p-2 rounded-lg group
|
||||
${darkMode
|
||||
? 'text-gray-300 hover:text-white hover:bg-gray-800'
|
||||
: 'text-gray-600 hover:text-brand-500 hover:bg-gray-100'
|
||||
}`}
|
||||
@ -75,14 +99,23 @@ const Sidebar = () => {
|
||||
<img
|
||||
src="/file.svg"
|
||||
alt="register employee"
|
||||
className={`w-5 h-5 mr-3 ${darkMode ? 'filter invert' : ''}`}
|
||||
className={`w-5 h-5 ${isCollapsed ? 'mx-auto' : 'mr-3'} ${darkMode ? 'filter invert' : ''}`}
|
||||
/>
|
||||
<span className="flex-1 text-left">register employee</span>
|
||||
{!isCollapsed && <span className="flex-1 text-left">register employee</span>}
|
||||
{isCollapsed && (
|
||||
<span className={`absolute left-full ml-2 px-2 py-1 text-xs rounded-md shadow-lg
|
||||
${darkMode ? 'bg-gray-800 text-white' : 'bg-white text-gray-900'}
|
||||
opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap`}>
|
||||
register employee
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
{/* --------------register employee--------- */}
|
||||
|
||||
{/* TemporaryTable */}
|
||||
<Link
|
||||
href="/pages/TemporaryTable"
|
||||
className={`flex items-center w-full p-2 rounded-lg ${darkMode
|
||||
className={`flex items-center w-full p-2 rounded-lg group
|
||||
${darkMode
|
||||
? 'text-gray-300 hover:text-white hover:bg-gray-800'
|
||||
: 'text-gray-600 hover:text-brand-500 hover:bg-gray-100'
|
||||
}`}
|
||||
@ -90,15 +123,23 @@ const Sidebar = () => {
|
||||
<img
|
||||
src="/file.svg"
|
||||
alt="TemporaryTable"
|
||||
className={`w-5 h-5 mr-3 ${darkMode ? 'filter invert' : ''}`}
|
||||
className={`w-5 h-5 ${isCollapsed ? 'mx-auto' : 'mr-3'} ${darkMode ? 'filter invert' : ''}`}
|
||||
/>
|
||||
<span className="flex-1 text-left">TemporaryTable</span>
|
||||
{!isCollapsed && <span className="flex-1 text-left">TemporaryTable</span>}
|
||||
{isCollapsed && (
|
||||
<span className={`absolute left-full ml-2 px-2 py-1 text-xs rounded-md shadow-lg
|
||||
${darkMode ? 'bg-gray-800 text-white' : 'bg-white text-gray-900'}
|
||||
opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap`}>
|
||||
TemporaryTable
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
{/* ----------------------- */}
|
||||
{/* ----------Trainees Data------------- */}
|
||||
|
||||
{/* Trainees Data */}
|
||||
<Link
|
||||
href="/pages/TraineesData"
|
||||
className={`flex items-center w-full p-2 rounded-lg ${darkMode
|
||||
className={`flex items-center w-full p-2 rounded-lg group
|
||||
${darkMode
|
||||
? 'text-gray-300 hover:text-white hover:bg-gray-800'
|
||||
: 'text-gray-600 hover:text-brand-500 hover:bg-gray-100'
|
||||
}`}
|
||||
@ -106,11 +147,17 @@ const Sidebar = () => {
|
||||
<img
|
||||
src="/file.svg"
|
||||
alt="TraineesData"
|
||||
className={`w-5 h-5 mr-3 ${darkMode ? 'filter invert' : ''}`}
|
||||
className={`w-5 h-5 ${isCollapsed ? 'mx-auto' : 'mr-3'} ${darkMode ? 'filter invert' : ''}`}
|
||||
/>
|
||||
<span className="flex-1 text-left">Trainees Data</span>
|
||||
{!isCollapsed && <span className="flex-1 text-left">Trainees Data</span>}
|
||||
{isCollapsed && (
|
||||
<span className={`absolute left-full ml-2 px-2 py-1 text-xs rounded-md shadow-lg
|
||||
${darkMode ? 'bg-gray-800 text-white' : 'bg-white text-gray-900'}
|
||||
opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap`}>
|
||||
Trainees Data
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
{/* ----------------------- */}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
@ -1,63 +1,32 @@
|
||||
// src/app/context/SidebarContext.js
|
||||
'use client';
|
||||
import { useState } from "react";
|
||||
import { account } from "../lib/appwrite";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { createContext, useContext, useState } from "react";
|
||||
|
||||
export default function AuthForm() {
|
||||
const [isLogin, setIsLogin] = useState(true);
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const router = useRouter();
|
||||
const SidebarContext = createContext();
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
export function SidebarProvider({ children }) {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false); // Changed from isOpen to isCollapsed
|
||||
|
||||
try {
|
||||
if (isLogin) {
|
||||
await account.createEmailPasswordSession(email, password);
|
||||
} else {
|
||||
await account.create('unique()', email, password);
|
||||
await account.createEmailPasswordSession(email, password);
|
||||
}
|
||||
router.push("/pages/dashboard");
|
||||
} catch (err) {
|
||||
alert("Authentication error: " + err.message);
|
||||
}
|
||||
};
|
||||
const toggleSidebar = () => setIsCollapsed(!isCollapsed);
|
||||
const collapseSidebar = () => setIsCollapsed(true);
|
||||
const expandSidebar = () => setIsCollapsed(false);
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto mt-10 p-6 border rounded-lg shadow-lg">
|
||||
<h2 className="text-2xl font-bold mb-4">{isLogin ? 'Login' : 'Register'}</h2>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
className="w-full border px-3 py-2 rounded"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
className="w-full border px-3 py-2 rounded"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button type="submit" className="w-full bg-blue-600 text-white py-2 rounded">
|
||||
{isLogin ? "Login" : "Register"}
|
||||
</button>
|
||||
</form>
|
||||
<p className="mt-4 text-sm text-center">
|
||||
{isLogin ? "New user?" : "Already registered?"}{" "}
|
||||
<button
|
||||
onClick={() => setIsLogin(!isLogin)}
|
||||
className="text-blue-500 underline"
|
||||
>
|
||||
{isLogin ? "Register here" : "Login here"}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
<SidebarContext.Provider value={{
|
||||
isCollapsed,
|
||||
toggleSidebar,
|
||||
collapseSidebar,
|
||||
expandSidebar
|
||||
}}>
|
||||
{children}
|
||||
</SidebarContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useSidebar() {
|
||||
const context = useContext(SidebarContext);
|
||||
if (!context) {
|
||||
throw new Error('useSidebar must be used within a SidebarProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
@ -6,6 +6,7 @@ import Navbar from "./components/Navbar";
|
||||
import Sidebar from "./components/Sidebar";
|
||||
import { AuthProvider } from "./context/AuthContext";
|
||||
import { ThemeProvider,useTheme } from "./context/ThemeContext";
|
||||
import { SidebarProvider } from './context/SidebarContext';
|
||||
|
||||
function LayoutContent({ children }) {
|
||||
const { darkMode } = useTheme();
|
||||
@ -48,7 +49,9 @@ export default function RootLayout({ children }) {
|
||||
<link rel="icon" href="/images/brand/brand-08.svg" />
|
||||
</head>
|
||||
<ThemeProvider>
|
||||
<SidebarProvider>
|
||||
<LayoutContent>{children}</LayoutContent>
|
||||
</SidebarProvider>
|
||||
</ThemeProvider>
|
||||
</html>
|
||||
);
|
||||
|
@ -2,31 +2,34 @@
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '../../ui/Table';
|
||||
|
||||
export default function TraineesDataPage() {
|
||||
const [trainees, setTrainees] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('https://dtsnew.adhcloud.space/items/trainees', {
|
||||
headers: {
|
||||
Authorization: 'Bearer G_2h5o1NlX1_PfomkUe5zk7Xx0dT2juV',
|
||||
},
|
||||
})
|
||||
|
||||
// fetch('http://localhost:8055/items/trainees')
|
||||
|
||||
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setTrainees(data.data || []);
|
||||
setLoading(false);
|
||||
useEffect(() => {
|
||||
fetch(`${process.env.NEXT_PUBLIC_API_URL}/items/trainees`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_API_TOKEN}`,
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Error fetching trainees:', err);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setTrainees(data.data || []);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Error fetching trainees:', err);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main className="p-6">
|
||||
@ -34,20 +37,37 @@ export default function TraineesDataPage() {
|
||||
{loading ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<ul className="space-y-2">
|
||||
{trainees.map((trainee) => (
|
||||
<li key={trainee.id} className="border p-4 rounded-md shadow">
|
||||
<Link href={`/pages/TraineesData/trainee/${trainee.id}`}>
|
||||
<span className="text-blue-600 hover:underline cursor-pointer">
|
||||
{trainee.Name}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="overflow-auto rounded-lg border border-gray-200 bg-white">
|
||||
<Table>
|
||||
<TableHeader className="border-b border-gray-100">
|
||||
<TableRow>
|
||||
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500">Name</TableCell>
|
||||
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500">Batch</TableCell>
|
||||
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500">DOJ</TableCell>
|
||||
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500">Speed</TableCell>
|
||||
<TableCell isHeader className="px-5 py-3 font-medium text-gray-500">Accuracy</TableCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody className="divide-y divide-gray-100">
|
||||
{trainees.map((trainee) => (
|
||||
<TableRow key={trainee.id}>
|
||||
<TableCell className="px-5 py-4 text-blue-600 hover:underline">
|
||||
<Link href={`/pages/TraineesData/trainee/${trainee.id}`}>
|
||||
{trainee.Name}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell className="px-5 py-4">{trainee.batch_name}</TableCell>
|
||||
<TableCell className="px-5 py-4">{trainee.doj}</TableCell>
|
||||
<TableCell className="px-5 py-4">{trainee.speed}</TableCell>
|
||||
<TableCell className="px-5 py-4">{trainee.accuracy}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
@ -1,29 +1,12 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
// async function getTrainee(id) {
|
||||
// try {
|
||||
// const res = await fetch(`http://localhost:8055/items/trainees/${id}`);
|
||||
// const json = await res.json();
|
||||
// return json.data;
|
||||
// } catch (error) {
|
||||
// console.error('Error fetching trainee:', error);
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
async function getTrainee(id) {
|
||||
try {
|
||||
const res = await fetch(`https://dtsnew.adhcloud.space/items/trainees/${id}`, {
|
||||
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/items/trainees/${id}`, {
|
||||
headers: {
|
||||
Authorization: 'Bearer G_2h5o1NlX1_PfomkUe5zk7Xx0dT2juV',
|
||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_API_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
// if we are using directus API
|
||||
// const res = await fetch(`http://localhost:8055/items/trainees/${id}`);
|
||||
|
||||
|
||||
const json = await res.json();
|
||||
return json.data;
|
||||
} catch (error) {
|
||||
@ -32,28 +15,125 @@ async function getTrainee(id) {
|
||||
}
|
||||
}
|
||||
|
||||
async function getAssignmentsByTrainee(traineeId) {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_API_URL}/items/assignment?filter[sub_by][_eq]=${traineeId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.NEXT_PUBLIC_API_TOKEN}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
const json = await res.json();
|
||||
return json.data || [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching assignments:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ✅ Destructure 'params' at the top level
|
||||
export default async function TraineeDetailPage({ params: { id } }) {
|
||||
const trainee = await getTrainee(id);
|
||||
const assignments = await getAssignmentsByTrainee(id);
|
||||
|
||||
if (!trainee) return notFound();
|
||||
|
||||
return (
|
||||
<main className="p-6">
|
||||
<h1 className="text-2xl font-bold mb-4">{trainee.Name}</h1>
|
||||
<ul className="space-y-1">
|
||||
<li><strong>Feedback:</strong> {trainee.overall_feedback}</li>
|
||||
<li><strong>Attendance:</strong> {trainee.overall_attendance}</li>
|
||||
<li><strong>Batch:</strong> {trainee.batch_name}</li>
|
||||
<li><strong>Speed:</strong> {trainee.speed}</li>
|
||||
<li><strong>Accuracy:</strong> {trainee.accuracy}</li>
|
||||
<li><strong>Precision:</strong> {trainee.precision}</li>
|
||||
<li><strong>DOJ:</strong> {trainee.doj}</li>
|
||||
</ul>
|
||||
|
||||
<div className="mb-8">
|
||||
<h2 className="text-xl font-semibold mb-2">Trainee Details</h2>
|
||||
<ul className="space-y-1">
|
||||
<li><strong>Feedback:</strong> {trainee.overall_feedback}</li>
|
||||
<li><strong>Attendance:</strong> {trainee.overall_attendance}</li>
|
||||
<li><strong>Batch:</strong> {trainee.batch_name}</li>
|
||||
<li><strong>Speed:</strong> {trainee.speed}</li>
|
||||
<li><strong>Accuracy:</strong> {trainee.accuracy}</li>
|
||||
<li><strong>Precision:</strong> {trainee.precision}</li>
|
||||
<li><strong>DOJ:</strong> {trainee.doj}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-2">Assignments</h2>
|
||||
{assignments.length === 0 ? (
|
||||
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4">
|
||||
<div className="flex">
|
||||
<div className="ml-3">
|
||||
<p className="text-sm text-yellow-700">
|
||||
No assignments found for this trainee. This could mean:
|
||||
</p>
|
||||
<ul className="list-disc pl-5 mt-1 text-sm text-yellow-700">
|
||||
<li>The trainee hasn't submitted any assignments yet</li>
|
||||
<li>Assignments exist but are associated with a different ID</li>
|
||||
<li>There might be a data connection issue</li>
|
||||
</ul>
|
||||
<div className="mt-2 text-xs text-yellow-600">
|
||||
<p>Debug info:</p>
|
||||
<p>Trainee ID: {id}</p>
|
||||
<p>Total assignments in system: {assignments.length}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{assignments.map((assignment, index) => (
|
||||
<div key={index} className="border p-4 rounded-md shadow">
|
||||
<h3 className="font-medium mb-2">Assignment {index + 1}</h3>
|
||||
<p><strong>Format:</strong> {assignment.format || 'Not specified'}</p>
|
||||
{assignment.link && (
|
||||
<p>
|
||||
<strong>Main Link:</strong>{" "}
|
||||
<a
|
||||
href={assignment.link.includes('://') ? assignment.link : `https://${assignment.link}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
{assignment.link}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="mt-3">
|
||||
<h4 className="font-medium">Submissions:</h4>
|
||||
{assignment.assignments && assignment.assignments.length > 0 ? (
|
||||
<ul className="space-y-2 mt-2">
|
||||
{assignment.assignments.map((submission, subIndex) => (
|
||||
<li key={subIndex} className="border-l-2 pl-3 py-1">
|
||||
<p>
|
||||
<strong>Submission {subIndex + 1}:</strong>{" "}
|
||||
{submission.link_or_zip?.includes('://') ? (
|
||||
<a
|
||||
href={submission.link_or_zip}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
{submission.link_or_zip}
|
||||
</a>
|
||||
) : (
|
||||
submission.link_or_zip || 'No link provided'
|
||||
)}
|
||||
</p>
|
||||
{submission.info && (
|
||||
<p className="mt-1"><strong>Feedback:</strong> {submission.info}</p>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500">No submissions yet</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user