feat: Ajout de la gestion des fichier d'inscription [#1]

This commit is contained in:
Luc SORIGNET
2025-01-11 16:14:03 +01:00
parent fb5d485ce1
commit 3c27133cdb
16 changed files with 469 additions and 143 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@ __pycache__/
node_modules/
Back-End/*/migrations/*
Back-End/documents
Back-End/data
Back-End/*.dmp
Back-End/staticfiles
hardcoded-strings-report.md

View File

@ -170,3 +170,10 @@ class FicheInscription(models.Model):
def __str__(self):
return "FI_" + self.eleve.nom + "_" + self.eleve.prenom
class FichierInscription(models.Model):
name = models.CharField(max_length=255)
file = models.FileField(upload_to='fichiers_inscription/')
date_ajout = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.nom

View File

@ -1,5 +1,5 @@
from rest_framework import serializers
from GestionInscriptions.models import FicheInscription, Eleve, Responsable, Frere, Langue, FraisInscription
from GestionInscriptions.models import FichierInscription, FicheInscription, Eleve, Responsable, Frere, Langue, FraisInscription
from GestionEnseignants.models import Classe
from GestionLogin.models import Profil
from GestionLogin.serializers import ProfilSerializer
@ -10,6 +10,10 @@ from django.utils import timezone
import pytz
from datetime import datetime
class FichierInscriptionSerializer(serializers.ModelSerializer):
class Meta:
model = FichierInscription
fields = '__all__'
class FraisInscriptionSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
@ -196,4 +200,3 @@ class NotificationSerializer(serializers.ModelSerializer):
class Meta:
model = Notification
fields = '__all__'

View File

@ -1,7 +1,7 @@
from django.urls import path, re_path
from . import views
from GestionInscriptions.views import ListFichesInscriptionView, FicheInscriptionView, EleveView, ResponsableView, ListeEnfantsView, ListeElevesView, FraisInscriptionView
from GestionInscriptions.views import FichierInscriptionView, ListFichesInscriptionView, FicheInscriptionView, EleveView, ResponsableView, ListeEnfantsView, ListeElevesView, FraisInscriptionView
urlpatterns = [
re_path(r'^fichesInscription/([a-zA-z]+)$', ListFichesInscriptionView.as_view(), name="listefichesInscriptions"),
@ -31,4 +31,6 @@ urlpatterns = [
# Frais d'inscription
re_path(r'^tarifsInscription$', FraisInscriptionView.as_view(), name="fraisInscription"),
re_path(r'^fichiersInscription$', FichierInscriptionView.as_view(), name='fichiersInscription'),
re_path(r'^fichiersInscription/([0-9]+)$', FichierInscriptionView.as_view(), name="fichiersInscription"),
]

View File

@ -6,7 +6,8 @@ from django.core.cache import cache
from django.core.paginator import Paginator
from django.core.files import File
from django.db.models import Q # Ajout de cet import
from rest_framework.parsers import JSONParser
from rest_framework.parsers import JSONParser,MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
@ -17,10 +18,11 @@ from io import BytesIO
import GestionInscriptions.mailManager as mailer
import GestionInscriptions.util as util
from GestionInscriptions.serializers import FicheInscriptionSerializer, EleveSerializer, FicheInscriptionByParentSerializer, EleveByDICreationSerializer, FraisInscriptionSerializer
from GestionInscriptions.serializers import FichierInscriptionSerializer, FicheInscriptionSerializer, EleveSerializer, FicheInscriptionByParentSerializer, EleveByDICreationSerializer, FraisInscriptionSerializer
from GestionInscriptions.pagination import CustomPagination
from GestionInscriptions.signals import clear_cache
from .models import Eleve, Responsable, FicheInscription, FraisInscription
from .models import Eleve, Responsable, FicheInscription, FraisInscription, FichierInscription
from GestionInscriptions.automate import Automate_DI_Inscription, load_config, getStateMachineObjectState, updateStateMachine
from GestionLogin.models import Profil
@ -254,3 +256,27 @@ class FraisInscriptionView(APIView):
tarifs = bdd.getAllObjects(FraisInscription)
tarifs_serializer = FraisInscriptionSerializer(tarifs, many=True)
return JsonResponse(tarifs_serializer.data, safe=False)
class FichierInscriptionView(APIView):
parser_classes = (MultiPartParser, FormParser)
def get(self, request):
fichiers = FichierInscription.objects.all()
serializer = FichierInscriptionSerializer(fichiers, many=True)
return Response(serializer.data)
def post(self, request):
serializer = FichierInscriptionSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, _id):
fichierInscription = bdd.getObject(_objectName=FichierInscription, _columnName='id', _value=_id)
if fichierInscription is not None:
fichierInscription.file.delete() # Supprimer le fichier uploadé
fichierInscription.delete()
return JsonResponse({'message': 'La suppression du fichier d\'inscription a été effectuée avec succès'}, safe=False)
else:
return JsonResponse({'erreur': 'Le fichier d\'inscription n\'a pas été trouvé'}, safe=False)

View File

@ -16,6 +16,8 @@ import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_URL = '/data/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'data')
LOGIN_REDIRECT_URL = '/GestionInscriptions/fichesInscriptions'

View File

@ -16,6 +16,8 @@ Including another URLconf
"""
from django.contrib import admin
from django.urls import include, path, re_path
from django.conf import settings
from django.conf.urls.static import static
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
@ -47,3 +49,6 @@ urlpatterns = [
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -28,5 +28,6 @@
"lastUpdateDate":"Last update",
"classe":"Class",
"registrationFileStatus":"Registration file status",
"files":"Files"
"files":"Files",
"subscribeFiles":"Subscribe files"
}

View File

@ -28,5 +28,6 @@
"lastUpdateDate":"Dernière mise à jour",
"classe":"Classe",
"registrationFileStatus":"État du dossier d'inscription",
"files":"Fichiers"
"files":"Fichiers",
"subscribeFiles":"Fichiers d'inscription"
}

View File

@ -63,7 +63,7 @@ export default function Layout({
<Sidebar currentPage={currentPage} items={Object.values(sidebarItems)} className="h-full" />
<div className="flex flex-col flex-1">
{/* Header - h-16 = 64px */}
<header className="h-16 bg-white border-b border-gray-200 px-8 py-4 flex items-center justify-between z-10">
<header className="h-16 bg-white border-b border-gray-200 px-8 py-4 flex items-center justify-between z-9">
<div className="text-xl font-semibold">{headerTitle}</div>
<DropdownMenu
buttonContent={<img src="https://i.pravatar.cc/32" alt="Profile" className="w-8 h-8 rounded-full cursor-pointer" />}

View File

@ -0,0 +1,77 @@
import React, { useState } from 'react';
import { Upload } from 'lucide-react';
export default function FileUpload({ onFileUpload }) {
const [dragActive, setDragActive] = useState(false);
const [fileName, setFileName] = useState('');
const [file, setFile] = useState(null);
const handleDragOver = (event) => {
event.preventDefault();
setDragActive(true);
};
const handleDragLeave = () => {
setDragActive(false);
};
const handleDrop = (event) => {
event.preventDefault();
setDragActive(false);
const droppedFile = event.dataTransfer.files[0];
setFile(droppedFile);
setFileName(droppedFile.name.replace(/\.[^/.]+$/, ""));
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
setFileName(selectedFile.name.replace(/\.[^/.]+$/, ""));
};
const handleFileNameChange = (event) => {
setFileName(event.target.value);
};
const handleUpload = () => {
if (file) {
onFileUpload(file, fileName);
setFile(null);
setFileName('');
}
};
return (
<div>
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={`border-2 border-dashed p-8 rounded-md ${dragActive ? 'border-blue-500' : 'border-gray-300'} flex flex-col items-center justify-center`}
style={{ height: '200px' }}
>
<input type="file" onChange={handleFileChange} className="hidden" id="fileInput" />
<label htmlFor="fileInput" className="cursor-pointer flex flex-col items-center">
<Upload size={48} className="text-gray-400 mb-2" />
<p className="text-center">{fileName || 'Glissez et déposez un fichier ici ou cliquez ici pour sélectionner un fichier'}</p>
</label>
</div>
<div className="flex mt-2">
<input
type="text"
placeholder="Nom du fichier"
value={fileName}
onChange={handleFileNameChange}
className="flex-grow p-2 border border-gray-200 rounded-md"
/>
<button
onClick={handleUpload}
className={`p-2 rounded-md shadow transition duration-200 ml-2 ${file ? 'bg-emerald-600 text-white hover:bg-emerald-900' : 'bg-gray-300 text-gray-500 cursor-not-allowed'}`}
disabled={!file}
>
Ajouter
</button>
</div>
</div>
);
}

View File

@ -13,17 +13,19 @@ 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, CheckCircle, Plus} from 'lucide-react';
import { MoreVertical, Send, Edit, Trash2, FileText, ChevronUp, UserPlus, CheckCircle, Plus, Download } from 'lucide-react';
import Modal from '@/components/Modal';
import InscriptionForm from '@/components/Inscription/InscriptionForm'
import AffectationClasseForm from '@/components/AffectationClasseForm'
import FileUpload from './components/FileUpload';
import { BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL,
import { BASE_URL, BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL,
BK_GESTIONINSCRIPTION_SEND_URL,
FR_ADMIN_SUBSCRIPTIONS_EDIT_URL,
BK_GESTIONINSCRIPTION_ARCHIVE_URL,
BK_GESTIONENSEIGNANTS_CLASSES_URL,
BK_GESTIONINSCRIPTION_FICHEINSCRIPTION_URL,
BK_GESTIONINSCRIPTION_FICHERSINSCRIPTION_URL ,
BK_GESTIONINSCRIPTION_ELEVES_URL,
BK_PROFILE_URL } from '@/utils/Url';
@ -53,6 +55,10 @@ export default function Page({ params: { locale } }) {
const [totalArchives, setTotalArchives] = useState(0);
const [itemsPerPage, setItemsPerPage] = useState(5); // Définir le nombre d'éléments par page
const [fichiers, setFichiers] = useState([]);
const [nomFichier, setNomFichier] = useState('');
const [fichier, setFichier] = useState(null);
const [isOpen, setIsOpen] = useState(false);
const [isOpenAffectationClasse, setIsOpenAffectationClasse] = useState(false);
const [eleve, setEleve] = useState('');
@ -180,6 +186,32 @@ export default function Page({ params: { locale } }) {
});
};
useEffect(() => {
const fetchFichiers = () => {
const request = new Request(
`${BK_GESTIONINSCRIPTION_FICHERSINSCRIPTION_URL}`,
{
method:'GET',
headers: {
'Content-Type':'application/json'
},
}
);
fetch(request).then(response => response.json())
.then(data => {
console.log('Success FILES:', data);
setFichiers(data);
})
.catch(error => {
console.error('Error fetching data:', error);
error = error.message;
console.log(error);
});
};
fetchFichiers();
}, []);
useEffect(() => {
fetchClasses();
fetchStudents();
@ -429,13 +461,11 @@ const columns = [
)
},
{ name: t('files'), transform: (row) => (
<ul>
{row.fichiers?.map((fichier, fileIndex) => (
<li key={fileIndex} className="flex items-center gap-2">
<ul>
<li className="flex items-center gap-2">
<FileText size={16} />
<a href={fichier.url}>{fichier.nom}</a>
<a href={ `${BASE_URL}${row.fichierInscription}`} target='_blank'>{row.fichierInscription.split('/').pop()}</a>
</li>
))}
</ul>
) },
{ name: 'Actions', transform: (row) => (
@ -509,14 +539,13 @@ const columnsSubscribed = [
</div>
)
},
{ name: t('files'), transform: (row) => (
{ name: t('files'), transform: (row) =>
(
<ul>
{row.fichiers?.map((fichier, fileIndex) => (
<li key={fileIndex} className="flex items-center gap-2">
<li className="flex items-center gap-2">
<FileText size={16} />
<a href={fichier.url}>{fichier.nom}</a>
<a href={ `${BASE_URL}${row.fichierInscription}`} target='_blank'>{row.fichierInscription.split('/').pop()}</a>
</li>
))}
</ul>
) },
{ name: 'Actions', transform: (row) => (
@ -545,6 +574,98 @@ const columnsSubscribed = [
];
const handleFileDelete = (fileId) => {
fetch(`${BK_GESTIONINSCRIPTION_FICHERSINSCRIPTION_URL}/${fileId}`, {
method: 'DELETE',
headers: {
'X-CSRFToken': csrfToken,
},
credentials: 'include',
})
.then(response => {
if (response.ok) {
setFichiers(fichiers.filter(fichier => fichier.id !== fileId));
alert('Fichier supprimé avec succès.');
} else {
alert('Erreur lors de la suppression du fichier.');
}
})
.catch(error => {
console.error('Error deleting file:', error);
alert('Erreur lors de la suppression du fichier.');
});
};
const columnsFiles = [
{ name: 'Nom du fichier', transform: (row) => row.name },
{ name: 'Date de création', transform: (row) => row.date_ajout },
{ name: 'Actions', transform: (row) => (
<div className="flex items-center justify-center gap-2">
<a href={`${BASE_URL}${row.file}`} target='_blank' className="text-blue-500 hover:text-blue-700">
<Download size={16} />
</a>
<button onClick={() => handleFileDelete(row.id)} className="text-red-500 hover:text-red-700">
<Trash2 size={16} />
</button>
</div>
) },
];
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
const [uploadFile, setUploadFile] = useState(null);
const [uploadFileName, setUploadFileName] = useState('');
const [fileName, setFileName] = useState('');
const openUploadModal = () => {
setIsUploadModalOpen(true);
};
const closeUploadModal = () => {
setIsUploadModalOpen(false);
setUploadFile(null);
setUploadFileName('');
setFileName('');
};
const handleFileChange = (event) => {
const file = event.target.files[0];
setUploadFile(file);
setUploadFileName(file ? file.name : '');
};
const handleFileNameChange = (event) => {
setFileName(event.target.value);
};
const handleFileUpload = (file, fileName) => {
if (!file || !fileName) {
alert('Veuillez sélectionner un fichier et entrer un nom de fichier.');
return;
}
const formData = new FormData();
formData.append('file', file);
formData.append('name', fileName);
fetch(`${BK_GESTIONINSCRIPTION_FICHERSINSCRIPTION_URL}`, {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': csrfToken,
},
credentials: 'include',
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
setFichiers([...fichiers, data]);
closeUploadModal();
})
.catch(error => {
console.error('Error uploading file:', error);
});
};
if (isLoading) {
return <Loader />;
} else {
@ -593,16 +714,23 @@ const columnsSubscribed = [
active={activeTab === 'archived'}
onClick={() => setActiveTab('archived')}
/>
<Tab
text={(
<>
{t('subscribeFiles')}
<span className="ml-2 text-sm text-gray-400">({totalSubscribed})</span>
</>
)}
active={activeTab === 'subscribeFiles'}
onClick={() => setActiveTab('subscribeFiles')}
/>
</div>
</div>
<div className="border-b border-gray-200 mb-6 w-full">
{/*SI STATE == pending || subscribe || archived */}
{activeTab === 'pending' || activeTab === 'subscribed' || activeTab === 'archived' ? (
<React.Fragment>
<div className="flex justify-between items-center mb-4 w-full">
<button
onClick={openModal}
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200 mr-4"
>
<Plus className="w-5 h-5" />
</button>
<div className="relative flex-grow">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" size={20} />
<input
@ -613,7 +741,14 @@ const columnsSubscribed = [
onChange={handleSearchChange}
/>
</div>
<button
onClick={openModal}
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200 ml-4"
>
<Plus className="w-5 h-5" />
</button>
</div>
<div className="w-full">
<DjangoCSRFToken csrfToken={csrfToken} />
<Table
@ -636,6 +771,25 @@ const columnsSubscribed = [
onPageChange={handlePageChange}
/>
</div>
</React.Fragment>
) : null}
{/*SI STATE == subscribeFiles */}
{activeTab === 'subscribeFiles' && (
<div>
<FileUpload onFileUpload={handleFileUpload} className="mb-4" />
<div className="mt-8">
<Table
data={fichiers}
columns={columnsFiles}
itemsPerPage={itemsPerPage}
currentPage={currentPage}
totalPages={totalPages}
onPageChange={handlePageChange}
className="mt-8"
/>
</div>
</div>
)}
</div>
<Popup
visible={popup.visible}
@ -654,10 +808,10 @@ const columnsSubscribed = [
title={"Création d'un nouveau dossier d'inscription"}
size='sm:w-1/4'
ContentComponent={() => (
<InscriptionForm eleves={eleves}
onSubmit={createDI}
/>
)}
<InscriptionForm eleves={eleves}
onSubmit={createDI}
/>
)}
/>
)}
{isOpenAffectationClasse && (
@ -674,6 +828,17 @@ const columnsSubscribed = [
)}
/>
)}
{isUploadModalOpen && (
<Modal
isOpen={isUploadModalOpen}
setIsOpen={setIsUploadModalOpen}
title="Uploader un nouveau fichier"
size='sm:w-1/4'
ContentComponent={() => (
<FileUpload onFileUpload={handleFileUpload} />
)}
/>
)}
</div>
);
}

View File

@ -70,7 +70,7 @@ const InscriptionForm = ( { eleves, onSubmit }) => {
<div className="space-y-4 mt-8">
{step === 1 && (
<div>
<h2 className="text-2xl font-bold mb-4">Nouvel élève</h2>
<h2 className="text-l font-bold mb-4">Nouvel élève</h2>
<InputTextIcon
name="eleveNom"
type="text"
@ -94,7 +94,7 @@ const InscriptionForm = ( { eleves, onSubmit }) => {
{step === 2 && (
<div className="mt-6">
<h2 className="text-2xl font-bold mb-4">Responsable(s)</h2>
<h2 className="text-l font-bold mb-4">Responsable(s)</h2>
<div className="flex flex-col space-y-4">
<label className="flex items-center space-x-3">
<input
@ -182,7 +182,7 @@ const InscriptionForm = ( { eleves, onSubmit }) => {
{step === 3 && (
<div className="mt-6">
<h2 className="text-2xl font-bold mb-4">Téléphone (optionnel)</h2>
<h2 className="text-l font-bold mb-4">Téléphone (optionnel)</h2>
<InputTextIcon
name="responsableTel"
type="tel"
@ -197,41 +197,67 @@ const InscriptionForm = ( { eleves, onSubmit }) => {
{step === maxStep && (
<div>
<h2 className="text-2xl font-bold mb-4">Récapitulatif</h2>
<div className="flex items-center justify-between space-x-4">
<div className="bg-gray-100 p-3 rounded-lg shadow-md flex items-center w-1/2 mb-4">
<User className="w-10 h-10 text-gray-300 mr-4" />
<div>
<p className="italic text-gray-600">Élève</p>
<p>Nom : {formData.eleveNom}</p>
<p>Prénom : {formData.elevePrenom}</p>
</div>
</div>
</div>
<div className="flex items-center justify-between space-x-4">
<div className="bg-gray-100 p-3 rounded-lg shadow-md flex items-center w-1/2">
<UserCheck className="w-10 h-10 text-gray-300 mr-4" />
<h2 className="text-l font-bold mb-4">Récapitulatif</h2>
<div className="space-y-4">
<section>
<h3 className="font-bold">Élève</h3>
<table className="min-w-full bg-white border">
<thead>
<tr>
<th className="px-4 py-2 border">Nom</th>
<th className="px-4 py-2 border">Prénom</th>
</tr>
</thead>
<tbody>
<tr>
<td className="px-4 py-2 border">{formData.eleveNom}</td>
<td className="px-4 py-2 border">{formData.elevePrenom}</td>
</tr>
</tbody>
</table>
</section>
<section>
<h3 className="font-bold">Responsable(s)</h3>
{formData.responsableType === 'new' && (
<div>
<p className="italic text-gray-600">Responsable</p>
<p>Email : {formData.responsableEmail}</p>
<p>Téléphone : {formData.responsableTel}</p>
</div>
<table className="min-w-full bg-white border">
<thead>
<tr>
<th className="px-4 py-2 border">Email</th>
<th className="px-4 py-2 border">Téléphone</th>
</tr>
</thead>
<tbody>
<tr>
<td className="px-4 py-2 border">{formData.responsableEmail}</td>
<td className="px-4 py-2 border">{formData.responsableTel}</td>
</tr>
</tbody>
</table>
)}
{formData.responsableType === 'existing' && selectedEleve && (
<div>
<p className="italic text-gray-600">Responsable(s)</p>
<p>Associé(s) à : {selectedEleve.nom} {selectedEleve.prenom}</p>
<ul className="list-disc ml-6">
{existingResponsables.filter(responsable => formData.selectedResponsables.includes(responsable.id)).map((responsable) => (
<li key={responsable.id}>
{responsable.nom && responsable.prenom ? `${responsable.nom} ${responsable.prenom}` : responsable.mail}
</li>
))}
</ul>
<table className="min-w-full bg-white border">
<thead>
<tr>
<th className="px-4 py-2 border">Nom</th>
<th className="px-4 py-2 border">Prénom</th>
<th className="px-4 py-2 border">Email</th>
</tr>
</thead>
<tbody>
{existingResponsables.filter(responsable => formData.selectedResponsables.includes(responsable.id)).map((responsable) => (
<tr key={responsable.id}>
<td className="px-4 py-2 border">{responsable.nom}</td>
<td className="px-4 py-2 border">{responsable.prenom}</td>
<td className="px-4 py-2 border">{responsable.mail}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</section>
</div>
<div className='mt-4'>
<ToggleSwitch

View File

@ -7,23 +7,26 @@ const Modal = ({ isOpen, setIsOpen, title, ContentComponent, size }) => {
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
<Dialog.Content className="fixed inset-0 flex items-center justify-center">
<div className={`inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:p-6 ${size ? size : 'sm:w-full' }`}>
<Dialog.Title className="text-lg font-medium text-gray-900">
{title}
</Dialog.Title>
<div className={`inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:p-6 ${size ? size : 'sm:w-full' }`} style={{ minWidth: '300px', width: 'auto' }}>
<div className="flex justify-between items-start">
<Dialog.Title className="text-xl font-medium text-gray-900">
{title}
</Dialog.Title>
<Dialog.Close asChild>
<button
onClick={() => setIsOpen(false)}
className="text-gray-400 hover:text-gray-500 ml-4 focus:outline-none"
>
<span className="sr-only">Fermer</span>
<svg className="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</Dialog.Close>
</div>
<div className="mt-2">
<ContentComponent />
</div>
<div className="mt-4 flex justify-end space-x-4">
<Dialog.Close asChild>
<Button text="Fermer"
onClick={() => setIsOpen(false)}
className="px-4 py-2 rounded-md shadow-sm focus:outline-none bg-gray-300 text-gray-700 hover:bg-gray-400"
secondary
type="submit"
name="Create" />
</Dialog.Close>
</div>
</div>
</Dialog.Content>
</Dialog.Portal>

View File

@ -4,12 +4,17 @@ export const BASE_URL = process.env.NEXT_PUBLIC_API_URL;
//URL-Back-End
// GESTION LOGIN
export const BK_NEW_PASSWORD_URL = `${BASE_URL}/GestionLogin/newPassword`
export const BK_REGISTER_URL = `${BASE_URL}/GestionLogin/subscribe`
export const BK_RESET_PASSWORD_URL = `${BASE_URL}/GestionLogin/resetPassword`
export const BK_LOGIN_URL = `${BASE_URL}/GestionLogin/login`
export const BK_LOGOUT_URL = `${BASE_URL}/GestionLogin/logout`
export const BK_PROFILE_URL = `${BASE_URL}/GestionLogin/profil`
export const BK_GET_CSRF = `${BASE_URL}/GestionLogin/csrf`
// GESTION INSCRIPTION
export const BK_GESTIONINSCRIPTION_ELEVE_URL = `${BASE_URL}/GestionInscriptions/eleve`
export const BK_GESTIONINSCRIPTION_ENFANTS_URL = `${BASE_URL}/GestionInscriptions/enfants` // Récupère la liste des élèves d'un profil
export const BK_GESTIONINSCRIPTION_ELEVES_URL = `${BASE_URL}/GestionInscriptions/eleves` // Récupère la liste des élèves inscrits ou en cours d'inscriptions
@ -17,8 +22,11 @@ export const BK_GESTIONINSCRIPTION_SEND_URL = `${BASE_URL}/GestionInscriptions/s
export const BK_GESTIONINSCRIPTION_ARCHIVE_URL = `${BASE_URL}/GestionInscriptions/archive`
export const BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL = `${BASE_URL}/GestionInscriptions/fichesInscription`
export const BK_GESTIONINSCRIPTION_FICHEINSCRIPTION_URL = `${BASE_URL}/GestionInscriptions/ficheInscription`
export const BK_GESTIONINSCRIPTION_FICHERSINSCRIPTION_URL = `${BASE_URL}/GestionInscriptions/fichiersInscription`
export const BK_GESTIONINSCRIPTION_RECUPEREDERNIER_RESPONSABLE_URL = `${BASE_URL}/GestionInscriptions/recupereDernierResponsable`
export const BK_GESTIONINSCRIPTION_MESSAGES_URL = `${BASE_URL}/GestionMessagerie/messagerie`
//GESTION ENSEIGNANT
export const BK_GESTIONENSEIGNANTS_SPECIALITES_URL = `${BASE_URL}/GestionEnseignants/specialites`
export const BK_GESTIONENSEIGNANTS_SPECIALITE_URL = `${BASE_URL}//GestionEnseignants/specialite`
export const BK_GESTIONENSEIGNANTS_CLASSES_URL = `${BASE_URL}/GestionEnseignants/classes`
@ -28,7 +36,6 @@ export const BK_GESTIONENSEIGNANTS_TEACHER_URL = `${BASE_URL}/GestionEnseignants
export const BK_GESTIONENSEIGNANTS_PLANNINGS_URL = `${BASE_URL}/GestionEnseignants/plannings`
export const BK_GESTIONENSEIGNANTS_PLANNING_URL = `${BASE_URL}/GestionEnseignants/planning`
export const BK_GET_CSRF = `${BASE_URL}/GestionLogin/csrf`
// URL FRONT-END