mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
feat(r6): R6.5 — Assistant mobile (chat sourcé + dictée)
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>
This commit is contained in:
52
apps/mobile/app/bibliotheque/[id].tsx
Normal file
52
apps/mobile/app/bibliotheque/[id].tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { useLocalSearchParams } from 'expo-router';
|
||||
import { ScrollView, Text } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useDocuments } from '@/api/ressources';
|
||||
import { Carte, EnteteFiche, LigneInfo } from '@/composants/ui';
|
||||
import { useTokens } from '@/theme/tokens';
|
||||
|
||||
const KIND_LABEL: Record<string, string> = {
|
||||
NOTICE: 'Notice',
|
||||
CERTIFICATE: 'Certificat',
|
||||
PHOTO: 'Photo',
|
||||
OTHER: 'Autre',
|
||||
};
|
||||
|
||||
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`;
|
||||
}
|
||||
|
||||
/** Fiche document — R6.5. Métadonnées seules (D2, comme la bibliothèque
|
||||
* R6.3) : l'ouverture du fichier lui-même demande `expo-sharing`, pas
|
||||
* encore une dépendance du projet — ouvrir depuis le web pour l'instant.
|
||||
* Destination de « Voir le document » depuis les sources de l'Assistant. */
|
||||
export default function PageFicheDocument() {
|
||||
const t = useTokens();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const { data: documents } = useDocuments();
|
||||
const doc = (documents ?? []).find((d) => d.id === id);
|
||||
if (!doc) return null;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: t.fond }} edges={['top']}>
|
||||
<ScrollView contentContainerStyle={{ padding: 14, gap: 10 }}>
|
||||
<EnteteFiche titre={doc.fileName} />
|
||||
<Carte titre="Document">
|
||||
<LigneInfo nom="Type" valeur={KIND_LABEL[doc.kind] ?? doc.kind} />
|
||||
<LigneInfo nom="Taille" valeur={taille(doc.size)} />
|
||||
<LigneInfo nom="Rattaché à" valeur={doc.assetReference ?? doc.workOrderReference ?? '—'} />
|
||||
<LigneInfo nom="Ajouté par" valeur={doc.uploadedByName ?? '—'} />
|
||||
<LigneInfo
|
||||
nom="Ajouté le"
|
||||
valeur={new Intl.DateTimeFormat('fr-FR', { dateStyle: 'long' }).format(new Date(doc.createdAt))}
|
||||
/>
|
||||
</Carte>
|
||||
<Text style={{ color: t.encre3, fontFamily: 'Manrope_400Regular', fontSize: 11.5, textAlign: 'center' }}>
|
||||
Ouvrir le fichier se fait depuis le web pour l'instant.
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FlatList, Text, View } from 'react-native';
|
||||
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';
|
||||
@@ -48,7 +49,9 @@ export default function PageBibliotheque() {
|
||||
</Text>
|
||||
}
|
||||
renderItem={({ item: d }) => (
|
||||
<View
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
onPress={() => router.push(`/bibliotheque/${d.id}`)}
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
@@ -71,7 +74,7 @@ export default function PageBibliotheque() {
|
||||
.join(' · ')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Pressable>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user