fix: corrections post-audit (traçabilité, identité État, accents, graphique mobile)

Backend:
- table audits réellement alimentée via trait Auditable (observers Eloquent)
  sur Utilisateur, SaisieTracker, Feed, Emotion ; mot de passe jamais loggé
- 4 nouveaux tests (19/19 au total)

Frontend web:
- en-tête/pied République Française ajoutés aux pages connexion/inscription
- bouton variant danger réellement rouge (tokens erreur DSFR)

Mobile:
- ~30 libellés ré-accentués (Intensité, Succès, Déconnexion, Répartition...)
- Colors.secondary = rouge Marianne #E1000F (différenciation cartes/icônes)
- vrai camembert SVG des rapports (react-native-svg)

Docs: plans oraux mis à jour (audits actif, 29 tests, stockage JWT en Q/R)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Azmog
2026-06-19 11:17:40 +02:00
parent 7be7bec53d
commit 4f54340e89
25 changed files with 580 additions and 49 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: Colors.secondary,
tabBarActiveTintColor: Colors.primary,
tabBarInactiveTintColor: Colors.gray[400],
tabBarStyle: {
backgroundColor: Colors.white,
+3 -3
View File
@@ -49,7 +49,7 @@ export default function DashboardScreen() {
</View>
<View style={styles.statCard}>
<Text style={styles.statValue}>{rapport.stats.intensite_moyenne?.toFixed(1) || '—'}</Text>
<Text style={styles.statLabel}>Intensite moy.</Text>
<Text style={styles.statLabel}>Intensité moy.</Text>
</View>
<View style={styles.statCard}>
<Text style={styles.statValue}>{rapport.stats.emotion_dominante?.icone || '—'}</Text>
@@ -63,7 +63,7 @@ export default function DashboardScreen() {
{recentSaisies.length > 0 && (
<View style={styles.recentContainer}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Dernieres emotions</Text>
<Text style={styles.sectionTitle}>Dernières émotions</Text>
<TouchableOpacity onPress={() => router.push('/(tabs)/journal')}>
<Text style={styles.seeAll}>Tout voir</Text>
</TouchableOpacity>
@@ -76,7 +76,7 @@ export default function DashboardScreen() {
<View style={styles.saisieContent}>
<Text style={styles.saisieEmotion}>{saisie.emotion?.nom}</Text>
<Text style={styles.saisieDate}>
{new Date(saisie.date_saisie).toLocaleDateString('fr-FR')} — Intensite: {saisie.intensite}/10
{new Date(saisie.date_saisie).toLocaleDateString('fr-FR')} — Intensité: {saisie.intensite}/10
</Text>
</View>
</View>
+1 -1
View File
@@ -7,7 +7,7 @@ export default function InfoLayout() {
screenOptions={{
headerStyle: { backgroundColor: Colors.white },
headerTitleStyle: { fontWeight: '700', color: Colors.black },
headerTintColor: Colors.secondary,
headerTintColor: Colors.primary,
}}
>
<Stack.Screen name="index" options={{ title: 'Infos' }} />
+4 -4
View File
@@ -38,7 +38,7 @@ export default function EditEntryScreen() {
note: note.trim() || undefined,
date_saisie: dateSaisie,
});
Alert.alert('Succes', 'Saisie modifiee !', [
Alert.alert('Succès', 'Saisie modifiée !', [
{ text: 'OK', onPress: () => router.back() },
]);
} catch {
@@ -60,13 +60,13 @@ export default function EditEntryScreen() {
{/* Emotion */}
<View style={styles.section}>
<Text style={styles.label}>Emotion</Text>
<Text style={styles.label}>Émotion</Text>
<EmotionPicker value={emotionId} onChange={setEmotionId} />
</View>
{/* Intensite */}
<View style={styles.section}>
<Text style={styles.label}>Intensite</Text>
<Text style={styles.label}>Intensité</Text>
<View style={styles.intensityContainer}>
<Text style={styles.intensityValue}>{intensite}</Text>
<Text style={styles.intensityUnit}>/10</Text>
@@ -95,7 +95,7 @@ export default function EditEntryScreen() {
style={styles.textArea}
value={note}
onChangeText={setNote}
placeholder="Decrivez ce que vous ressentez..."
placeholder="Décrivez ce que vous ressentez..."
multiline
numberOfLines={4}
textAlignVertical="top"
+2 -2
View File
@@ -45,7 +45,7 @@ export default function JournalScreen() {
<View style={[styles.badge, { backgroundColor: `${item.emotion?.couleur}20` }]}>
<Text style={[styles.badgeText, { color: item.emotion?.couleur }]}>{item.emotion?.nom}</Text>
</View>
<Text style={styles.intensityText}>Intensite: {item.intensite}/10</Text>
<Text style={styles.intensityText}>Intensité: {item.intensite}/10</Text>
</View>
<View style={styles.intensityBar}>
<View style={[styles.intensityFill, { width: `${item.intensite * 10}%`, backgroundColor: item.emotion?.couleur || Colors.primary }]} />
@@ -76,7 +76,7 @@ export default function JournalScreen() {
<View style={styles.empty}>
<Ionicons name="book-outline" size={48} color={Colors.gray[300]} />
<Text style={styles.emptyTitle}>Aucune saisie</Text>
<Text style={styles.emptyText}>Commencez par enregistrer votre premiere emotion</Text>
<Text style={styles.emptyText}>Commencez par enregistrer votre première émotion</Text>
</View>
}
/>
+3 -3
View File
@@ -22,7 +22,7 @@ export default function NewEntryScreen() {
note: note.trim() || undefined,
date_saisie: new Date().toISOString().split('T')[0],
});
Alert.alert('Succes', 'Saisie enregistree !', [
Alert.alert('Succès', 'Saisie enregistrée !', [
{ text: 'OK', onPress: () => router.back() },
]);
} catch {
@@ -67,7 +67,7 @@ export default function NewEntryScreen() {
{/* Step 2: Intensity */}
{step === 2 && (
<View>
<Text style={styles.stepTitle}>A quel point ressentez-vous cette emotion ?</Text>
<Text style={styles.stepTitle}>À quel point ressentez-vous cette émotion ?</Text>
<View style={styles.intensityContainer}>
<Text style={styles.intensityValue}>{intensite}</Text>
<Text style={styles.intensityLabel}>/10</Text>
@@ -115,7 +115,7 @@ export default function NewEntryScreen() {
style={styles.textArea}
value={note}
onChangeText={setNote}
placeholder="Decrivez ce que vous ressentez, ce qui a declenche cette emotion..."
placeholder="Décrivez ce que vous ressentez, ce qui a déclenché cette émotion..."
multiline
numberOfLines={5}
textAlignVertical="top"
+19 -5
View File
@@ -3,6 +3,7 @@ import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from 'react-nati
import { Ionicons } from '@expo/vector-icons';
import { useRapport } from '@/hooks/useRapport';
import { Colors } from '@/lib/colors';
import EmotionPieChart from '@/components/EmotionPieChart';
type Period = 'week' | 'month' | 'quarter' | 'year';
@@ -10,7 +11,7 @@ const PERIODS: { value: Period; label: string }[] = [
{ value: 'week', label: 'Semaine' },
{ value: 'month', label: 'Mois' },
{ value: 'quarter', label: 'Trimestre' },
{ value: 'year', label: 'Annee' },
{ value: 'year', label: 'Année' },
];
export default function ReportsScreen() {
@@ -50,10 +51,10 @@ export default function ReportsScreen() {
<View style={styles.statCard}>
<Ionicons name="trending-up" size={20} color={Colors.secondary} />
<Text style={styles.statValue}>{rapport.stats.intensite_moyenne?.toFixed(1) || '—'}</Text>
<Text style={styles.statLabel}>Intensite moy.</Text>
<Text style={styles.statLabel}>Intensité moy.</Text>
</View>
<View style={styles.statCard}>
<Ionicons name="heart" size={20} color={Colors.rougeMarianne} />
<Ionicons name="heart" size={20} color={rapport.stats.emotion_dominante?.couleur || Colors.blueFranceSun} />
<Text style={styles.statValue}>{rapport.stats.emotion_dominante?.icone || '—'}</Text>
<Text style={styles.statLabel}>{rapport.stats.emotion_dominante?.nom || 'Aucune'}</Text>
</View>
@@ -69,7 +70,19 @@ export default function ReportsScreen() {
{/* Repartition */}
{rapport.repartition.length > 0 && (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Repartition des emotions</Text>
<Text style={styles.sectionTitle}>Répartition des émotions</Text>
{/* Camembert (donut) — équivalent mobile du graphique Recharts du web */}
<EmotionPieChart
data={rapport.repartition.map((item) => ({
nom: item.nom,
couleur: item.couleur,
count: item.count,
pourcentage: item.pourcentage,
}))}
/>
<Text style={styles.subSectionTitle}>Détail par émotion</Text>
{rapport.repartition.map((item) => (
<View key={item.nom} style={styles.repartitionItem}>
<View style={styles.repartitionHeader}>
@@ -92,7 +105,7 @@ export default function ReportsScreen() {
) : (
<View style={styles.empty}>
<Ionicons name="bar-chart-outline" size={48} color={Colors.gray[300]} />
<Text style={styles.emptyText}>Pas de donnees pour cette periode</Text>
<Text style={styles.emptyText}>Pas de données pour cette période</Text>
</View>
)}
</ScrollView>
@@ -120,6 +133,7 @@ const styles = StyleSheet.create({
periodInfoText: { fontSize: 13, color: Colors.gray[400], textAlign: 'center' },
section: { marginBottom: 24 },
sectionTitle: { fontSize: 18, fontWeight: '700', color: Colors.black, marginBottom: 16 },
subSectionTitle: { fontSize: 14, fontWeight: '700', color: Colors.gray[600], marginBottom: 12, marginTop: 4 },
repartitionItem: { marginBottom: 16 },
repartitionHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 6 },
colorDot: { width: 10, height: 10, borderRadius: 5, marginRight: 8 },
+5 -5
View File
@@ -9,10 +9,10 @@ export default function ProfileScreen() {
const { user, logout } = useAuthStore();
const handleLogout = () => {
Alert.alert('Deconnexion', 'Voulez-vous vous deconnecter ?', [
Alert.alert('Déconnexion', 'Voulez-vous vous déconnecter ?', [
{ text: 'Annuler', style: 'cancel' },
{
text: 'Se deconnecter',
text: 'Se déconnecter',
style: 'destructive',
onPress: async () => {
await logout();
@@ -44,14 +44,14 @@ export default function ProfileScreen() {
<View style={styles.menuCard}>
<MenuItem icon="person-outline" label="Informations personnelles" />
<MenuItem icon="lock-closed-outline" label="Changer le mot de passe" />
<MenuItem icon="shield-checkmark-outline" label="Donnees personnelles (RGPD)" />
<MenuItem icon="shield-checkmark-outline" label="Données personnelles (RGPD)" />
</View>
</View>
<View style={styles.menuSection}>
<Text style={styles.sectionTitle}>Application</Text>
<View style={styles.menuCard}>
<MenuItem icon="information-circle-outline" label="A propos de CESIZen" />
<MenuItem icon="information-circle-outline" label="À propos de CESIZen" />
<MenuItem icon="document-text-outline" label="Conditions d'utilisation" />
</View>
</View>
@@ -59,7 +59,7 @@ export default function ProfileScreen() {
{/* Logout */}
<TouchableOpacity style={styles.logoutBtn} onPress={handleLogout}>
<Ionicons name="log-out-outline" size={20} color={Colors.error} />
<Text style={styles.logoutText}>Se deconnecter</Text>
<Text style={styles.logoutText}>Se déconnecter</Text>
</TouchableOpacity>
<Text style={styles.version}>CESIZen v1.0.0</Text>