import { Ionicons } from '@expo/vector-icons'; import { useRouter } from 'expo-router'; import { useShareIntentContext } from 'expo-share-intent'; import { useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, Alert, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Customer, fetchCustomers, fetchPlants, fetchProjects, Plant, Project, uploadCustomerFile, uploadPlantFile, uploadProjectFile, } from '@/src/lib/api'; import { useAuth } from '@/src/providers/auth-provider'; type TargetType = 'project' | 'customer' | 'plant'; type UploadTarget = Project | Customer | Plant; const TARGET_TYPES: { key: TargetType; label: string; plural: string; icon: keyof typeof Ionicons.glyphMap }[] = [ { key: 'project', label: 'Projekt', plural: 'Projekte', icon: 'folder-outline' }, { key: 'customer', label: 'Kunde', plural: 'Kunden', icon: 'people-outline' }, { key: 'plant', label: 'Objekt', plural: 'Objekte', icon: 'business-outline' }, ]; export default function ShareUploadScreen() { const router = useRouter(); const { token } = useAuth(); const { shareIntent, resetShareIntent } = useShareIntentContext(); const [targetType, setTargetType] = useState(null); const [targets, setTargets] = useState([]); const [search, setSearch] = useState(''); const [isLoading, setIsLoading] = useState(false); const [isUploading, setIsUploading] = useState(false); const [uploadedCount, setUploadedCount] = useState(0); const [loadError, setLoadError] = useState(null); const files = shareIntent.files || []; const selectedType = TARGET_TYPES.find((type) => type.key === targetType); useEffect(() => { if (!targetType || !token) return; let isActive = true; setIsLoading(true); setLoadError(null); setSearch(''); const request = targetType === 'project' ? fetchProjects(token) : targetType === 'customer' ? fetchCustomers(token) : fetchPlants(token); void request .then((rows) => { if (isActive) setTargets(rows.sort((a, b) => a.name.localeCompare(b.name, 'de'))); }) .catch((error) => { if (isActive) setLoadError(error instanceof Error ? error.message : 'Die Ziele konnten nicht geladen werden.'); }) .finally(() => { if (isActive) setIsLoading(false); }); return () => { isActive = false; }; }, [targetType, token]); const filteredTargets = useMemo(() => { const term = search.trim().toLocaleLowerCase('de'); if (!term) return targets; return targets.filter((target) => { const number = 'projectNumber' in target ? target.projectNumber : 'customerNumber' in target ? target.customerNumber : null; return `${target.name} ${number || ''}`.toLocaleLowerCase('de').includes(term); }); }, [search, targets]); const close = () => { resetShareIntent(); router.replace('/'); }; const confirmCancel = () => { Alert.alert('Upload abbrechen?', 'Die geteilten Dateien werden nicht hochgeladen.', [ { text: 'Weiter auswählen', style: 'cancel' }, { text: 'Abbrechen', style: 'destructive', onPress: close }, ]); }; const uploadTo = async (target: UploadTarget) => { if (!token || !targetType || files.length === 0 || isUploading) return; setIsUploading(true); setUploadedCount(0); try { for (const [index, file] of files.entries()) { const common = { uri: file.path, filename: file.fileName || `Geteilte Datei ${index + 1}`, mimeType: file.mimeType || undefined, }; if (targetType === 'project') { await uploadProjectFile(token, { ...common, projectId: target.id }); } else if (targetType === 'customer') { await uploadCustomerFile(token, { ...common, customerId: target.id }); } else { await uploadPlantFile(token, { ...common, plantId: target.id }); } setUploadedCount(index + 1); } resetShareIntent(); Alert.alert('Upload abgeschlossen', `${files.length} ${files.length === 1 ? 'Datei wurde' : 'Dateien wurden'} zu „${target.name}“ hochgeladen.`, [ { text: 'Fertig', onPress: () => router.replace('/') }, ]); } catch (error) { Alert.alert('Upload fehlgeschlagen', error instanceof Error ? error.message : 'Die Datei konnte nicht hochgeladen werden.'); } finally { setIsUploading(false); } }; if (files.length === 0) { return ( Keine Dateien gefunden Teile eine Datei oder ein Foto erneut mit FEDEO. Zurück zu FEDEO ); } return ( {files.length} {files.length === 1 ? 'Datei' : 'Dateien'} ausgewählt {files.map((file) => file.fileName).join(', ')} {!targetType ? ( <> Wohin möchtest du hochladen? Wähle zuerst die Art des Ziels aus. {TARGET_TYPES.map((type) => ( setTargetType(type.key)}> {type.label} ))} ) : ( <> setTargetType(null)} disabled={isUploading}> Andere Zielart wählen {selectedType?.label} auswählen {isLoading ? ( ) : loadError ? ( {loadError} ) : filteredTargets.length === 0 ? ( Keine passenden {selectedType?.plural.toLocaleLowerCase('de')} gefunden. ) : ( {filteredTargets.map((target) => { const number = 'projectNumber' in target ? target.projectNumber : 'customerNumber' in target ? target.customerNumber : null; return ( void uploadTo(target)} disabled={isUploading}> {target.name} {number ? {String(number)} : null} ); })} )} )} {isUploading ? ( Upload läuft: {uploadedCount} von {files.length} ) : null} Abbrechen ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: '#f9fafb' }, content: { padding: 20, paddingBottom: 36, gap: 16 }, flex: { flex: 1 }, fileSummary: { flexDirection: 'row', gap: 12, alignItems: 'center', padding: 16, borderRadius: 14, backgroundColor: '#eff6ff' }, summaryTitle: { fontSize: 16, fontWeight: '700', color: '#1e3a8a' }, summaryText: { marginTop: 3, fontSize: 13, color: '#3b4f78' }, heading: { marginTop: 8, fontSize: 23, fontWeight: '800', color: '#111827' }, description: { marginTop: -10, fontSize: 15, color: '#6b7280' }, typeGrid: { gap: 12 }, typeCard: { flexDirection: 'row', alignItems: 'center', gap: 14, padding: 16, borderWidth: 1, borderColor: '#e5e7eb', borderRadius: 14, backgroundColor: '#fff' }, iconCircle: { width: 46, height: 46, borderRadius: 23, alignItems: 'center', justifyContent: 'center', backgroundColor: '#eff6ff' }, typeLabel: { flex: 1, fontSize: 17, fontWeight: '700', color: '#111827' }, backLink: { flexDirection: 'row', alignItems: 'center', alignSelf: 'flex-start', marginTop: 2 }, backLinkText: { fontSize: 14, fontWeight: '600', color: '#2563eb' }, searchInput: { height: 48, paddingHorizontal: 14, borderWidth: 1, borderColor: '#d1d5db', borderRadius: 12, backgroundColor: '#fff', fontSize: 16, color: '#111827' }, loader: { marginVertical: 32 }, errorText: { padding: 16, borderRadius: 12, backgroundColor: '#fef2f2', color: '#b91c1c' }, emptyListText: { paddingVertical: 28, textAlign: 'center', color: '#6b7280' }, targetList: { overflow: 'hidden', borderWidth: 1, borderColor: '#e5e7eb', borderRadius: 14, backgroundColor: '#fff' }, targetRow: { minHeight: 64, flexDirection: 'row', alignItems: 'center', gap: 12, paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#d1d5db' }, targetName: { fontSize: 16, fontWeight: '600', color: '#111827' }, targetNumber: { marginTop: 3, fontSize: 13, color: '#6b7280' }, uploadOverlay: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10, padding: 14, borderRadius: 12, backgroundColor: '#eff6ff' }, uploadText: { fontWeight: '600', color: '#1e3a8a' }, cancelButton: { alignItems: 'center', padding: 14 }, cancelButtonText: { fontSize: 16, fontWeight: '600', color: '#b91c1c' }, emptyState: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 28 }, emptyTitle: { marginTop: 14, fontSize: 20, fontWeight: '700', color: '#111827' }, emptyText: { marginTop: 6, textAlign: 'center', color: '#6b7280' }, primaryButton: { marginTop: 22, paddingHorizontal: 18, paddingVertical: 13, borderRadius: 11, backgroundColor: '#2563eb' }, primaryButtonText: { fontSize: 15, fontWeight: '700', color: '#fff' }, });