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'; import { filterCustomers, filterPlants, filterProjects, getActiveProjectPhase, getPlantCustomerName, } from '@/src/lib/resource-list-filters'; 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' }, ]; const PRIMARY = '#69c350'; 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, true) : targetType === 'customer' ? fetchCustomers(token, true) : fetchPlants(token, true); 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(() => { if (targetType === 'project') return filterProjects(targets as Project[], search); if (targetType === 'customer') return filterCustomers(targets as Customer[], search); if (targetType === 'plant') return filterPlants(targets as Plant[], search); return []; }, [search, targets, targetType]); 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; const detail = targetType === 'project' ? getActiveProjectPhase(target as Project) : targetType === 'plant' ? getPlantCustomerName((target as Plant).customer) : null; return ( [styles.targetRow, pressed ? styles.targetRowPressed : null]} onPress={() => void uploadTo(target)} disabled={isUploading}> {target.name} {number ? Nr.: {String(number)} : null} {detail ? ( {targetType === 'project' ? `Phase: ${detail}` : `Kunde: ${detail}`} ) : null} ); })} )} )} {isUploading ? ( Upload läuft: {uploadedCount} von {files.length} ) : null} Abbrechen ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: '#ffffff' }, content: { paddingBottom: 28, gap: 14 }, flex: { flex: 1 }, fileSummary: { flexDirection: 'row', gap: 12, alignItems: 'center', paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: '#d8edcf', backgroundColor: '#eff9ea' }, summaryIcon: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center', backgroundColor: '#ffffff' }, summaryTitle: { fontSize: 15, fontWeight: '700', color: '#2f5f24' }, summaryText: { marginTop: 2, fontSize: 12, color: '#4f6f47' }, heading: { marginTop: 4, paddingHorizontal: 16, fontSize: 20, fontWeight: '700', color: '#111827' }, description: { marginTop: -8, paddingHorizontal: 16, fontSize: 14, color: '#6b7280' }, typeGrid: { borderTopWidth: 1, borderTopColor: '#e5e7eb' }, typeCard: { flexDirection: 'row', alignItems: 'center', gap: 14, paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: '#e5e7eb', backgroundColor: '#ffffff' }, iconCircle: { width: 42, height: 42, borderRadius: 21, alignItems: 'center', justifyContent: 'center', backgroundColor: '#eff9ea' }, typeLabel: { flex: 1, fontSize: 15, fontWeight: '600', color: '#111827' }, backLink: { flexDirection: 'row', alignItems: 'center', alignSelf: 'flex-start', marginHorizontal: 12, marginTop: 2, padding: 4 }, backLinkText: { fontSize: 14, fontWeight: '600', color: '#3d7a30' }, searchBox: { minHeight: 44, marginHorizontal: 16, flexDirection: 'row', alignItems: 'center', gap: 8, paddingHorizontal: 12, borderWidth: 1, borderColor: '#d1d5db', borderRadius: 10, backgroundColor: '#ffffff' }, searchInput: { flex: 1, paddingVertical: 10, fontSize: 15, color: '#111827' }, loader: { marginVertical: 32 }, errorText: { marginHorizontal: 16, padding: 12, borderRadius: 10, backgroundColor: '#fef2f2', color: '#b91c1c' }, emptyListText: { paddingVertical: 28, textAlign: 'center', color: '#6b7280' }, targetList: { borderTopWidth: 1, borderTopColor: '#e5e7eb', backgroundColor: '#ffffff' }, targetRow: { minHeight: 64, flexDirection: 'row', alignItems: 'center', gap: 12, paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#e5e7eb', backgroundColor: '#ffffff' }, targetRowPressed: { backgroundColor: '#f3f4f6' }, targetName: { fontSize: 15, fontWeight: '600', color: '#111827' }, targetNumber: { marginTop: 3, fontSize: 13, color: '#6b7280' }, uploadIcon: { width: 38, height: 38, borderRadius: 19, alignItems: 'center', justifyContent: 'center', backgroundColor: '#eff9ea' }, uploadOverlay: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 10, marginHorizontal: 16, padding: 14, borderRadius: 10, backgroundColor: '#eff9ea' }, uploadText: { fontWeight: '600', color: '#2f5f24' }, cancelButton: { alignItems: 'center', padding: 14 }, cancelButtonText: { fontSize: 16, fontWeight: '600', color: '#b91c1c' }, emptyState: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 28 }, emptyIcon: { width: 64, height: 64, borderRadius: 32, alignItems: 'center', justifyContent: 'center', backgroundColor: '#eff9ea' }, emptyTitle: { marginTop: 14, fontSize: 20, fontWeight: '700', color: '#111827' }, emptyText: { marginTop: 6, textAlign: 'center', color: '#6b7280' }, primaryButton: { marginTop: 22, paddingHorizontal: 18, paddingVertical: 13, borderRadius: 10, backgroundColor: PRIMARY }, primaryButtonText: { fontSize: 15, fontWeight: '700', color: '#fff' }, });