mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-04 04:01:27 +00:00
feat: Ajout bouton de refus de dossier avec zone de saisie de motif [N3WTS-2]
This commit is contained in:
69
Front-End/src/components/Inscription/RefuseSubscription.js
Normal file
69
Front-End/src/components/Inscription/RefuseSubscription.js
Normal file
@ -0,0 +1,69 @@
|
||||
import React, { useState } from 'react';
|
||||
import Textarea from '@/components/Textarea';
|
||||
import Button from '@/components/Form/Button';
|
||||
|
||||
/**
|
||||
* Composant pour afficher le textarea de refus et le bouton d'action
|
||||
* @param {function} onRefuse - callback appelée avec la raison du refus
|
||||
* @param {boolean} isLoading - état de chargement
|
||||
*/
|
||||
const RefuseSubscription = ({ onRefuse, isLoading }) => {
|
||||
const [reason, setReason] = useState('');
|
||||
const [showTextarea, setShowTextarea] = useState(false);
|
||||
|
||||
const handleRefuseClick = () => {
|
||||
setShowTextarea((prev) => !prev);
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (reason.trim()) {
|
||||
onRefuse(reason);
|
||||
setShowTextarea(false);
|
||||
setReason('');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-stretch justify-center h-full">
|
||||
{!showTextarea ? (
|
||||
<Button
|
||||
text="Refuser le dossier d'inscription"
|
||||
onClick={handleRefuseClick}
|
||||
className="bg-red-500 hover:bg-red-700 text-white min-w-[220px] h-10"
|
||||
type="button"
|
||||
/>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-2 mt-2">
|
||||
<div className="flex items-start gap-2">
|
||||
<Textarea
|
||||
value={reason}
|
||||
onChange={(e) => setReason(e.target.value)}
|
||||
placeholder="Ex : Réception de dossier trop tardive"
|
||||
rows={3}
|
||||
className="w-full"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRefuseClick}
|
||||
className="text-sm text-gray-500 hover:text-red-600 underline mt-1"
|
||||
tabIndex={-1}
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
</div>
|
||||
<Button
|
||||
text={isLoading ? 'Refus en cours...' : 'Confirmer le refus'}
|
||||
type="submit"
|
||||
className="bg-red-600 hover:bg-red-800 text-white w-full"
|
||||
style={{ height: '40px' }}
|
||||
disabled={isLoading || !reason.trim()}
|
||||
/>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RefuseSubscription;
|
||||
@ -11,6 +11,7 @@ import logger from '@/utils/logger';
|
||||
import { School, CheckCircle, Hourglass, FileText } from 'lucide-react';
|
||||
import SectionHeader from '@/components/SectionHeader';
|
||||
import Button from '@/components/Form/Button';
|
||||
import RefuseSubscription from './RefuseSubscription';
|
||||
|
||||
export default function ValidateSubscription({
|
||||
studentId,
|
||||
@ -20,6 +21,8 @@ export default function ValidateSubscription({
|
||||
student_file,
|
||||
onAccept,
|
||||
classes,
|
||||
onRefuse, // callback pour refus
|
||||
isLoadingRefuse = false, // état de chargement refus
|
||||
}) {
|
||||
const [schoolFileTemplates, setSchoolFileTemplates] = useState([]);
|
||||
const [parentFileTemplates, setParentFileTemplates] = useState([]);
|
||||
@ -162,8 +165,8 @@ export default function ValidateSubscription({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Colonne droite : Liste des documents, Option de fusion et Affectation */}
|
||||
<div className="w-1/4 flex flex-col gap-4">
|
||||
{/* Colonne droite : Liste des documents, Option de fusion, Affectation, Refus */}
|
||||
<div className="w-1/4 flex flex-col flex-1 gap-4">
|
||||
{/* Liste des documents */}
|
||||
<div className="flex-1 bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-200 overflow-y-auto">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">
|
||||
@ -194,52 +197,50 @@ export default function ValidateSubscription({
|
||||
</div>
|
||||
|
||||
{/* Option de fusion */}
|
||||
<div className="bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-200">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">
|
||||
Option de fusion
|
||||
</h3>
|
||||
<div className="flex items-center justify-between">
|
||||
<ToggleSwitch
|
||||
label="Fusionner les documents"
|
||||
checked={mergeDocuments}
|
||||
onChange={handleToggleMergeDocuments}
|
||||
<div className="bg-white p-4 rounded-lg shadow-sm border border-gray-200 flex items-center justify-center mb-2 min-h-[70px]">
|
||||
<ToggleSwitch
|
||||
label="Fusionner les documents"
|
||||
checked={mergeDocuments}
|
||||
onChange={handleToggleMergeDocuments}
|
||||
className="ml-0"
|
||||
/>
|
||||
</div>
|
||||
{/* Affectation à une classe */}
|
||||
<div className="bg-white p-4 rounded-lg shadow-sm border border-gray-200 flex items-center gap-4">
|
||||
<div className="flex-1 flex flex-col">
|
||||
<SelectChoice
|
||||
name="associated_class"
|
||||
label="Liste des classes"
|
||||
placeHolder="Sélectionner une classe"
|
||||
selected={formData.associated_class || ''}
|
||||
callback={(e) => onChange('associated_class', e.target.value)}
|
||||
choices={classes.map((classe) => ({
|
||||
value: classe.id,
|
||||
label: classe.atmosphere_name,
|
||||
}))}
|
||||
required
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section Affectation */}
|
||||
<div className="bg-white p-4 rounded-lg shadow-sm border border-gray-200">
|
||||
<h3 className="text-lg font-semibold text-gray-800 mb-4">
|
||||
Affectation à une classe
|
||||
</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<SelectChoice
|
||||
name="associated_class"
|
||||
label="Classe"
|
||||
placeHolder="Sélectionner une classe"
|
||||
selected={formData.associated_class || ''} // La valeur actuelle de la classe associée
|
||||
callback={(e) => onChange('associated_class', e.target.value)} // Met à jour formData
|
||||
choices={classes.map((classe) => ({
|
||||
value: classe.id,
|
||||
label: classe.atmosphere_name,
|
||||
}))} // Liste des classes disponibles
|
||||
required
|
||||
/>
|
||||
<Button
|
||||
text="Valider le dossier d'inscription"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleAssignClass();
|
||||
}}
|
||||
primary
|
||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||
!isPageValid
|
||||
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
||||
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
||||
}`}
|
||||
disabled={!isPageValid}
|
||||
/>
|
||||
</div>
|
||||
{/* Boutons Valider/Refuser en bas, centrés */}
|
||||
<div className="mt-auto flex justify-center gap-4 py-4">
|
||||
<Button
|
||||
text="Valider le dossier d'inscription"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleAssignClass();
|
||||
}}
|
||||
primary
|
||||
className={`min-w-[220px] h-10 rounded-md shadow-sm focus:outline-none ${
|
||||
!isPageValid
|
||||
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
||||
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
||||
}`}
|
||||
disabled={!isPageValid}
|
||||
/>
|
||||
<RefuseSubscription onRefuse={onRefuse} isLoading={isLoadingRefuse} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
23
Front-End/src/components/Textarea.js
Normal file
23
Front-End/src/components/Textarea.js
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Textarea composant réutilisable
|
||||
* @param {string} value - Valeur du textarea
|
||||
* @param {function} onChange - Fonction appelée lors d'un changement
|
||||
* @param {string} placeholder - Texte d'exemple
|
||||
* @param {number} rows - Nombre de lignes
|
||||
* @param {string} className - Classes CSS additionnelles
|
||||
* @param {object} props - Props additionnels
|
||||
*/
|
||||
const Textarea = ({ value, onChange, placeholder = '', rows = 3, className = '', ...props }) => (
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
rows={rows}
|
||||
className={`border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-emerald-500 resize-y ${className}`}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Textarea;
|
||||
Reference in New Issue
Block a user