All checks were successful
Docker Build and Publish / build-and-push (push) Successful in 33s
123 lines
5.7 KiB
JavaScript
123 lines
5.7 KiB
JavaScript
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}
|
|
onEntryDeleted={() => {
|
|
// Trigger reload of this employee's entries
|
|
// We can just toggle selectedEmployeeId momentarily or just use another state?
|
|
// Better: add a refresh dependency to the effect.
|
|
// Actually, let's keep it simple: just re-set the ID to trigger effect? No that's ugly.
|
|
// Let's just re-fetch in place or add a version state.
|
|
// For now, if supervisor deletes, we might want to refresh.
|
|
// But Wait, does supervisor have permission to delete?
|
|
// User said "delete a time entry owned by the user".
|
|
// Assuming supervisor can too, or we just rely on PB permissions.
|
|
// I'll add a simple force refresh mechanism.
|
|
const currentId = selectedEmployeeId;
|
|
setSelectedEmployeeId(null);
|
|
setTimeout(() => setSelectedEmployeeId(currentId), 50);
|
|
}}
|
|
/>
|
|
</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;
|