From a5b5c6def8b6057b2c51f6a8ce8e42ff55bca72c Mon Sep 17 00:00:00 2001 From: Louis Mazin Date: Sat, 19 Sep 2026 22:29:29 +0200 Subject: [PATCH] test --- frontend/src/pages/AdminUsersPage.jsx | 16 +- frontend/src/pages/TimeClockPage.jsx | 253 +++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 21 deletions(-) diff --git a/frontend/src/pages/AdminUsersPage.jsx b/frontend/src/pages/AdminUsersPage.jsx index c730937..1d2ad7d 100644 --- a/frontend/src/pages/AdminUsersPage.jsx +++ b/frontend/src/pages/AdminUsersPage.jsx @@ -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); diff --git a/frontend/src/pages/TimeClockPage.jsx b/frontend/src/pages/TimeClockPage.jsx index cf34330..053c1ff 100644 --- a/frontend/src/pages/TimeClockPage.jsx +++ b/frontend/src/pages/TimeClockPage.jsx @@ -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 ( @@ -712,10 +877,71 @@ const TimeClockPage = () => { {/* Time tracking section */}
{isAdmin && ( + + + Utilisateur + + +
+
+ + +
+ + +
+ + {showAllUsers && ( +

+ La sélection d’un utilisateur et le pointage manuel sont + désactivés lorsque tous les utilisateurs sont affichés. +

+ )} +
+
+ )} + + {isAdmin && !showAllUsers && ( - {editingEntry ? 'Modifier le pointage' : 'Ajouter un pointage manuel'} + {editingEntry + ? 'Modifier le pointage' + : `Ajouter un pointage manuel${selectedUser ? ` — ${getUserLabel(selectedUser)}` : ''}`} @@ -848,14 +1074,14 @@ const TimeClockPage = () => { {loading ? (

Chargement des pointages...

- ) : timeEntries.length === 0 ? ( + ) : displayedTimeEntries.length === 0 ? (

Aucun pointage enregistré

) : (
- {isAdmin && Nom} + {isAdmin && Utilisateur} Date Entrée Sortie @@ -864,9 +1090,16 @@ const TimeClockPage = () => { - {getDisplayTimeEntries().map((entry) => ( + {getDisplayTimeEntries() + .filter( + (entry) => + !isAdmin || + showAllUsers || + getEntryUserId(entry) === String(selectedUserId), + ) + .map((entry) => ( - {isAdmin && {entry.first_name} {entry.last_name}} + {isAdmin && {entry.user}} {formatDate(entry.date)} {formatTime(entry.start_time)} {formatTime(entry.end_time)}