mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 20:51:53 +00:00
Maquette dédiée validée (docs/02-design/maquettes/maquette-mobile-assistant.html, 3 écrans) — la seule sous-étape de R6 à avoir eu son propre tour de design-first plutôt qu'une exécution directe de la maquette R6 initiale, un chat sourcé étant un patron d'écran neuf. D1 un échange à la fois ; D2 "Voir le document" → métadonnées seules (R6.3), "Ouvrir l'OT" → fiche R4 ; D3 refus chiffré + avertissement permanents, identiques au web ; D4 Assistant rejoint Pilotage pour tout rôle avec WORK_ORDERS.view ; D5 (ajoutée en revue) — la question peut être tapée OU dictée, même pipeline que la dictée déjà livrée en clôture (ADR-004 §5, faster-whisper local, audio jamais persisté), la transcription remplit le champ, jamais d'envoi automatique. api/assistant.ts (useAskAssistant, 503 géré comme le web). Écran Assistant : chat un échange à la fois, citations numérotées, sources avec extrait exact, refus honnête chiffré + Reformuler. Micro dans la barre de saisie : réutilise telle quelle la mécanique de cloture.tsx (useAudioRecorder, setAudioModeAsync, useTranscription, purge quoi qu'il arrive) — pipeline déjà recetté sur iPhone physique (01/08), aucun nouveau bug à découvrir. Fiche document (bibliotheque/[id].tsx, nouveau) — la liste R6.3 y mène aussi désormais. Menu : Assistant route réellement — le Menu R6 n'a plus d'entrée "à venir" dans les 4 groupes (Catégories exceptée, admin, hors périmètre mobile). R6 fonctionnellement complet (Exploitation/Parc/Ressources/Pilotage, Assistant inclus, pour tous les rôles) — reste une recette complète avant de considérer R6 close au sens R0→R5. Typecheck propre, 17 tests Jest, lint 5/5 paquets. Contrat non touché. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
84 lines
3.3 KiB
TypeScript
84 lines
3.3 KiB
TypeScript
import { router } from 'expo-router';
|
|
import { FlatList, Pressable, Text, View } from 'react-native';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { useDocuments } from '@/api/ressources';
|
|
import { EnteteFiche } from '@/composants/ui';
|
|
import { useTokens } from '@/theme/tokens';
|
|
|
|
const ICONE_TYPE: Record<string, string> = {
|
|
NOTICE: '📘',
|
|
CERTIFICATE: '📜',
|
|
PHOTO: '🖼',
|
|
OTHER: '📄',
|
|
};
|
|
|
|
function taille(octets: number): string {
|
|
if (octets < 1024) return `${octets} o`;
|
|
if (octets < 1024 * 1024) return `${Math.round(octets / 1024)} Ko`;
|
|
return `${(octets / (1024 * 1024)).toFixed(1)} Mo`;
|
|
}
|
|
|
|
/** Fichiers — R6.3 (Ressources). Consultation des métadonnées seulement
|
|
* (D4) : l'ouverture/téléchargement d'un document sur mobile demande un
|
|
* flux authentifié (l'API le streame, MinIO n'est jamais exposé) suivi
|
|
* d'un partage natif — nécessite `expo-sharing`, pas encore une dépendance
|
|
* du projet ; ajoutée dans une prochaine étape plutôt que d'introduire une
|
|
* nouvelle dépendance native (et donc un nouveau build) dans cette passe.
|
|
* En attendant : ouvrir depuis le web. */
|
|
export default function PageBibliotheque() {
|
|
const t = useTokens();
|
|
const { data: documents } = useDocuments();
|
|
const fichiers = [...(documents ?? [])].sort(
|
|
(a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt),
|
|
);
|
|
|
|
return (
|
|
<SafeAreaView style={{ flex: 1, backgroundColor: t.fond }} edges={['top']}>
|
|
<View style={{ flex: 1, padding: 14, gap: 10 }}>
|
|
<EnteteFiche titre="Fichiers" />
|
|
<Text style={{ fontFamily: 'Manrope_600SemiBold', fontSize: 11.5, color: t.encre3 }}>
|
|
Consultation seulement — l'ouverture se fait depuis le web pour l'instant.
|
|
</Text>
|
|
<FlatList
|
|
data={fichiers}
|
|
keyExtractor={(d) => d.id}
|
|
contentContainerStyle={{ gap: 8, paddingBottom: 12 }}
|
|
ListEmptyComponent={
|
|
<Text style={{ color: t.encre3, fontFamily: 'Manrope_600SemiBold', padding: 16, textAlign: 'center' }}>
|
|
Aucun document accessible.
|
|
</Text>
|
|
}
|
|
renderItem={({ item: d }) => (
|
|
<Pressable
|
|
accessibilityRole="button"
|
|
onPress={() => router.push(`/bibliotheque/${d.id}`)}
|
|
style={{
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 10,
|
|
backgroundColor: t.surface,
|
|
borderColor: t.bordure,
|
|
borderWidth: 1,
|
|
borderRadius: 12,
|
|
padding: 12,
|
|
}}
|
|
>
|
|
<Text style={{ fontSize: 20 }}>{ICONE_TYPE[d.kind] ?? '📄'}</Text>
|
|
<View style={{ flex: 1, gap: 1 }}>
|
|
<Text numberOfLines={1} style={{ fontFamily: 'Manrope_700Bold', fontSize: 13, color: t.encre }}>
|
|
{d.fileName}
|
|
</Text>
|
|
<Text style={{ fontFamily: 'Manrope_400Regular', fontSize: 11.5, color: t.encre2 }}>
|
|
{[d.assetReference && `Asc. ${d.assetReference}`, d.workOrderReference, taille(d.size)]
|
|
.filter(Boolean)
|
|
.join(' · ')}
|
|
</Text>
|
|
</View>
|
|
</Pressable>
|
|
)}
|
|
/>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|