Files
tirages2/src/screens/DetailClasseScreen.js
T
2026-09-04 20:05:29 +02:00

204 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useCallback, useRef, useState } from 'react';
import {
View,
Text,
FlatList,
TouchableOpacity,
StyleSheet,
ActivityIndicator,
} from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
import { obtenirClasse, effectuerTirage, reTirer } from '../storage';
import { couleurs } from '../theme';
function formaterDate(iso) {
const d = new Date(iso);
const pad = (n) => String(n).padStart(2, '0');
return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad(
d.getHours()
)}:${pad(d.getMinutes())}`;
}
export default function DetailClasseScreen({ route, navigation }) {
const { classeId } = route.params;
const [classe, setClasse] = useState(null);
const [enCours, setEnCours] = useState(false);
// Ref en plus du state : le state ne se répercute qu'au prochain rendu,
// ce qui laisse une fenêtre où un second tap très rapide (avant que le
// bouton soit visuellement désactivé) pourrait lancer un 2e tirage pendant
// que le premier écrit encore dans le stockage. La ref, elle, est lue et
// mise à jour de façon synchrone, donc elle bloque le double-tap immédiatement.
const enCoursRef = useRef(false);
const recharger = useCallback(async () => {
const c = await obtenirClasse(classeId);
setClasse(c);
if (c) navigation.setOptions({ title: c.nom });
}, [classeId]);
useFocusEffect(
useCallback(() => {
recharger();
}, [recharger])
);
const tirer = async () => {
if (enCoursRef.current) return;
enCoursRef.current = true;
setEnCours(true);
try {
const c = await effectuerTirage(classeId);
setClasse(c);
} finally {
enCoursRef.current = false;
setEnCours(false);
}
};
const reTirerAction = async () => {
if (enCoursRef.current) return;
enCoursRef.current = true;
setEnCours(true);
try {
const c = await reTirer(classeId);
setClasse(c);
} finally {
enCoursRef.current = false;
setEnCours(false);
}
};
if (!classe) {
return (
<View style={[styles.container, styles.centre]}>
<ActivityIndicator />
</View>
);
}
const dernierTirage = classe.tirages[0];
return (
<View style={styles.container}>
<Text style={styles.sousTitre}>
{classe.nombreEleves} élèves · pondération max ×{classe.ponderationMax}
</Text>
<View style={styles.cardTirage}>
<TouchableOpacity
style={[styles.bouton, enCours && styles.boutonDesactive]}
onPress={tirer}
disabled={enCours}
>
<Text style={styles.boutonTexte}>🎲 Tirer un élève au sort</Text>
</TouchableOpacity>
{dernierTirage && (
<>
<Text style={styles.numeroTire}>n°{dernierTirage.numeroEleve}</Text>
<TouchableOpacity
style={[styles.boutonSecondaire, enCours && styles.boutonDesactive]}
onPress={reTirerAction}
disabled={enCours}
>
<Text style={styles.boutonSecondaireTexte}>
🔁 Re-tirer (annule et retire)
</Text>
</TouchableOpacity>
</>
)}
</View>
<Text style={styles.titreHistorique}>Historique des tirages</Text>
<FlatList
data={classe.tirages}
keyExtractor={(item) => item.id}
contentContainerStyle={{ paddingBottom: 24 }}
ListEmptyComponent={
<Text style={styles.texteVide}>Aucun tirage pour le moment.</Text>
}
renderItem={({ item, index }) => (
<View style={styles.ligneHistorique}>
<View style={styles.badge}>
<Text style={styles.badgeTexte}>{item.numeroEleve}</Text>
</View>
<Text style={styles.dateHistorique}>{formaterDate(item.dateTirage)}</Text>
{index === 0 && <Text style={styles.etiquetteRecent}>dernier</Text>}
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: couleurs.fond, padding: 16 },
centre: { justifyContent: 'center', alignItems: 'center' },
sousTitre: { color: couleurs.texteSecondaire, marginBottom: 14, fontSize: 14 },
cardTirage: {
backgroundColor: couleurs.carte,
borderRadius: 14,
padding: 20,
alignItems: 'center',
marginBottom: 22,
shadowColor: '#000',
shadowOpacity: 0.06,
shadowRadius: 4,
shadowOffset: { width: 0, height: 1 },
elevation: 1,
},
bouton: {
backgroundColor: couleurs.primaire,
borderRadius: 10,
paddingVertical: 14,
paddingHorizontal: 20,
alignItems: 'center',
width: '100%',
},
boutonDesactive: { opacity: 0.6 },
boutonTexte: { color: 'white', fontSize: 16, fontWeight: '600' },
numeroTire: {
fontSize: 52,
fontWeight: '800',
color: couleurs.primaire,
marginVertical: 14,
},
boutonSecondaire: {
borderRadius: 10,
paddingVertical: 12,
paddingHorizontal: 20,
alignItems: 'center',
width: '100%',
backgroundColor: couleurs.fond,
borderWidth: 1,
borderColor: couleurs.bordure,
},
boutonSecondaireTexte: { color: couleurs.texte, fontSize: 15, fontWeight: '600' },
titreHistorique: { fontSize: 16, fontWeight: '700', marginBottom: 10, color: couleurs.texte },
texteVide: { color: couleurs.texteSecondaire },
ligneHistorique: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: couleurs.bordure,
},
badge: {
backgroundColor: couleurs.primaire,
borderRadius: 14,
width: 28,
height: 28,
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
},
badgeTexte: { color: 'white', fontWeight: '700', fontSize: 13 },
dateHistorique: { color: couleurs.texteSecondaire, flex: 1 },
etiquetteRecent: {
fontSize: 11,
color: couleurs.succes,
fontWeight: '700',
textTransform: 'uppercase',
},
});