feat: made stuff
This commit is contained in:
@@ -67,7 +67,9 @@ function validatedWeeks(mobs: Mobilite[]): number {
|
||||
.reduce((sum, m) => sum + m.duree, 0);
|
||||
}
|
||||
|
||||
export default function MobilityOverview() {
|
||||
export default function MobilityOverview(
|
||||
{ initialNumEtud }: { initialNumEtud?: number } = {},
|
||||
) {
|
||||
const [students, setStudents] = useState<Student[]>([]);
|
||||
const [promos, setPromos] = useState<Promotion[]>([]);
|
||||
const [mobilites, setMobilites] = useState<Mobilite[]>([]);
|
||||
@@ -105,6 +107,12 @@ export default function MobilityOverview() {
|
||||
setStagesMap(
|
||||
Object.fromEntries((stData as Stage[]).map((s) => [s.id, s])),
|
||||
);
|
||||
if (initialNumEtud) {
|
||||
const s = (sData as Student[]).find((s) =>
|
||||
s.numEtud === initialNumEtud
|
||||
);
|
||||
if (s) setDetailStudent(s);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : "Erreur");
|
||||
} finally {
|
||||
@@ -116,6 +124,18 @@ export default function MobilityOverview() {
|
||||
load();
|
||||
}, []);
|
||||
|
||||
function openStudent(s: Student) {
|
||||
setDetailStudent(s);
|
||||
history.pushState(null, "", `/mobility/overview/${s.numEtud}`);
|
||||
}
|
||||
|
||||
function closeStudent() {
|
||||
setDetailStudent(null);
|
||||
setEditingMob(null);
|
||||
setShowAddForm(false);
|
||||
history.pushState(null, "", "/mobility/overview");
|
||||
}
|
||||
|
||||
// If in detail view, render that
|
||||
if (detailStudent) {
|
||||
return (
|
||||
@@ -128,11 +148,7 @@ export default function MobilityOverview() {
|
||||
setEditingMob={setEditingMob}
|
||||
showAddForm={showAddForm}
|
||||
setShowAddForm={setShowAddForm}
|
||||
onBack={() => {
|
||||
setDetailStudent(null);
|
||||
setEditingMob(null);
|
||||
setShowAddForm(false);
|
||||
}}
|
||||
onBack={closeStudent}
|
||||
onReload={load}
|
||||
/>
|
||||
);
|
||||
@@ -207,14 +223,14 @@ export default function MobilityOverview() {
|
||||
<ListView
|
||||
students={filtered}
|
||||
mobsByStudent={mobsByStudent}
|
||||
onConsult={(s) => setDetailStudent(s)}
|
||||
onConsult={(s) => openStudent(s)}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<KanbanView
|
||||
students={filtered}
|
||||
mobsByStudent={mobsByStudent}
|
||||
onConsult={(s) => setDetailStudent(s)}
|
||||
onConsult={(s) => openStudent(s)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -637,6 +653,9 @@ function DetailView(
|
||||
numEtud={student.numEtud}
|
||||
ecoles={ecoles}
|
||||
paysList={paysList}
|
||||
availableStages={Object.values(stagesMap)
|
||||
.filter((s) => s.numEtud === student.numEtud)
|
||||
.filter((s) => !mobilites.some((m) => m.idStage === s.id))}
|
||||
onCancel={() => setShowAddForm(false)}
|
||||
onSave={async () => {
|
||||
setShowAddForm(false);
|
||||
@@ -774,10 +793,11 @@ function MobEditForm(
|
||||
}
|
||||
|
||||
function MobAddForm(
|
||||
{ numEtud, ecoles, paysList, onCancel, onSave }: {
|
||||
{ numEtud, ecoles, paysList, availableStages, onCancel, onSave }: {
|
||||
numEtud: number;
|
||||
ecoles: string[];
|
||||
paysList: string[];
|
||||
availableStages: Stage[];
|
||||
onCancel: () => void;
|
||||
onSave: () => Promise<void>;
|
||||
},
|
||||
@@ -786,8 +806,19 @@ function MobAddForm(
|
||||
const [ecole, setEcole] = useState("");
|
||||
const [pays, setPays] = useState("");
|
||||
const [status, setStatus] = useState("contracts_received");
|
||||
const [selectedStageId, setSelectedStageId] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const isStageLinked = selectedStageId !== "";
|
||||
|
||||
function onStageChange(value: string) {
|
||||
setSelectedStageId(value);
|
||||
if (value) {
|
||||
const stage = availableStages.find((s) => s.id === Number(value));
|
||||
if (stage) setDuree(String(stage.duree));
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
setBusy(true);
|
||||
try {
|
||||
@@ -797,9 +828,10 @@ function MobAddForm(
|
||||
body: JSON.stringify({
|
||||
numEtud,
|
||||
duree: parseInt(duree),
|
||||
ecole: ecole || null,
|
||||
pays: pays || null,
|
||||
status,
|
||||
ecole: isStageLinked ? null : (ecole || null),
|
||||
pays: isStageLinked ? null : (pays || null),
|
||||
status: isStageLinked ? "validated" : status,
|
||||
idStage: isStageLinked ? Number(selectedStageId) : null,
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error("Erreur");
|
||||
@@ -815,6 +847,24 @@ function MobAddForm(
|
||||
<div class="edit-section" style={{ marginBottom: "1rem" }}>
|
||||
<p class="edit-section-title">Nouvelle mobilité</p>
|
||||
<div class="form-grid">
|
||||
{availableStages.length > 0 && (
|
||||
<div class="form-field">
|
||||
<label>Lier à un stage</label>
|
||||
<select
|
||||
class="filter-select"
|
||||
value={selectedStageId}
|
||||
onChange={(e) =>
|
||||
onStageChange((e.target as HTMLSelectElement).value)}
|
||||
>
|
||||
<option value="">— Mobilité d'étude —</option>
|
||||
{availableStages.map((s) => (
|
||||
<option key={s.id} value={String(s.id)}>
|
||||
{s.nomEntreprise} ({s.duree} sem.)
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
<div class="form-field">
|
||||
<label>Durée (semaines)</label>
|
||||
<input
|
||||
@@ -825,43 +875,59 @@ function MobAddForm(
|
||||
onInput={(e) => setDuree((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>École</label>
|
||||
<input
|
||||
class="form-input"
|
||||
list="add-ecoles"
|
||||
value={ecole}
|
||||
onInput={(e) => setEcole((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
<datalist id="add-ecoles">
|
||||
{ecoles.map((e) => <option key={e} value={e} />)}
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Pays</label>
|
||||
<input
|
||||
class="form-input"
|
||||
list="add-pays"
|
||||
value={pays}
|
||||
onInput={(e) => setPays((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
<datalist id="add-pays">
|
||||
{paysList.map((p) => <option key={p} value={p} />)}
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Status</label>
|
||||
<select
|
||||
class="filter-select"
|
||||
value={status}
|
||||
onChange={(e) => setStatus((e.target as HTMLSelectElement).value)}
|
||||
>
|
||||
{STATUS_ORDER.map((s) => (
|
||||
<option key={s} value={s}>{STATUS_LABELS[s]}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{!isStageLinked && (
|
||||
<>
|
||||
<div class="form-field">
|
||||
<label>École</label>
|
||||
<input
|
||||
class="form-input"
|
||||
list="add-ecoles"
|
||||
value={ecole}
|
||||
onInput={(e) => setEcole((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
<datalist id="add-ecoles">
|
||||
{ecoles.map((e) => <option key={e} value={e} />)}
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Pays</label>
|
||||
<input
|
||||
class="form-input"
|
||||
list="add-pays"
|
||||
value={pays}
|
||||
onInput={(e) => setPays((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
<datalist id="add-pays">
|
||||
{paysList.map((p) => <option key={p} value={p} />)}
|
||||
</datalist>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Status</label>
|
||||
<select
|
||||
class="filter-select"
|
||||
value={status}
|
||||
onChange={(e) =>
|
||||
setStatus((e.target as HTMLSelectElement).value)}
|
||||
>
|
||||
{STATUS_ORDER.map((s) => (
|
||||
<option key={s} value={s}>{STATUS_LABELS[s]}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isStageLinked && (
|
||||
<p
|
||||
style={{
|
||||
fontSize: "0.8rem",
|
||||
opacity: 0.7,
|
||||
margin: "0.4rem 0",
|
||||
}}
|
||||
>
|
||||
Mobilité liée à un stage — status automatiquement « Validé »
|
||||
</p>
|
||||
)}
|
||||
<div style={{ display: "flex", gap: "0.5rem", marginTop: "0.5rem" }}>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user