Initial commit
All checks were successful
Docker Build and Publish / build-and-push (push) Successful in 2m17s
All checks were successful
Docker Build and Publish / build-and-push (push) Successful in 2m17s
This commit is contained in:
105
frontend/src/components/Supervisor/SupervisorDashboard.jsx
Normal file
105
frontend/src/components/Supervisor/SupervisorDashboard.jsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { pb } from '../../lib/pocketbase';
|
||||
import TimeList from '../TimeEntry/TimeList';
|
||||
import BalanceCard from '../TimeEntry/BalanceCard';
|
||||
import { LanguageContext } from '../../contexts/LanguageContext';
|
||||
|
||||
const SupervisorDashboard = ({ user }) => {
|
||||
const { t } = useContext(LanguageContext);
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const [selectedEmployeeId, setSelectedEmployeeId] = useState(null);
|
||||
const [employeeEntries, setEmployeeEntries] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadEmployees = async () => {
|
||||
try {
|
||||
// Find users who have assigned ME as supervisor
|
||||
const result = await pb.collection('users').getList(1, 50, {
|
||||
filter: `supervisor = "${user.id}"`,
|
||||
});
|
||||
setEmployees(result.items);
|
||||
} catch (err) {
|
||||
console.error("Error loading employees", err);
|
||||
}
|
||||
};
|
||||
if (user.is_supervisor) {
|
||||
loadEmployees();
|
||||
}
|
||||
}, [user.id, user.is_supervisor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedEmployeeId) {
|
||||
const loadEntries = async () => {
|
||||
try {
|
||||
const result = await pb.collection('time_entries').getList(1, 50, {
|
||||
filter: `user = "${selectedEmployeeId}"`,
|
||||
sort: '-date',
|
||||
});
|
||||
setEmployeeEntries(result.items);
|
||||
} catch (err) {
|
||||
console.error("Error loading employee entries", err);
|
||||
}
|
||||
};
|
||||
loadEntries();
|
||||
} else {
|
||||
setEmployeeEntries([]);
|
||||
}
|
||||
}, [selectedEmployeeId]);
|
||||
|
||||
if (!user.is_supervisor) return null;
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-lg shadow-md mb-6">
|
||||
<h2 className="text-xl font-bold mb-4 text-indigo-700">{t('sup.title')}</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
{/* Employee List */}
|
||||
<div className="md:col-span-1 border-r border-gray-200 pr-4">
|
||||
<h3 className="font-semibold text-gray-700 mb-2">{t('sup.my_team')}</h3>
|
||||
{employees.length === 0 ? (
|
||||
<p className="text-sm text-gray-500">{t('sup.no_employees')}</p>
|
||||
) : (
|
||||
<ul className="space-y-2">
|
||||
{employees.map(emp => (
|
||||
<li key={emp.id}>
|
||||
<button
|
||||
onClick={() => setSelectedEmployeeId(emp.id)}
|
||||
className={`w-full text-left px-3 py-2 rounded-md text-sm transition ${selectedEmployeeId === emp.id
|
||||
? 'bg-indigo-100 text-indigo-700 font-medium'
|
||||
: 'hover:bg-gray-50 text-gray-600'
|
||||
}`}
|
||||
>
|
||||
{emp.name || emp.email}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Employee Details */}
|
||||
<div className="md:col-span-3">
|
||||
{selectedEmployeeId ? (
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center bg-gray-50 p-4 rounded-lg">
|
||||
<h3 className="font-bold text-gray-800">
|
||||
{t('dash.employee_viewing')}: {employees.find(e => e.id === selectedEmployeeId)?.name || 'Employee'}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<BalanceCard userId={selectedEmployeeId} />
|
||||
|
||||
<TimeList entries={employeeEntries} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full flex items-center justify-center text-gray-400 border-2 border-dashed border-gray-200 rounded-lg">
|
||||
{t('sup.select_prompt')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupervisorDashboard;
|
||||
Reference in New Issue
Block a user