mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Signatures électroniques docuseal [#22]
This commit is contained in:
200
Front-End/src/components/Structure/Files/FileUpload.js
Normal file
200
Front-End/src/components/Structure/Files/FileUpload.js
Normal file
@ -0,0 +1,200 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ToggleSwitch from '@/components/ToggleSwitch'; // Import du composant ToggleSwitch
|
||||
import { fetchRegistrationFileGroups } from '@/app/actions/registerFileGroupAction';
|
||||
import DocusealBuilder from '@/components/DocusealBuilder'; // Import du composant wrapper
|
||||
import logger from '@/utils/logger';
|
||||
import { BE_DOCUSEAL_GET_JWT, BASE_URL } from '@/utils/Url';
|
||||
import Button from '@/components/Button'; // Import du composant Button
|
||||
import MultiSelect from '@/components/MultiSelect'; // Import du composant MultiSelect
|
||||
import { createRegistrationTemplates } from '@/app/actions/subscriptionAction'; // Import de la fonction createRegistrationTemplates
|
||||
import { useCsrfToken } from '@/context/CsrfContext';
|
||||
|
||||
export default function FileUpload({ handleCreateTemplateMaster, fileToEdit = null }) {
|
||||
const [isRequired, setIsRequired] = useState(false); // État pour le toggle isRequired
|
||||
const [order, setOrder] = useState(0);
|
||||
const [groups, setGroups] = useState([]);
|
||||
const [token, setToken] = useState(null);
|
||||
const [templateMaster, setTemplateMaster] = useState(null);
|
||||
const [uploadedFileName, setUploadedFileName] = useState('');
|
||||
const [selectedGroups, setSelectedGroups] = useState([]);
|
||||
const [guardianEmails, setGuardianEmails] = useState([]);
|
||||
const [registrationFormIds, setRegistrationFormIds] = useState([]);
|
||||
|
||||
const csrfToken = useCsrfToken();
|
||||
|
||||
useEffect(() => {
|
||||
fetchRegistrationFileGroups().then(data => setGroups(data));
|
||||
|
||||
if (fileToEdit) {
|
||||
setUploadedFileName(fileToEdit.name || '');
|
||||
setSelectedGroups(fileToEdit.groups || []);
|
||||
}
|
||||
}, [fileToEdit]);
|
||||
|
||||
useEffect(() => {
|
||||
const body = fileToEdit
|
||||
? JSON.stringify({
|
||||
user_email: 'n3wt.school@gmail.com',
|
||||
template_id: fileToEdit.template_id
|
||||
})
|
||||
: JSON.stringify({
|
||||
user_email: 'n3wt.school@gmail.com'
|
||||
});
|
||||
|
||||
fetch('/api/docuseal/generateToken', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: body,
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
setToken(data.token);
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
}, [fileToEdit]);
|
||||
|
||||
const handleFileNameChange = (event) => {
|
||||
setUploadedFileName(event.target.value);
|
||||
};
|
||||
|
||||
const handleGroupChange = (selectedGroups) => {
|
||||
setSelectedGroups(selectedGroups);
|
||||
|
||||
const emails = selectedGroups.flatMap(group => group.registration_forms.flatMap(form => form.guardians.map(guardian => guardian.email)));
|
||||
setGuardianEmails(emails); // Mettre à jour la variable d'état avec les emails des guardians
|
||||
|
||||
const registrationFormIds = selectedGroups.flatMap(group => group.registration_forms.map(form => form.student_id));
|
||||
setRegistrationFormIds(registrationFormIds); // Mettre à jour la variable d'état avec les IDs des dossiers d'inscription
|
||||
|
||||
logger.debug('Emails des Guardians associés aux groupes sélectionnés:', emails);
|
||||
logger.debug('IDs des dossiers d\'inscription associés aux groupes sélectionnés:', registrationFormIds);
|
||||
};
|
||||
|
||||
const handleLoad = (detail) => {
|
||||
const templateId = detail?.id;
|
||||
setTemplateMaster(detail);
|
||||
logger.debug('Master template created with ID:', templateId);
|
||||
}
|
||||
|
||||
const handleUpload = (detail) => {
|
||||
logger.debug('Uploaded file detail:', detail);
|
||||
setUploadedFileName(detail.name);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
logger.debug('Création du template master:', templateMaster?.id);
|
||||
handleCreateTemplateMaster({
|
||||
name: uploadedFileName,
|
||||
group_ids: selectedGroups.map(group => group.id),
|
||||
template_id: templateMaster?.id
|
||||
});
|
||||
|
||||
guardianEmails.forEach((email, index) => {
|
||||
cloneTemplate(templateMaster?.id, email)
|
||||
.then(clonedDocument => {
|
||||
|
||||
// Sauvegarde des templates clonés dans la base de données
|
||||
const data = {
|
||||
name: `clone_${clonedDocument.id}`,
|
||||
template_id: clonedDocument.id,
|
||||
master: templateMaster?.id,
|
||||
registration_form: registrationFormIds[index]
|
||||
};
|
||||
|
||||
createRegistrationTemplates(data, csrfToken)
|
||||
.then(response => {
|
||||
logger.debug('Template enregistré avec succès:', response);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Erreur lors de l\'enregistrement du template:', error);
|
||||
});
|
||||
|
||||
|
||||
// Logique pour envoyer chaque template au submitter
|
||||
logger.debug('Sending template to:', email);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error during cloning or sending:', error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const cloneTemplate = (templateId, email) => {
|
||||
return fetch('/api/docuseal/cloneTemplate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
templateId,
|
||||
email
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.json().then(err => { throw new Error(err.message); });
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
logger.debug('Template cloned successfully:', data);
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error cloning template:', error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col mt-4 space-y-4">
|
||||
<div className="grid grid-cols-10 gap-4 items-start">
|
||||
<div className="col-span-2">
|
||||
<MultiSelect
|
||||
name="groups"
|
||||
label="Sélection de groupes de fichiers"
|
||||
options={groups}
|
||||
selectedOptions={selectedGroups}
|
||||
onChange={handleGroupChange}
|
||||
errorMsg={null}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-7">
|
||||
{token && (
|
||||
<DocusealBuilder
|
||||
token={token}
|
||||
headers={{
|
||||
'Authorization': `Bearer ${token}`
|
||||
}}
|
||||
withSendButton={false}
|
||||
withSignYourselfButton={false}
|
||||
autosave={true}
|
||||
language={'fr'}
|
||||
onLoad={handleLoad}
|
||||
onUpload={handleUpload}
|
||||
className="h-full overflow-auto" // Ajouter overflow-auto pour permettre le défilement
|
||||
style={{ maxHeight: '70vh' }} // Limiter la hauteur maximale du composant
|
||||
// Il faut auter l'host correspondant (une fois passé en HTTPS)
|
||||
//host="docuseal:3001"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-span-1 flex justify-end">
|
||||
<Button
|
||||
text="Valider"
|
||||
onClick={handleSubmit}
|
||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||
(uploadedFileName === '' || selectedGroups.length === 0)
|
||||
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
||||
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
||||
}`}
|
||||
primary
|
||||
disabled={uploadedFileName === '' || selectedGroups.length === 0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -2,14 +2,15 @@ import React, { useState, useEffect } from 'react';
|
||||
import { Plus, Download, Edit, Trash2, FolderPlus, Signature } from 'lucide-react';
|
||||
import Modal from '@/components/Modal';
|
||||
import Table from '@/components/Table';
|
||||
import FileUpload from '@/components/FileUpload';
|
||||
import FileUpload from '@/components/Structure/Files/FileUpload';
|
||||
import { formatDate } from '@/utils/Date';
|
||||
import { BASE_URL } from '@/utils/Url';
|
||||
import {
|
||||
fetchRegisterFormFileTemplate,
|
||||
createRegistrationFormFileTemplate,
|
||||
editRegistrationFormFileTemplate,
|
||||
deleteRegisterFormFileTemplate
|
||||
fetchRegistrationTemplateMaster,
|
||||
createRegistrationTemplateMaster,
|
||||
editRegistrationTemplateMaster,
|
||||
deleteRegistrationTemplateMaster,
|
||||
getRegisterFormFileTemplate
|
||||
} from '@/app/actions/subscriptionAction';
|
||||
import {
|
||||
fetchRegistrationFileGroups,
|
||||
@ -17,10 +18,9 @@ import {
|
||||
deleteRegistrationFileGroup,
|
||||
editRegistrationFileGroup
|
||||
} from '@/app/actions/registerFileGroupAction';
|
||||
import RegistrationFileGroupForm from '@/components/RegistrationFileGroupForm';
|
||||
import logger from '@/utils/logger';
|
||||
import RegistrationFileGroupForm from '@/components/Structure/Files/RegistrationFileGroupForm';
|
||||
|
||||
export default function FilesManagement({ csrfToken }) {
|
||||
export default function FilesGroupsManagement({ csrfToken }) {
|
||||
const [fichiers, setFichiers] = useState([]);
|
||||
const [groups, setGroups] = useState([]);
|
||||
const [selectedGroup, setSelectedGroup] = useState(null);
|
||||
@ -29,23 +29,18 @@ export default function FilesManagement({ csrfToken }) {
|
||||
const [fileToEdit, setFileToEdit] = useState(null);
|
||||
const [isGroupModalOpen, setIsGroupModalOpen] = useState(false);
|
||||
const [groupToEdit, setGroupToEdit] = useState(null);
|
||||
const [token, setToken] = useState(null);
|
||||
const [selectedFile, setSelectedFile] = useState(null);
|
||||
|
||||
// Fonction pour transformer les données des fichiers avec les informations complètes du groupe
|
||||
const transformFileData = (file, groups) => {
|
||||
if (!file.group) return file;
|
||||
|
||||
const groupInfo = groups.find(g => g.id === file.group);
|
||||
const groupInfos = file.groups.map(groupId => groups.find(g => g.id === groupId) || { id: groupId, name: 'Groupe inconnu' });
|
||||
return {
|
||||
...file,
|
||||
group: groupInfo || { id: file.group, name: 'Groupe inconnu' }
|
||||
groups: groupInfos
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
fetchRegisterFormFileTemplate(),
|
||||
fetchRegistrationTemplateMaster(),
|
||||
fetchRegistrationFileGroups()
|
||||
]).then(([filesData, groupsData]) => {
|
||||
setGroups(groupsData);
|
||||
@ -57,12 +52,12 @@ export default function FilesManagement({ csrfToken }) {
|
||||
const transformedFiles = filesData.map(file => transformFileData(file, groupsData));
|
||||
setFichiers(transformedFiles);
|
||||
}).catch(err => {
|
||||
logger.debug(err.message);
|
||||
console.log(err.message);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleFileDelete = (fileId) => {
|
||||
deleteRegisterFormFileTemplate(fileId, csrfToken)
|
||||
deleteRegistrationTemplateMaster(fileId, csrfToken)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
setFichiers(fichiers.filter(fichier => fichier.id !== fileId));
|
||||
@ -72,7 +67,7 @@ export default function FilesManagement({ csrfToken }) {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error deleting file:', error);
|
||||
console.error('Error deleting file:', error);
|
||||
alert('Erreur lors de la suppression du fichier.');
|
||||
});
|
||||
};
|
||||
@ -83,7 +78,45 @@ export default function FilesManagement({ csrfToken }) {
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const handleFileUpload = ({file, name, is_required, order, groupId}) => {
|
||||
const handleCreateTemplateMaster = ({name, group_ids, template_id}) => {
|
||||
const data = {
|
||||
name: name,
|
||||
template_id: template_id,
|
||||
groups: group_ids
|
||||
};
|
||||
console.log(data);
|
||||
|
||||
if (isEditing && fileToEdit) {
|
||||
editRegistrationTemplateMaster(fileToEdit.id, data, csrfToken)
|
||||
.then(data => {
|
||||
// Transformer le fichier mis à jour avec les informations du groupe
|
||||
const transformedFile = transformFileData(data, groups);
|
||||
setFichiers(prevFichiers =>
|
||||
prevFichiers.map(f => f.id === fileToEdit.id ? transformedFile : f)
|
||||
);
|
||||
setIsModalOpen(false);
|
||||
setFileToEdit(null);
|
||||
setIsEditing(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error editing file:', error);
|
||||
alert('Erreur lors de la modification du fichier');
|
||||
});
|
||||
} else {
|
||||
createRegistrationTemplateMaster(data, csrfToken)
|
||||
.then(data => {
|
||||
// Transformer le nouveau fichier avec les informations du groupe
|
||||
const transformedFile = transformFileData(data, groups);
|
||||
setFichiers(prevFiles => [...prevFiles, transformedFile]);
|
||||
setIsModalOpen(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error uploading file:', error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleTemplate = ({name, is_required, order, groupId, document_id}) => {
|
||||
if (!name) {
|
||||
alert('Veuillez entrer un nom de fichier.');
|
||||
return;
|
||||
@ -96,14 +129,16 @@ export default function FilesManagement({ csrfToken }) {
|
||||
formData.append('name', name);
|
||||
formData.append('is_required', is_required);
|
||||
formData.append('order', order);
|
||||
formData.append('document_id', document_id);
|
||||
|
||||
// Modification ici : vérifier si groupId existe et n'est pas vide
|
||||
if (groupId && groupId !== '') {
|
||||
formData.append('group', groupId); // Notez que le nom du champ est 'group' et non 'group_id'
|
||||
}
|
||||
|
||||
|
||||
if (isEditing && fileToEdit) {
|
||||
editRegistrationFormFileTemplate(fileToEdit.id, formData, csrfToken)
|
||||
editRegistrationTemplateMaster(fileToEdit.id, formData, csrfToken)
|
||||
.then(data => {
|
||||
// Transformer le fichier mis à jour avec les informations du groupe
|
||||
const transformedFile = transformFileData(data, groups);
|
||||
@ -115,11 +150,11 @@ export default function FilesManagement({ csrfToken }) {
|
||||
setIsEditing(false);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error editing file:', error);
|
||||
console.error('Error editing file:', error);
|
||||
alert('Erreur lors de la modification du fichier');
|
||||
});
|
||||
} else {
|
||||
createRegistrationFormFileTemplate(formData, csrfToken)
|
||||
createRegistrationTemplateMaster(formData, csrfToken)
|
||||
.then(data => {
|
||||
// Transformer le nouveau fichier avec les informations du groupe
|
||||
const transformedFile = transformFileData(data, groups);
|
||||
@ -127,25 +162,33 @@ export default function FilesManagement({ csrfToken }) {
|
||||
setIsModalOpen(false);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error uploading file:', error);
|
||||
console.error('Error uploading file:', error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleGroupSubmit = async (groupData) => {
|
||||
try {
|
||||
if (groupToEdit) {
|
||||
const updatedGroup = await editRegistrationFileGroup(groupToEdit.id, groupData, csrfToken);
|
||||
setGroups(groups.map(group => group.id === groupToEdit.id ? updatedGroup : group));
|
||||
setGroupToEdit(null);
|
||||
} else {
|
||||
const newGroup = await createRegistrationFileGroup(groupData, csrfToken);
|
||||
setGroups([...groups, newGroup]);
|
||||
}
|
||||
setIsGroupModalOpen(false);
|
||||
} catch (error) {
|
||||
logger.error('Error handling group:', error);
|
||||
alert('Erreur lors de l\'opération sur le groupe');
|
||||
const handleGroupSubmit = (groupData) => {
|
||||
if (groupToEdit) {
|
||||
editRegistrationFileGroup(groupToEdit.id, groupData, csrfToken)
|
||||
.then(updatedGroup => {
|
||||
setGroups(groups.map(group => group.id === groupToEdit.id ? updatedGroup : group));
|
||||
setGroupToEdit(null);
|
||||
setIsGroupModalOpen(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error handling group:', error);
|
||||
alert('Erreur lors de l\'opération sur le groupe');
|
||||
});
|
||||
} else {
|
||||
createRegistrationFileGroup(groupData, csrfToken)
|
||||
.then(newGroup => {
|
||||
setGroups([...groups, newGroup]);
|
||||
setIsGroupModalOpen(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error handling group:', error);
|
||||
alert('Erreur lors de l\'opération sur le groupe');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -175,24 +218,20 @@ export default function FilesManagement({ csrfToken }) {
|
||||
alert('Groupe supprimé avec succès.');
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error deleting group:', error);
|
||||
console.error('Error deleting group:', error);
|
||||
alert(error.message || 'Erreur lors de la suppression du groupe. Vérifiez qu\'aucune inscription n\'utilise ce groupe.');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Ajouter cette fonction de filtrage
|
||||
const filteredFiles = fichiers.filter(file => {
|
||||
if (!selectedGroup) return true;
|
||||
return file.group && file.group.id === parseInt(selectedGroup);
|
||||
return file.groups && file.groups.some(group => group.id === parseInt(selectedGroup));
|
||||
});
|
||||
|
||||
const columnsFiles = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||
{ name: 'Groupe', transform: (row) => row.group ? row.group.name : 'Aucun' },
|
||||
{ name: 'Date de création', transform: (row) => formatDate(new Date (row.date_added),"DD/MM/YYYY hh:mm:ss") },
|
||||
{ name: 'Fichier Obligatoire', transform: (row) => row.is_required ? 'Oui' : 'Non' },
|
||||
{ name: 'Ordre de fusion', transform: (row) => row.order },
|
||||
{ name: 'Groupes', transform: (row) => row.groups && row.groups.length > 0 ? row.groups.map(group => group.name).join(', ') : 'Aucun' },
|
||||
{ name: 'Actions', transform: (row) => (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
{row.file && (
|
||||
@ -206,9 +245,6 @@ export default function FilesManagement({ csrfToken }) {
|
||||
<button onClick={() => handleFileDelete(row.id)} className="text-red-500 hover:text-red-700">
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
<button onClick={() => handleSignatureRequest(row)} className="text-green-500 hover:text-green-700">
|
||||
<Signature size={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
];
|
||||
@ -228,40 +264,24 @@ export default function FilesManagement({ csrfToken }) {
|
||||
)}
|
||||
];
|
||||
|
||||
// Fonction pour gérer la demande de signature
|
||||
const handleSignatureRequest = (file) => {
|
||||
fetch('http://localhost:8080/DocuSeal/generateToken', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
document_id: file.id,
|
||||
user_email: 'anthony.casini.30@gmail.com',
|
||||
url: file.file
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
console.log("token received : ", data.token);
|
||||
setToken(data.token);
|
||||
setSelectedFile(file);
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
setIsOpen={setIsModalOpen}
|
||||
title={isEditing ? 'Modifier un fichier' : 'Ajouter un fichier'}
|
||||
setIsOpen={(isOpen) => {
|
||||
setIsModalOpen(isOpen);
|
||||
if (!isOpen) {
|
||||
setFileToEdit(null);
|
||||
}
|
||||
}}
|
||||
title={isEditing ? 'Modification du document' : 'Ajouter un document'}
|
||||
ContentComponent={() => (
|
||||
<FileUpload
|
||||
onFileUpload={handleFileUpload}
|
||||
handleCreateTemplateMaster={handleCreateTemplateMaster}
|
||||
fileToEdit={fileToEdit}
|
||||
/>
|
||||
)}
|
||||
modalClassName='w-4/5 h-4/5'
|
||||
/>
|
||||
<Modal
|
||||
isOpen={isGroupModalOpen}
|
||||
@ -324,14 +344,6 @@ export default function FilesManagement({ csrfToken }) {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{token && selectedFile && (
|
||||
<DocusealBuilder
|
||||
token={token}
|
||||
headers={{
|
||||
'Authorization': `Bearer Rh2CC75ZMZqirmtBGA5NRjUzj8hr9eDYTBeZxv3jgzb`
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
export default function RegistrationFileGroupForm({ onSubmit, initialData }) {
|
||||
const [name, setName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
setName(initialData.name);
|
||||
setDescription(initialData.description);
|
||||
}
|
||||
}, [initialData]);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
onSubmit({ name, description });
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Nom du groupe
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
rows={3}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
||||
>
|
||||
{initialData ? 'Modifier le groupe' : 'Créer le groupe'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { fetchRegistrationFileGroups } from '@/app/actions/registerFileGroupAction';
|
||||
|
||||
export default function RegistrationFileGroupList() {
|
||||
const [groups, setGroups] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchRegistrationFileGroups().then(data => setGroups(data));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Groupes de fichiers d'inscription</h2>
|
||||
<ul>
|
||||
{groups.map(group => (
|
||||
<li key={group.id}>{group.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user