diff --git a/backend/internal/handler/files.go b/backend/internal/handler/files.go index 0794065..4fc5dde 100644 --- a/backend/internal/handler/files.go +++ b/backend/internal/handler/files.go @@ -77,6 +77,7 @@ func (h *FileHandler) List(c *gin.Context) { Size: f.Size, Tags: []string{}, CreatedAt: f.CreatedAt, + OcrText: f.OcrText, UpdatedAt: f.UpdatedAt, MimeType: f.MimeType, } diff --git a/mobile/app/index.tsx b/mobile/app/index.tsx index fd7c18a..94d6108 100644 --- a/mobile/app/index.tsx +++ b/mobile/app/index.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { View, FlatList, StyleSheet, TouchableOpacity, Text, Dimensions, Image, ActivityIndicator } from 'react-native'; +import React, { useState, useMemo } from 'react'; +import { View, FlatList, StyleSheet, TouchableOpacity, Text, Dimensions, Image, ActivityIndicator, TextInput } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useFiles, useFileImage } from '../hooks/useFiles'; @@ -13,7 +13,6 @@ type RootStackParamList = { Home: undefined; Upload: undefined; Scan: undefined; - Search: undefined; }; type NavigationProp = NativeStackNavigationProp; @@ -51,9 +50,55 @@ function formatDateLabel(key: string): string { return key.charAt(0).toUpperCase() + key.slice(1); } +function FileGridItem({ file }: { file: FileItem }) { + const { data, isLoading } = useFileImage(file.id); + + const uri = data?.data?.url; + + return ( + + {isLoading ? ( + + + + ) : uri ? ( + + ) : ( + + PDF + + )} + {file.name} + + ); +} + +function matchesQuery(file: FileItem, query: string): boolean { + if (!query) return true; + const q = query.toLowerCase(); + if (file.name.toLowerCase().includes(q)) return true; + if (file.ocrText?.toLowerCase().includes(q)) return true; + if (file.tags?.some((t) => { + const tagName = typeof t === 'string' ? t : t.name; + return tagName?.toLowerCase().includes(q); + })) return true; + return false; +} + export function HomeScreen() { const navigation = useNavigation(); const { data, isLoading, error } = useFiles(); + const [searchQuery, setSearchQuery] = useState(''); + + const files = data?.data ?? []; + + const filteredFiles = useMemo( + () => searchQuery.trim() ? files.filter((f) => matchesQuery(f, searchQuery)) : files, + [files, searchQuery] + ); + + const groups = groupByDate(filteredFiles); + const groupKeys = Object.keys(groups); if (isLoading) { return ( @@ -71,16 +116,36 @@ export function HomeScreen() { ); } - const files = data?.data ?? []; - const groups = groupByDate(files); - const groupKeys = Object.keys(groups); - return ( + + + {searchQuery.length > 0 && ( + setSearchQuery('')} style={styles.clearBtn}> + + + )} + + item} contentContainerStyle={styles.list} + ListEmptyComponent={ + + + {searchQuery ? 'Aucun résultat' : 'Aucun fichier'} + + + } renderItem={({ item: dateKey }) => { const groupFiles = groups[dateKey]; return ( @@ -110,41 +175,11 @@ export function HomeScreen() { > Scan - - navigation.navigate('Search')} - > - Recherche - ); } -function FileGridItem({ file }: { file: FileItem }) { - const { data, isLoading } = useFileImage(file.id); - - const uri = data?.data?.url; - - return ( - - {isLoading ? ( - - - - ) : uri ? ( - - ) : ( - - PDF - - )} - {file.name} - - ); -} - const styles = StyleSheet.create({ container: { flex: 1, @@ -159,10 +194,43 @@ const styles = StyleSheet.create({ fontSize: 16, color: '#666', }, + searchBar: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 16, + paddingTop: 12, + paddingBottom: 8, + }, + searchInput: { + flex: 1, + backgroundColor: '#fff', + borderRadius: 10, + paddingHorizontal: 14, + paddingVertical: 10, + fontSize: 15, + borderWidth: 1, + borderColor: '#e0e0e0', + }, + clearBtn: { + marginLeft: 8, + padding: 6, + }, + clearText: { + fontSize: 18, + color: '#999', + }, list: { padding: 16, paddingBottom: 80, }, + empty: { + paddingVertical: 60, + alignItems: 'center', + }, + emptyText: { + fontSize: 16, + color: '#999', + }, section: { marginBottom: 24, },