fix(mobile): déconnexion accessible depuis tous les onglets

Bug remonté par le référent : une fois authentifié, impossible de revenir en
arrière ou de se déconnecter. Seule « Ma journée » portait ce contrôle
(onLongPress non découvrable) ; les 3 autres onglets n'avaient rien.

EnteteTabs() factorise l'entête (composants/ui.tsx) : tap simple + confirmation
Alert, rôle affiché = celui du compte connecté (plus de libellé "Technicien"
figé — le mobile n'est plus réservé aux techniciens). Réutilisée dans les 4
onglets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-08-01 22:09:17 +01:00
parent 4af3f5668a
commit 3e9e708116
5 changed files with 84 additions and 58 deletions

View File

@@ -1,7 +1,8 @@
import { router } from 'expo-router';
import { useState, type ReactNode } from 'react';
import { Modal, Pressable, ScrollView, Text, View } from 'react-native';
import { Alert, Modal, Pressable, ScrollView, Text, View } from 'react-native';
import { WORK_ORDER_STATUS_LABELS, type WorkOrderStatus } from '@siop/shared';
import { useHorsLigne, useLogout, useMe } from '@/auth/session';
import { useTokens, type Tokens } from '@/theme/tokens';
/** Briques d'écran de la maquette R4 — cartes, chips, boutons, sélecteur. */
@@ -263,3 +264,76 @@ export function ChoixTel({
</View>
);
}
/** Entête commune à tous les onglets (D'où qu'on parte, on peut se
* déconnecter — trouvée manquante en recette : seule « Ma journée » avait
* ce contrôle, ailleurs l'utilisateur se sentait bloqué). Le rôle affiché
* est CELUI du compte connecté, pas un libellé figé — le mobile n'est plus
* réservé aux techniciens (voir décision élargissant le périmètre). */
export function EnteteTabs() {
const t = useTokens();
const horsLigne = useHorsLigne();
const { data: me } = useMe();
const logout = useLogout();
const seDeconnecter = () => {
Alert.alert('Se déconnecter ?', me?.displayName ? `Compte : ${me.displayName}` : undefined, [
{ text: 'Annuler', style: 'cancel' },
{
text: 'Se déconnecter',
style: 'destructive',
onPress: () => void logout().then(() => router.replace('/connexion')),
},
]);
};
return (
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<Text style={{ color: t.safran, fontSize: 15 }}></Text>
<Text style={{ fontFamily: 'Manrope_800ExtraBold', fontSize: 17, color: t.encre }}>SIOP</Text>
<Text style={{ fontFamily: 'Manrope_600SemiBold', fontSize: 10.5, color: t.encre3 }}>
{me?.role.name ?? '·'}
</Text>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, marginLeft: 'auto' }}>
<Text
accessibilityLabel="État de synchronisation"
style={{
fontFamily: 'Manrope_700Bold',
fontSize: 10.5,
color: horsLigne ? t.stAttente : t.stTermine,
backgroundColor: horsLigne ? t.stAttenteFond : t.stTermineFond,
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 999,
overflow: 'hidden',
}}
>
{horsLigne ? 'Hors-ligne' : 'Synchro à jour'}
</Text>
<Pressable
accessibilityRole="button"
accessibilityLabel="Compte et déconnexion"
onPress={seDeconnecter}
hitSlop={8}
style={{
width: 26,
height: 26,
borderRadius: 13,
backgroundColor: t.stEncours,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text style={{ color: '#fff', fontFamily: 'Manrope_700Bold', fontSize: 10 }}>
{(me?.displayName ?? '·')
.split(' ')
.map((m) => m[0])
.join('')
.slice(0, 2)
.toUpperCase()}
</Text>
</Pressable>
</View>
</View>
);
}