test
This commit is contained in:
parent
ba76763c24
commit
a5b5c6def8
@ -17,8 +17,10 @@ import { Checkbox } from '@/components/ui/checkbox';
|
||||
import ConfirmDialog from '@/components/ConfirmDialog';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useAuth } from '../context/useAuth';
|
||||
|
||||
const AdminUsersPage = () => {
|
||||
const { currentUser, logout } = useAuth();
|
||||
const [users, setUsers] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
@ -78,20 +80,12 @@ const AdminUsersPage = () => {
|
||||
}, []);
|
||||
|
||||
const handleDeleteUser = async (userId) => {
|
||||
// Remplacer window.confirm par le composant ConfirmDialog
|
||||
const confirmed = await new Promise((resolve) => {
|
||||
// Pour cette implémentation, nous allons directement utiliser le composant
|
||||
// dans le JSX plutôt que de le gérer ici
|
||||
resolve(true); // On résout directement pour l'exemple
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await userApi.deleteUser(userId);
|
||||
setUsers(users.filter(user => user.id !== userId));
|
||||
if(userId===currentUser.id){
|
||||
logout();
|
||||
}
|
||||
} catch (err) {
|
||||
setError('Erreur lors de la suppression de l\'utilisateur');
|
||||
console.error(err);
|
||||
|
||||
@ -211,12 +211,15 @@ const TimePicker = ({
|
||||
const TimeClockPage = () => {
|
||||
const videoRef = useRef(null);
|
||||
const qrControlsRef = useRef(null);
|
||||
const { isAdmin } = useAuth();
|
||||
const { isAdmin, user: authUser, currentUser } = useAuth();
|
||||
const [scanMessage, setScanMessage] = useState('');
|
||||
const [scanConflict, setScanConflict] = useState(null);
|
||||
const [conflictTime, setConflictTime] = useState(new Date().toTimeString().slice(0, 5));
|
||||
const [error, setError] = useState('');
|
||||
const [timeEntries, setTimeEntries] = useState([]);
|
||||
const [users, setUsers] = useState([]);
|
||||
const [selectedUserId, setSelectedUserId] = useState('');
|
||||
const [showAllUsers, setShowAllUsers] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isScannerDialogOpen, setIsScannerDialogOpen] = useState(false);
|
||||
const [cameraPermissionError, setCameraPermissionError] = useState(false);
|
||||
@ -234,10 +237,136 @@ const TimeClockPage = () => {
|
||||
});
|
||||
|
||||
|
||||
const getUserId = (user) => {
|
||||
if (!user) return '';
|
||||
|
||||
return String(
|
||||
user.id ??
|
||||
user.pk ??
|
||||
user.user_id ??
|
||||
user.user?.id ??
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
const getUserLabel = (user) => {
|
||||
if (!user) return 'Utilisateur';
|
||||
|
||||
const firstName = user.first_name || user.firstName || '';
|
||||
const lastName = user.last_name || user.lastName || '';
|
||||
const fullName = `${firstName} ${lastName}`.trim();
|
||||
|
||||
return (
|
||||
fullName ||
|
||||
user.username ||
|
||||
user.name ||
|
||||
user.email ||
|
||||
`Utilisateur #${getUserId(user)}`
|
||||
);
|
||||
};
|
||||
|
||||
const getEntryUserId = (entry) => {
|
||||
return String(
|
||||
entry.user_id ??
|
||||
entry.userId ??
|
||||
entry.user?.id ??
|
||||
entry.user?.pk ??
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
const getCurrentUserId = () => {
|
||||
return getUserId(authUser) || getUserId(currentUser);
|
||||
};
|
||||
|
||||
const loadTimeEntries = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const entries = await timeApi.getTimeEntries();
|
||||
setTimeEntries(entries);
|
||||
|
||||
if (!isAdmin) return;
|
||||
|
||||
// Si l'API expose une liste d'utilisateurs, on l'utilise.
|
||||
// Sinon, on reconstruit une liste à partir des pointages retournés.
|
||||
let availableUsers = [];
|
||||
|
||||
if (typeof timeApi.getUsers === 'function') {
|
||||
try {
|
||||
const apiUsers = await timeApi.getUsers();
|
||||
availableUsers = Array.isArray(apiUsers) ? apiUsers : [];
|
||||
} catch (usersError) {
|
||||
console.warn(
|
||||
'Impossible de charger la liste des utilisateurs. Fallback sur les pointages.',
|
||||
usersError,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!availableUsers.length) {
|
||||
const usersById = new Map();
|
||||
|
||||
entries.forEach((entry) => {
|
||||
const id = getEntryUserId(entry);
|
||||
|
||||
if (!id || usersById.has(id)) return;
|
||||
|
||||
const entryUser =
|
||||
typeof entry.user === 'object' && entry.user
|
||||
? entry.user
|
||||
: {
|
||||
id,
|
||||
username: entry.username,
|
||||
name: entry.user,
|
||||
};
|
||||
|
||||
usersById.set(id, entryUser);
|
||||
});
|
||||
|
||||
availableUsers = [...usersById.values()];
|
||||
}
|
||||
|
||||
const currentUserId = getCurrentUserId();
|
||||
|
||||
if (
|
||||
currentUserId &&
|
||||
!availableUsers.some(
|
||||
(availableUser) => getUserId(availableUser) === currentUserId,
|
||||
)
|
||||
) {
|
||||
const currentUser = authUser || currentUser;
|
||||
|
||||
if (currentUser) {
|
||||
availableUsers.unshift(currentUser);
|
||||
}
|
||||
}
|
||||
|
||||
availableUsers = availableUsers
|
||||
.filter((availableUser) => getUserId(availableUser))
|
||||
.sort((first, second) =>
|
||||
getUserLabel(first).localeCompare(getUserLabel(second), 'fr'),
|
||||
);
|
||||
|
||||
setUsers(availableUsers);
|
||||
|
||||
setSelectedUserId((currentSelectedId) => {
|
||||
if (
|
||||
currentSelectedId &&
|
||||
availableUsers.some(
|
||||
(availableUser) =>
|
||||
getUserId(availableUser) === String(currentSelectedId),
|
||||
)
|
||||
) {
|
||||
return currentSelectedId;
|
||||
}
|
||||
|
||||
return (
|
||||
currentUserId ||
|
||||
getUserId(availableUsers[0]) ||
|
||||
''
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
setError('Erreur lors du chargement des pointages');
|
||||
console.error(err);
|
||||
@ -246,7 +375,6 @@ const TimeClockPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Load time entries when component mounts
|
||||
useEffect(() => {
|
||||
loadTimeEntries();
|
||||
}, []);
|
||||
@ -258,12 +386,20 @@ const TimeClockPage = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAdmin && !showAllUsers && !selectedUserId) {
|
||||
setError('Veuillez sélectionner un utilisateur.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (newEntry.date === newEntry.end_date) {
|
||||
await timeApi.createTimeEntry({
|
||||
date: newEntry.date,
|
||||
start_time: newEntry.start_time,
|
||||
end_time: newEntry.end_time,
|
||||
...(isAdmin && selectedUserId
|
||||
? { user_id: selectedUserId }
|
||||
: {}),
|
||||
});
|
||||
} else {
|
||||
const dates = [];
|
||||
@ -277,12 +413,31 @@ const TimeClockPage = () => {
|
||||
|
||||
await Promise.all(dates.map((date, index) => {
|
||||
if (index === 0) {
|
||||
return timeApi.createTimeEntry({ date, start_time: newEntry.start_time });
|
||||
return timeApi.createTimeEntry({
|
||||
date,
|
||||
start_time: newEntry.start_time,
|
||||
...(isAdmin && selectedUserId
|
||||
? { user_id: selectedUserId }
|
||||
: {}),
|
||||
});
|
||||
}
|
||||
if (index === dates.length - 1) {
|
||||
return timeApi.createTimeEntry({ date, end_time: newEntry.end_time });
|
||||
return timeApi.createTimeEntry({
|
||||
date,
|
||||
end_time: newEntry.end_time,
|
||||
...(isAdmin && selectedUserId
|
||||
? { user_id: selectedUserId }
|
||||
: {}),
|
||||
});
|
||||
}
|
||||
return timeApi.createTimeEntry({ date, start_time: '00:00', end_time: '23:59' });
|
||||
return timeApi.createTimeEntry({
|
||||
date,
|
||||
start_time: '00:00',
|
||||
end_time: '23:59',
|
||||
...(isAdmin && selectedUserId
|
||||
? { user_id: selectedUserId }
|
||||
: {}),
|
||||
});
|
||||
}));
|
||||
}
|
||||
setNewEntry({
|
||||
@ -650,6 +805,16 @@ const TimeClockPage = () => {
|
||||
return `${hours}h${minutes}`;
|
||||
};
|
||||
|
||||
const displayedTimeEntries = isAdmin && !showAllUsers && selectedUserId
|
||||
? timeEntries.filter(
|
||||
(entry) => getEntryUserId(entry) === String(selectedUserId),
|
||||
)
|
||||
: timeEntries;
|
||||
|
||||
const selectedUser = users.find(
|
||||
(user) => getUserId(user) === String(selectedUserId),
|
||||
);
|
||||
|
||||
return (
|
||||
<Card className="flex h-full w-full">
|
||||
<CardHeader className="border-b">
|
||||
@ -712,10 +877,71 @@ const TimeClockPage = () => {
|
||||
{/* Time tracking section */}
|
||||
<div className="w-full">
|
||||
{isAdmin && (
|
||||
<Card className="mb-4">
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle>Utilisateur</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-end">
|
||||
<div className="min-w-0 flex-1">
|
||||
<Label htmlFor="selected-user">
|
||||
Voir les pointages de
|
||||
</Label>
|
||||
<select
|
||||
id="selected-user"
|
||||
value={selectedUserId}
|
||||
disabled={showAllUsers}
|
||||
onChange={(event) => {
|
||||
setSelectedUserId(event.target.value);
|
||||
setEditingEntry(null);
|
||||
setError('');
|
||||
}}
|
||||
className="mt-2 flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{users.length === 0 ? (
|
||||
<option value="">Aucun utilisateur disponible</option>
|
||||
) : (
|
||||
users.map((user) => (
|
||||
<option key={getUserId(user)} value={getUserId(user)}>
|
||||
{getUserLabel(user)}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<label className="flex h-10 shrink-0 cursor-pointer items-center gap-2 rounded-md border px-3 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showAllUsers}
|
||||
onChange={(event) => {
|
||||
setShowAllUsers(event.target.checked);
|
||||
setEditingEntry(null);
|
||||
setError('');
|
||||
}}
|
||||
className="size-4 accent-primary"
|
||||
/>
|
||||
<span>Voir tout le monde</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{showAllUsers && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
La sélection d’un utilisateur et le pointage manuel sont
|
||||
désactivés lorsque tous les utilisateurs sont affichés.
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{isAdmin && !showAllUsers && (
|
||||
<Card className="mb-4">
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle>
|
||||
{editingEntry ? 'Modifier le pointage' : 'Ajouter un pointage manuel'}
|
||||
{editingEntry
|
||||
? 'Modifier le pointage'
|
||||
: `Ajouter un pointage manuel${selectedUser ? ` — ${getUserLabel(selectedUser)}` : ''}`}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@ -848,14 +1074,14 @@ const TimeClockPage = () => {
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<p>Chargement des pointages...</p>
|
||||
) : timeEntries.length === 0 ? (
|
||||
) : displayedTimeEntries.length === 0 ? (
|
||||
<p>Aucun pointage enregistré</p>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{isAdmin && <TableHead>Nom</TableHead>}
|
||||
{isAdmin && <TableHead>Utilisateur</TableHead>}
|
||||
<TableHead>Date</TableHead>
|
||||
<TableHead>Entrée</TableHead>
|
||||
<TableHead>Sortie</TableHead>
|
||||
@ -864,9 +1090,16 @@ const TimeClockPage = () => {
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{getDisplayTimeEntries().map((entry) => (
|
||||
{getDisplayTimeEntries()
|
||||
.filter(
|
||||
(entry) =>
|
||||
!isAdmin ||
|
||||
showAllUsers ||
|
||||
getEntryUserId(entry) === String(selectedUserId),
|
||||
)
|
||||
.map((entry) => (
|
||||
<TableRow key={entry.id}>
|
||||
{isAdmin && <TableCell>{entry.first_name} {entry.last_name}</TableCell>}
|
||||
{isAdmin && <TableCell>{entry.user}</TableCell>}
|
||||
<TableCell>{formatDate(entry.date)}</TableCell>
|
||||
<TableCell>{formatTime(entry.start_time)}</TableCell>
|
||||
<TableCell>{formatTime(entry.end_time)}</TableCell>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user