mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
268 lines
8.9 KiB
JavaScript
268 lines
8.9 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import Modal from '@/components/Modal';
|
|
import logger from '@/utils/logger';
|
|
import { Edit3, Trash2, Plus } from 'lucide-react';
|
|
|
|
function ParentFileForm({ initialData, groups, onSubmit, onCancel }) {
|
|
const [name, setName] = useState(initialData?.name || '');
|
|
const [description, setDescription] = useState(initialData?.description || '');
|
|
// Correction : s'assurer que selectedGroups ne contient que des IDs uniques
|
|
const [selectedGroups, setSelectedGroups] = useState(
|
|
Array.isArray(initialData?.groups)
|
|
? Array.from(
|
|
new Set(
|
|
initialData.groups.map(g => (typeof g === 'object' && g !== null && 'id' in g ? g.id : g))
|
|
)
|
|
)
|
|
: []
|
|
);
|
|
const [isRequired, setIsRequired] = useState(initialData?.is_required || false);
|
|
|
|
useEffect(() => {
|
|
if (initialData) {
|
|
setName(initialData.name || '');
|
|
setDescription(initialData.description || '');
|
|
setSelectedGroups(
|
|
Array.isArray(initialData.groups)
|
|
? Array.from(
|
|
new Set(
|
|
initialData.groups.map(g => (typeof g === 'object' && g !== null && 'id' in g ? g.id : g))
|
|
)
|
|
)
|
|
: []
|
|
);
|
|
setIsRequired(initialData.is_required || false);
|
|
}
|
|
}, [initialData]);
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
if (!name || selectedGroups.length === 0) return;
|
|
const data = {
|
|
name,
|
|
description,
|
|
groups: selectedGroups,
|
|
is_required: isRequired,
|
|
id: initialData?.id,
|
|
};
|
|
logger.debug('[ParentFileForm] handleSubmit data:', data);
|
|
onSubmit(data);
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} className="space-y-4 py-2">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Nom de la pièce <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={name}
|
|
required
|
|
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-orange-500 focus:border-orange-500"
|
|
/>
|
|
</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={2}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-orange-500 focus:border-orange-500"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
Dossiers d'inscription <span className="text-red-500">*</span>
|
|
</label>
|
|
<select
|
|
multiple
|
|
value={selectedGroups}
|
|
onChange={e =>
|
|
setSelectedGroups(
|
|
Array.from(new Set(Array.from(e.target.selectedOptions, opt => Number(opt.value))))
|
|
)
|
|
}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-orange-500 focus:border-orange-500"
|
|
required
|
|
>
|
|
{groups.map(group => (
|
|
<option key={`group-option-${group.id}`} value={group.id}>
|
|
{group.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<input
|
|
type="checkbox"
|
|
id="is_required"
|
|
checked={isRequired}
|
|
onChange={e => setIsRequired(e.target.checked)}
|
|
className="mr-2"
|
|
/>
|
|
<label htmlFor="is_required" className="text-sm text-gray-700">
|
|
Obligatoire
|
|
</label>
|
|
</div>
|
|
<div className="flex justify-end gap-2">
|
|
<button
|
|
type="button"
|
|
className="px-4 py-2 rounded-md bg-gray-200 text-gray-700 hover:bg-gray-300"
|
|
onClick={onCancel}
|
|
>
|
|
Annuler
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
className="bg-orange-600 text-white px-4 py-2 rounded-md hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2"
|
|
disabled={!name || selectedGroups.length === 0}
|
|
>
|
|
{initialData?.id ? 'Modifier' : 'Créer'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|
|
|
|
export default function ParentFiles({
|
|
parentFiles,
|
|
groups,
|
|
handleCreate,
|
|
handleEdit,
|
|
handleDelete,
|
|
singleForm = false,
|
|
initialData = null,
|
|
onCancel,
|
|
}) {
|
|
const [isModalOpen, setIsModalOpen] = useState(singleForm);
|
|
const [editingFile, setEditingFile] = useState(initialData);
|
|
|
|
useEffect(() => {
|
|
if (singleForm) {
|
|
setIsModalOpen(true);
|
|
setEditingFile(initialData);
|
|
}
|
|
}, [singleForm, initialData]);
|
|
|
|
const openCreateModal = () => {
|
|
setEditingFile(null);
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const openEditModal = (file) => {
|
|
setEditingFile(file);
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const closeModal = () => {
|
|
setEditingFile(null);
|
|
setIsModalOpen(false);
|
|
if (onCancel) onCancel();
|
|
};
|
|
|
|
const handleFormSubmit = (data) => {
|
|
logger.debug('[ParentFiles] handleFormSubmit data:', data);
|
|
if (editingFile && editingFile.id) {
|
|
logger.debug('[ParentFiles] handleEdit called with:', data.id, data);
|
|
handleEdit(data.id, data).then(closeModal);
|
|
} else {
|
|
logger.debug('[ParentFiles] handleCreate called with:', data);
|
|
handleCreate(data).then(closeModal);
|
|
}
|
|
};
|
|
|
|
if (singleForm) {
|
|
return (
|
|
<ParentFileForm
|
|
initialData={editingFile}
|
|
groups={groups}
|
|
onSubmit={handleFormSubmit}
|
|
onCancel={closeModal}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h2 className="text-lg font-bold text-orange-700">Pièces à fournir</h2>
|
|
<button
|
|
className="flex items-center gap-2 bg-orange-500 hover:bg-orange-600 text-white px-4 py-2 rounded shadow"
|
|
onClick={openCreateModal}
|
|
>
|
|
<Plus className="w-5 h-5" />
|
|
<span>Ajouter une pièce</span>
|
|
</button>
|
|
</div>
|
|
<table className="min-w-full border border-gray-200 rounded bg-white">
|
|
<thead>
|
|
<tr className="bg-orange-50">
|
|
<th className="px-3 py-2 text-left text-xs font-medium text-orange-700 border-b">Nom</th>
|
|
<th className="px-3 py-2 text-left text-xs font-medium text-orange-700 border-b">Description</th>
|
|
<th className="px-3 py-2 text-left text-xs font-medium text-orange-700 border-b">Dossiers</th>
|
|
<th className="px-3 py-2 text-center text-xs font-medium text-orange-700 border-b">Obligatoire</th>
|
|
<th className="px-3 py-2 text-center text-xs font-medium text-orange-700 border-b">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{parentFiles.length === 0 ? (
|
|
<tr>
|
|
<td colSpan={5} className="text-center py-6 text-gray-400">Aucune pièce à fournir</td>
|
|
</tr>
|
|
) : (
|
|
parentFiles.map((file) => (
|
|
<tr key={file.id} className="hover:bg-orange-50">
|
|
<td className="px-3 py-2 border-b">{file.name}</td>
|
|
<td className="px-3 py-2 border-b">{file.description}</td>
|
|
<td className="px-3 py-2 border-b">
|
|
{(file.groups || []).map(
|
|
gid => groups.find(g => g.id === gid)?.name || gid
|
|
).join(', ')}
|
|
</td>
|
|
<td className="px-3 py-2 border-b text-center">
|
|
{file.is_required ? (
|
|
<span className="bg-green-100 text-green-700 px-2 py-1 rounded text-xs font-semibold">Oui</span>
|
|
) : (
|
|
<span className="bg-gray-100 text-gray-600 px-2 py-1 rounded text-xs">Non</span>
|
|
)}
|
|
</td>
|
|
<td className="px-3 py-2 border-b text-center">
|
|
<button
|
|
className="text-blue-500 hover:text-blue-700 mr-2"
|
|
onClick={() => openEditModal(file)}
|
|
>
|
|
<Edit3 className="w-5 h-5" />
|
|
</button>
|
|
<button
|
|
className="text-red-500 hover:text-red-700"
|
|
onClick={() => handleDelete(file.id)}
|
|
>
|
|
<Trash2 className="w-5 h-5" />
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
<Modal
|
|
isOpen={isModalOpen}
|
|
setIsOpen={closeModal}
|
|
title={editingFile ? 'Modifier la pièce à fournir' : 'Créer une pièce à fournir'}
|
|
modalClassName="w-full max-w-md"
|
|
>
|
|
<ParentFileForm
|
|
initialData={editingFile}
|
|
groups={groups}
|
|
onSubmit={handleFormSubmit}
|
|
onCancel={closeModal}
|
|
/>
|
|
</Modal>
|
|
</div>
|
|
);
|
|
}
|