From c2ac668e9613a0ba4ab240949054dc25f2e57f16 Mon Sep 17 00:00:00 2001 From: m Date: Sun, 12 Jul 2026 15:59:59 +0200 Subject: [PATCH] select image and generate pdf --- mobile/app/pending-review.tsx | 162 ++++++++++++++++++---------------- 1 file changed, 87 insertions(+), 75 deletions(-) diff --git a/mobile/app/pending-review.tsx b/mobile/app/pending-review.tsx index bf3a2ef..97ab4df 100644 --- a/mobile/app/pending-review.tsx +++ b/mobile/app/pending-review.tsx @@ -1,11 +1,15 @@ import React, { useState, useCallback } from 'react'; -import { View, Text, StyleSheet, FlatList, TouchableOpacity, Image, TextInput, Alert, ActivityIndicator } from 'react-native'; +import { View, Text, StyleSheet, FlatList, TouchableOpacity, Image, TextInput, Alert, ActivityIndicator, Modal, Dimensions } from 'react-native'; import { useRoute, useNavigation, RouteProp } from '@react-navigation/native'; import { useBatchStore } from '../hooks/useBatchStore'; import { usePdfGeneration } from '../hooks/usePdfGeneration'; import { useUpload } from '../hooks/useUpload'; import { CapturedPhoto } from '../types'; +const NUM_COLUMNS = 3; +const SCREEN_WIDTH = Dimensions.get('window').width; +const ITEM_SIZE = (SCREEN_WIDTH - 16 * 2 - (NUM_COLUMNS - 1) * 6) / NUM_COLUMNS; + type PendingReviewRouteParams = { PendingReview: { batchId: string; photoIds: string[] }; }; @@ -24,26 +28,21 @@ export function PendingReviewScreen() { ); const [photos, setPhotos] = useState(initialPhotos); + const [selectedSet, setSelectedSet] = useState>(new Set(initialPhotos.map((p) => p.id))); const [batchTags, setBatchTags] = useState(batch?.tags ?? []); const [tagInput, setTagInput] = useState(''); + const [previewUri, setPreviewUri] = useState(null); - const moveUp = useCallback((index: number) => { - if (index === 0) return; - setPhotos((prev) => { - const next = [...prev]; - [next[index - 1], next[index]] = [next[index], next[index - 1]]; + const orderedPhotos = photos.filter((p) => selectedSet.has(p.id)); + + const togglePhoto = (id: string) => { + setSelectedSet((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); return next; }); - }, []); - - const moveDown = useCallback((index: number) => { - if (index >= photos.length - 1) return; - setPhotos((prev) => { - const next = [...prev]; - [next[index], next[index + 1]] = [next[index + 1], next[index]]; - return next; - }); - }, [photos.length]); + }; const handleAddTag = () => { const tag = tagInput.trim().toLowerCase(); @@ -59,9 +58,12 @@ export function PendingReviewScreen() { }; const handleFinalize = async () => { - if (photos.length === 0) return; + if (orderedPhotos.length === 0) { + Alert.alert('Aucune photo', 'Sélectionnez au moins une photo'); + return; + } - const pdfUri = await generatePdf(photos.map((p) => ({ uri: p.uri }))); + const pdfUri = await generatePdf(orderedPhotos.map((p) => ({ uri: p.uri }))); if (!pdfUri) { Alert.alert('Erreur', 'Échec de la génération du PDF'); return; @@ -103,39 +105,44 @@ export function PendingReviewScreen() { ); } + const renderPhoto = ({ item, index }: { item: CapturedPhoto; index: number }) => { + const isSelected = selectedSet.has(item.id); + const pageNumber = isSelected + ? orderedPhotos.findIndex((p) => p.id === item.id) + 1 + : null; + + return ( + togglePhoto(item.id)} + onLongPress={() => setPreviewUri(item.uri)} + > + + {isSelected && ( + + {pageNumber} + + )} + + ); + }; + return ( Réorganiser les photos - {photos.length} photo{photos.length > 1 ? 's' : ''} + + {orderedPhotos.length}/{photos.length} sélectionnée{orderedPhotos.length > 1 ? 's' : ''} + item.id} - contentContainerStyle={styles.list} - renderItem={({ item, index }) => ( - - - #{index + 1} - - moveUp(index)} - disabled={index === 0} - > - - - = photos.length - 1 && styles.arrowDisabled]} - onPress={() => moveDown(index)} - disabled={index >= photos.length - 1} - > - - - - - )} + contentContainerStyle={styles.grid} + columnWrapperStyle={styles.gridRow} + renderItem={renderPhoto} /> @@ -181,6 +188,14 @@ export function PendingReviewScreen() { )} + + setPreviewUri(null)}> + setPreviewUri(null)}> + {previewUri && ( + + )} + + ); } @@ -213,47 +228,34 @@ const styles = StyleSheet.create({ fontSize: 14, color: '#666', }, - list: { + grid: { padding: 16, }, - photoRow: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: 12, - backgroundColor: '#f9f9f9', - borderRadius: 8, - padding: 8, + gridRow: { + gap: 6, + }, + gridItem: { + width: ITEM_SIZE, + height: ITEM_SIZE, + borderRadius: 6, + overflow: 'hidden', + backgroundColor: '#f0f0f0', + marginBottom: 6, }, thumb: { - width: 60, - height: 60, - borderRadius: 6, - backgroundColor: '#e0e0e0', + width: '100%', + height: '100%', }, - photoIndex: { - marginLeft: 12, - fontSize: 16, - fontWeight: '600', - color: '#333', - flex: 1, - }, - arrows: { - gap: 4, - }, - arrowBtn: { - width: 36, - height: 28, - borderRadius: 4, - backgroundColor: '#e0e0e0', + selectedOverlay: { + ...StyleSheet.absoluteFill, + backgroundColor: 'rgba(25,118,210,0.35)', justifyContent: 'center', alignItems: 'center', }, - arrowDisabled: { - opacity: 0.3, - }, - arrowText: { - fontSize: 14, - color: '#333', + pageNumber: { + color: '#fff', + fontSize: 28, + fontWeight: '800', }, tagSection: { padding: 16, @@ -336,4 +338,14 @@ const styles = StyleSheet.create({ fontSize: 16, fontWeight: '600', }, + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0,0,0,0.9)', + justifyContent: 'center', + alignItems: 'center', + }, + previewImage: { + width: SCREEN_WIDTH * 0.95, + height: '80%', + }, });