feat: Gestion des profils des enseignants / Visualisation d'une classe [#4]

This commit is contained in:
N3WT DE COMPET
2024-11-23 20:02:51 +01:00
parent af0cd1c840
commit 81d1dfa9a7
26 changed files with 792 additions and 178 deletions

View File

@ -4,6 +4,8 @@ import React, { useState, useEffect } from 'react';
import { useTranslations } from 'next-intl';
import { Users, Clock, CalendarCheck, School, TrendingUp, UserCheck } from 'lucide-react';
import Loader from '@/components/Loader';
import { BK_GESTIONINSCRIPTION_CLASSES_URL } from '@/utils/Url';
import ClasseDetails from '@/components/ClasseDetails';
// Composant StatCard pour afficher une statistique
const StatCard = ({ title, value, icon, change, color = "blue" }) => (
@ -54,7 +56,23 @@ export default function DashboardPage() {
}
});
const [classes, setClasses] = useState([]);
const fetchClasses = () => {
fetch(`${BK_GESTIONINSCRIPTION_CLASSES_URL}`)
.then(response => response.json())
.then(data => {
setClasses(data);
})
.catch(error => {
console.error('Error fetching classes:', error);
});
};
useEffect(() => {
// Fetch data for classes
fetchClasses();
// Simulation de chargement des données
setTimeout(() => {
setStats({
@ -120,7 +138,7 @@ export default function DashboardPage() {
</div>
{/* Événements et KPIs */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
{/* Graphique des inscriptions */}
<div className="lg:col-span-2 bg-white p-6 rounded-lg shadow-sm border border-gray-100">
<h2 className="text-lg font-semibold mb-4">{t('inscriptionTrends')}</h2>
@ -138,6 +156,14 @@ export default function DashboardPage() {
))}
</div>
</div>
<div className="flex flex-wrap">
{classes.map((classe) => (
<div className="lg:col-span-2 bg-white p-6 rounded-lg shadow-sm border border-gray-100 mr-4">
<ClasseDetails key={classe.id} classe={classe} />
</div>
))}
</div>
</div>
);
}

View File

@ -1,10 +1,8 @@
'use client'
import React, { useState, useEffect } from 'react';
import Table from '@/components/Table';
import SpecialitiesSection from '@/components/SpecialitiesSection'
import ClassesSection from '@/components/ClassesSection'
import TeachersSection from '@/components/TeachersSection';
import { User, School } from 'lucide-react'
import { BK_GESTIONINSCRIPTION_SPECIALITES_URL,
BK_GESTIONINSCRIPTION_CLASSES_URL,
BK_GESTIONINSCRIPTION_SPECIALITE_URL,
@ -66,7 +64,6 @@ export default function Page() {
};
const handleCreate = (url, newData, setDatas) => {
console.log('SEND POST :', JSON.stringify(newData));
fetch(url, {
method: 'POST',
headers: {

View File

@ -13,19 +13,29 @@ import Button from '@/components/Button';
import DropdownMenu from "@/components/DropdownMenu";
import { swapFormatDate } from '@/utils/Date';
import { formatPhoneNumber } from '@/utils/Telephone';
import { MoreVertical, Send, Edit, Trash2, FileText, ChevronUp, UserPlus } from 'lucide-react';
import { MoreVertical, Send, Edit, Trash2, FileText, ChevronUp, UserPlus, CheckCircle } from 'lucide-react';
import Modal from '@/components/Modal';
import InscriptionForm from '@/components/Inscription/InscriptionForm'
import AffectationClasseForm from '@/components/AffectationClasseForm'
import { BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL, BK_GESTIONINSCRIPTION_SEND_URL, FR_ADMIN_STUDENT_EDIT_SUBSCRIBE, BK_GESTIONINSCRIPTION_ARCHIVE_URL } from '@/utils/Url';
import { BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL,
BK_GESTIONINSCRIPTION_SEND_URL,
FR_ADMIN_STUDENT_EDIT_SUBSCRIBE,
BK_GESTIONINSCRIPTION_ARCHIVE_URL,
BK_GESTIONINSCRIPTION_CLASSES_URL,
BK_GESTIONINSCRIPTION_FICHEINSCRIPTION_URL } from '@/utils/Url';
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
import useCsrfToken from '@/hooks/useCsrfToken';
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
export default function Page({ params: { locale } }) {
const t = useTranslations('students');
const [ficheInscriptions, setFicheInscriptions] = useState([]);
const [ficheInscriptionsData, setFicheInscriptionsData] = useState([]);
const [fichesInscriptionsDataArchivees, setFicheInscriptionsDataArchivees] = useState([]);
const [fichesInscriptionsDataEnCours, setFichesInscriptionsDataEnCours] = useState([]);
const [fichesInscriptionsDataInscrits, setichesInscriptionsDataInscrits] = useState([]);
const [fichesInscriptionsDataArchivees, setFichesInscriptionsDataArchivees] = useState([]);
// const [filter, setFilter] = useState('*');
const [searchTerm, setSearchTerm] = useState('');
const [alertPage, setAlertPage] = useState(false);
@ -33,22 +43,32 @@ export default function Page({ params: { locale } }) {
const [ficheArchivee, setFicheArchivee] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [popup, setPopup] = useState({ visible: false, message: '', onConfirm: null });
const [activeTab, setActiveTab] = useState('all');
const [activeTab, setActiveTab] = useState('pending');
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [totalStudents, setTotalStudents] = useState(0);
const [totalPending, setTotalPending] = useState(0);
const [totalSubscribed, setTotalSubscribed] = useState(0);
const [totalArchives, setTotalArchives] = useState(0);
const [itemsPerPage, setItemsPerPage] = useState(5); // Définir le nombre d'éléments par page
const [isOpen, setIsOpen] = useState(false);
const [isOpenAffectationClasse, setIsOpenAffectationClasse] = useState(false);
const [eleve, setEleve] = useState('');
const [classes, setClasses] = useState([]);
const csrfToken = useCsrfToken();
const openModal = () => {
setIsOpen(true);
}
const openModalAssociationEleve = (eleveSelected) => {
setIsOpenAffectationClasse(true);
setEleve(eleveSelected);
}
// Modifier la fonction fetchData pour inclure le terme de recherche
const fetchData = (page, pageSize, search = '') => {
const url = `${BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL}/all?page=${page}&page_size=${pageSize}&search=${search}`;
const url = `${BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL}/pending?page=${page}&page_size=${pageSize}&search=${search}`;
fetch(url, {
headers: {
'Content-Type': 'application/json',
@ -58,11 +78,33 @@ export default function Page({ params: { locale } }) {
setIsLoading(false);
if (data) {
const { fichesInscriptions, count } = data;
setFicheInscriptionsData(fichesInscriptions);
setFichesInscriptionsDataEnCours(fichesInscriptions);
const calculatedTotalPages = Math.ceil(count / pageSize);
setTotalStudents(count);
setTotalPending(count);
setTotalPages(calculatedTotalPages);
}
console.log('Success PENDING:', data);
})
.catch(error => {
console.error('Error fetching data:', error);
setIsLoading(false);
});
};
const fetchDataSubscribed = () => {
fetch(`${BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL}/subscribed`, {
headers: {
'Content-Type': 'application/json',
},
}).then(response => response.json())
.then(data => {
setIsLoading(false);
if (data) {
const { fichesInscriptions, count } = data;
setTotalSubscribed(count);
setichesInscriptionsDataInscrits(fichesInscriptions);
}
console.log('Success SUBSCRIBED:', data);
})
.catch(error => {
console.error('Error fetching data:', error);
@ -81,7 +123,7 @@ export default function Page({ params: { locale } }) {
if (data) {
const { fichesInscriptions, count } = data;
setTotalArchives(count);
setFicheInscriptionsDataArchivees(fichesInscriptions);
setFichesInscriptionsDataArchivees(fichesInscriptions);
}
console.log('Success ARCHIVED:', data);
})
@ -91,14 +133,31 @@ export default function Page({ params: { locale } }) {
});
};
const fetchClasses = () => {
fetch(`${BK_GESTIONINSCRIPTION_CLASSES_URL}`)
.then(response => response.json())
.then(data => {
setClasses(data);
console.log("classes : ", data)
})
.catch(error => {
console.error('Error fetching classes:', error);
});
};
useEffect(() => {
fetchClasses();
}, []);
useEffect(() => {
const fetchDataAndSetState = () => {
if (!useFakeData) {
fetchData(currentPage, itemsPerPage, searchTerm);
fetchDataSubscribed();
fetchDataArchived();
} else {
setTimeout(() => {
setFicheInscriptionsData(mockFicheInscription);
setFichesInscriptionsDataEnCours(mockFicheInscription);
setIsLoading(false);
}, 1000);
}
@ -183,13 +242,37 @@ export default function Page({ params: { locale } }) {
fetchData(newPage, itemsPerPage); // Appeler fetchData directement ici
};
const validateAndAssociate = (updatedData) => {
fetch(`${BK_GESTIONINSCRIPTION_FICHEINSCRIPTION_URL}/${eleve.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify(updatedData),
credentials: 'include'
})
.then(response => response.json())
.then(data => {
console.log('Succès :', data);
})
.catch(error => {
console.error('Erreur :', error);
});
}
const columns = [
{ name: t('studentName'), transform: (row) => row.eleve.nom },
{ name: t('studentFistName'), transform: (row) => row.eleve.prenom },
{ name: t('mainContactMail'), transform: (row) => row.eleve.responsables[0].mail },
{ name: t('phone'), transform: (row) => formatPhoneNumber(row.eleve.responsables[0].telephone) },
{ name: t('lastUpdateDate'), transform: (row) => swapFormatDate(row.dateMAJ, "DD-MM-YYYY hh:mm:ss", "DD/MM/YYYY hh:mm") },
{ name: t('registrationFileStatus'), transform: (row) => <StatusLabel etat={row.etat} onChange={(newStatus) => updateStatusAction(row.eleve.id, newStatus)} /> },
{ name: t('lastUpdateDate'), transform: (row) => row.dateMAJ_formattee},
{ name: t('registrationFileStatus'), transform: (row) => (
<div className="flex justify-center items-center h-full">
<StatusLabel etat={row.etat} onChange={(newStatus) => updateStatusAction(row.eleve.id, newStatus)} showDropdown={false} />
</div>
)
},
{ name: t('files'), transform: (row) => (
<ul>
{row.fichiers?.map((fichier, fileIndex) => (
@ -228,6 +311,22 @@ const columns = [
),
onClick: () => window.location.href = `${FR_ADMIN_STUDENT_EDIT_SUBSCRIBE}?idEleve=${row.eleve.id}&id=1`,
}] : []),
...(row.etat === 3 ? [{
label: (
<>
<CheckCircle size={16} className="mr-2" /> Valider
</>
),
onClick: () => openModalAssociationEleve(row.eleve),
}] : []),
...(row.etat === 5 ? [{
label: (
<>
<CheckCircle size={16} className="mr-2" /> Rattacher
</>
),
onClick: () => openModalAssociationEleve(row.eleve),
}] : []),
...(row.etat !== 6 ? [{
label: (
<>
@ -242,6 +341,53 @@ const columns = [
/>
) },
];
const columnsSubscribed = [
{ name: t('studentName'), transform: (row) => row.eleve.nom },
{ name: t('studentFistName'), transform: (row) => row.eleve.prenom },
{ name: t('lastUpdateDate'), transform: (row) => row.dateMAJ_formattee},
{ name: t('class'), transform: (row) => row.eleve.classeAssocieeName},
{ name: t('registrationFileStatus'), transform: (row) => (
<div className="flex justify-center items-center h-full">
<StatusLabel etat={row.etat} onChange={(newStatus) => updateStatusAction(row.eleve.id, newStatus)} showDropdown={false} />
</div>
)
},
{ name: t('files'), transform: (row) => (
<ul>
{row.fichiers?.map((fichier, fileIndex) => (
<li key={fileIndex} className="flex items-center gap-2">
<FileText size={16} />
<a href={fichier.url}>{fichier.nom}</a>
</li>
))}
</ul>
) },
{ name: 'Actions', transform: (row) => (
<DropdownMenu
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
items={[
{ label: (
<>
<CheckCircle size={16} className="mr-2" /> Rattacher
</>
),
onClick: () => openModalAssociationEleve(row.eleve)
},
{ label: (
<>
<Trash2 size={16} className="mr-2 text-red-700" /> Archiver
</>
),
onClick: () => archiveFicheInscription(row.eleve.id, row.eleve.nom, row.eleve.prenom),
}
]}
buttonClassName="text-gray-400 hover:text-gray-600"
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-10 flex flex-col items-center"
/>
) },
];
if (isLoading) {
@ -262,22 +408,22 @@ const columns = [
<div className='p-8'>
<div className="border-b border-gray-200 mb-6">
<div className="flex gap-8">
<Tab
text={<>
{t('allStudents')}
<span className="ml-2 text-sm text-gray-400">({totalStudents})</span>
</>}
active={activeTab === 'all'}
onClick={() => setActiveTab('all')}
/>
<Tab
text={<>
{t('pending')}
<span className="ml-2 text-sm text-gray-400">({12})</span>
<span className="ml-2 text-sm text-gray-400">({totalPending})</span>
</>}
active={activeTab === 'pending'}
onClick={() => setActiveTab('pending')}
/>
<Tab
text={<>
{t('subscribed')}
<span className="ml-2 text-sm text-gray-400">({totalSubscribed})</span>
</>}
active={activeTab === 'subscribed'}
onClick={() => setActiveTab('subscribed')}
/>
<Tab
text={<>
{t('archived')}
@ -309,10 +455,21 @@ const columns = [
</div>
</div>
<DjangoCSRFToken csrfToken={csrfToken} />
<Table
key={`${currentPage}-${searchTerm}`}
data={(activeTab === 'all' || activeTab === 'pending') ? ficheInscriptionsData : fichesInscriptionsDataArchivees}
columns={columns}
data={
activeTab === 'pending'
? fichesInscriptionsDataEnCours
: activeTab === 'subscribed'
? fichesInscriptionsDataInscrits
: fichesInscriptionsDataArchivees
}
columns={
activeTab === 'subscribed'
? columnsSubscribed
: columns
}
itemsPerPage={itemsPerPage}
currentPage={currentPage}
totalPages={totalPages}
@ -327,6 +484,20 @@ const columns = [
}}
onCancel={() => setPopup({ ...popup, visible: false })}
/>
{isOpenAffectationClasse && (
<Modal
isOpen={isOpenAffectationClasse}
setIsOpen={setIsOpenAffectationClasse}
title="Affectation à une classe"
ContentComponent={() => (
<AffectationClasseForm
eleve={eleve}
onSubmit={validateAndAssociate}
classes={classes}
/>
)}
/>
)}
</div>
);
}