mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 20:51:53 +00:00
R4 avait délibérément fermé le mobile au Technicien. Le référent demande l'ouverture à tous les rôles avec les mêmes droits qu'au web — nouvelle release R6 (R4 est close, R4.1/4.2/4.3 déjà pris par le socle mobile technicien d'origine, pas de réouverture ni de collision de numérotation). Maquette (docs/02-design/maquettes/maquette-mobile-tous-roles.html, 7 écrans) validée par le référent : D1 barre d'onglets adaptative par rôle (Technicien/Technicien limité inchangés + onglet Menu ajouté) ; D2 le Menu reprend à l'identique les 4 groupes du web, même matrice de permissions ; D3 groupe sans lien visible masqué en entier ; D4 le mobile porte les actions courantes par famille, pas les flux de gestion les plus denses ; D5 aucune logique de permission propre au mobile. R6.1 — socle : usePermissions() mobile calqué sur le web ; barre d'onglets adaptative (ongletsVisibles par rôle, href:null masque sans retirer du navigateur) ; Accueil (dashboard réel pour Administrateur/Gestionnaire/ Dispatcher/Vue seule, aucun chiffre inventé) ; OT (liste complète, viewOther déjà géré serveur, réutilise la fiche OT R4 telle quelle) ; Menu (groupes filtrés, vide masqué) ; Demandes (PanneauDemandes — un seul composant pour tous les rôles : création/suivi Demandeur, approbation/ rejet Gestionnaire/Dispatcher/Administrateur, lecture Vue seule). Familles pas encore portées : écran « à venir » plutôt qu'un lien mort. Typecheck propre, 17 tests Jest, lint 5/5 paquets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import { Tabs } from 'expo-router';
|
|
import { Text, type ColorValue } from 'react-native';
|
|
import { useAssets, useReferenceValues } from '@/api/exploitation';
|
|
import { useMe } from '@/auth/session';
|
|
import { ongletsVisibles } from '@/auth/roles';
|
|
import { usePermissions } from '@/auth/use-permissions';
|
|
import { useFile } from '@/file/store';
|
|
import { useTokens } from '@/theme/tokens';
|
|
|
|
/** Barre d'onglets ADAPTATIVE (maquette « mobile ouvert à tous les rôles »,
|
|
* D1) : le Technicien/Technicien limité gardent exactement leurs 4 onglets
|
|
* terrain R4, inchangés. Les autres rôles reçoivent Accueil/OT/Menu (ou une
|
|
* variante plus étroite — Demandeur, Vue seule — selon leurs droits,
|
|
* `ongletsVisibles`). Tous les écrans sont toujours déclarés : `href: null`
|
|
* masque un onglet de la barre sans le retirer du navigateur (le Menu peut
|
|
* toujours y pousser directement). */
|
|
|
|
function Pic({ glyphe, couleur }: { glyphe: string; couleur: ColorValue }) {
|
|
return <Text style={{ fontSize: 17, color: couleur, lineHeight: 20 }}>{glyphe}</Text>;
|
|
}
|
|
|
|
export default function CoquilleTabs() {
|
|
const t = useTokens();
|
|
const file = useFile();
|
|
const conflits = file.some((s) => s.statut === 'CONFLIT');
|
|
const { data: me } = useMe();
|
|
const { can } = usePermissions();
|
|
const visibles = ongletsVisibles(me?.role.name);
|
|
// D1 « lecture locale » : le parc (scan D4) et les référentiels du bilan
|
|
// se préchargent dès l'entrée — le sous-sol n'attend pas qu'on y pense.
|
|
// Le Demandeur n'a pas ASSETS.view (seulement /assets/options) : inutile
|
|
// d'appeler /assets pour lui, l'API répondrait 403.
|
|
const peutAssets = can('ASSETS', 'view');
|
|
useAssets({ enabled: peutAssets });
|
|
useReferenceValues({ enabled: peutAssets });
|
|
|
|
const cacher = (nom: string) => (visibles.has(nom) ? undefined : null);
|
|
|
|
return (
|
|
<Tabs
|
|
screenOptions={{
|
|
headerShown: false,
|
|
tabBarActiveTintColor: t.primaire,
|
|
tabBarInactiveTintColor: t.encre3,
|
|
tabBarStyle: { backgroundColor: t.surface, borderTopColor: t.bordure },
|
|
tabBarLabelStyle: { fontFamily: 'Manrope_700Bold', fontSize: 10 },
|
|
sceneStyle: { backgroundColor: t.fond },
|
|
}}
|
|
>
|
|
<Tabs.Screen
|
|
name="journee"
|
|
options={{
|
|
href: cacher('journee'),
|
|
title: 'Ma journée',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="☰" couleur={color} />,
|
|
}}
|
|
/>
|
|
<Tabs.Screen
|
|
name="scanner"
|
|
options={{
|
|
href: cacher('scanner'),
|
|
title: 'Scanner',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="▣" couleur={color} />,
|
|
}}
|
|
/>
|
|
<Tabs.Screen
|
|
name="preventif"
|
|
options={{
|
|
href: cacher('preventif'),
|
|
title: 'Préventif',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="✓" couleur={color} />,
|
|
}}
|
|
/>
|
|
<Tabs.Screen
|
|
name="synchro"
|
|
options={{
|
|
href: cacher('synchro'),
|
|
title: 'Synchro',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="⇅" couleur={color} />,
|
|
tabBarBadge: file.length || undefined,
|
|
tabBarBadgeStyle: {
|
|
backgroundColor: conflits ? t.prioBloque : t.stAttente,
|
|
color: '#fff',
|
|
fontFamily: 'Manrope_800ExtraBold',
|
|
fontSize: 10,
|
|
},
|
|
}}
|
|
/>
|
|
<Tabs.Screen
|
|
name="accueil"
|
|
options={{
|
|
href: cacher('accueil'),
|
|
title: 'Accueil',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="⌂" couleur={color} />,
|
|
}}
|
|
/>
|
|
<Tabs.Screen
|
|
name="ot"
|
|
options={{
|
|
href: cacher('ot'),
|
|
title: 'OT',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="☰" couleur={color} />,
|
|
}}
|
|
/>
|
|
<Tabs.Screen
|
|
name="nouvelle-demande"
|
|
options={{
|
|
href: cacher('nouvelle-demande'),
|
|
title: 'Nouvelle demande',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="+" couleur={color} />,
|
|
}}
|
|
/>
|
|
<Tabs.Screen
|
|
name="menu"
|
|
options={{
|
|
href: cacher('menu'),
|
|
title: 'Menu',
|
|
tabBarIcon: ({ color }) => <Pic glyphe="☷" couleur={color} />,
|
|
}}
|
|
/>
|
|
</Tabs>
|
|
);
|
|
}
|