import { useCallback, useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, KeyboardAvoidingView, Modal, Platform, Pressable, RefreshControl, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { router } from 'expo-router'; import { createProject, Customer, fetchCustomers, fetchPlants, fetchProjects, Plant, Project } from '@/src/lib/api'; import { useAuth } from '@/src/providers/auth-provider'; const PRIMARY = '#69c350'; function getProjectLine(project: Project): string { if (project.projectNumber) return `${project.name} · ${project.projectNumber}`; return project.name; } function getActivePhaseLabel(project: Project): string { const explicit = String(project.active_phase || '').trim(); if (explicit) return explicit; const phases = Array.isArray(project.phases) ? project.phases : []; const active = phases.find((phase: any) => phase?.active); return String(active?.label || '').trim(); } function isProjectCompletedByPhase(project: Project): boolean { const phase = getActivePhaseLabel(project).toLowerCase(); return phase === 'abgeschlossen'; } export default function ProjectsScreen() { const { token } = useAuth(); const [projects, setProjects] = useState([]); const [customers, setCustomers] = useState([]); const [plants, setPlants] = useState([]); const [search, setSearch] = useState(''); const [showArchived, setShowArchived] = useState(false); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [createOpen, setCreateOpen] = useState(false); const [saving, setSaving] = useState(false); const [createError, setCreateError] = useState(null); const [nameInput, setNameInput] = useState(''); const [projectNumberInput, setProjectNumberInput] = useState(''); const [notesInput, setNotesInput] = useState(''); const [selectedCustomerId, setSelectedCustomerId] = useState(null); const [selectedPlantId, setSelectedPlantId] = useState(null); const [pickerMode, setPickerMode] = useState<'customer' | 'plant' | null>(null); const [customerSearch, setCustomerSearch] = useState(''); const [plantSearch, setPlantSearch] = useState(''); const filteredProjects = useMemo(() => { const terms = search .trim() .toLowerCase() .split(/\s+/) .filter(Boolean); return projects.filter((project) => { if (!showArchived && isProjectCompletedByPhase(project)) return false; if (terms.length === 0) return true; const haystack = [ project.name, project.projectNumber, project.notes, project.customerRef, project.active_phase, getActivePhaseLabel(project), ] .map((value) => String(value || '').toLowerCase()) .join(' '); return terms.every((term) => haystack.includes(term)); }); }, [projects, search, showArchived]); const selectedCustomerLabel = useMemo(() => { if (!selectedCustomerId) return 'Kunde auswählen (optional)'; const customer = customers.find((item) => Number(item.id) === selectedCustomerId); return customer ? `${customer.name}${customer.customerNumber ? ` · ${customer.customerNumber}` : ''}` : `ID ${selectedCustomerId}`; }, [customers, selectedCustomerId]); const selectedPlantLabel = useMemo(() => { if (!selectedPlantId) return 'Objekt auswählen (optional)'; const plant = plants.find((item) => Number(item.id) === selectedPlantId); return plant ? plant.name : `ID ${selectedPlantId}`; }, [plants, selectedPlantId]); const filteredCustomerOptions = useMemo(() => { const terms = customerSearch.trim().toLowerCase().split(/\s+/).filter(Boolean); return customers .filter((customer) => !customer.archived) .filter((customer) => { if (terms.length === 0) return true; const haystack = [customer.name, customer.customerNumber] .map((value) => String(value || '').toLowerCase()) .join(' '); return terms.every((term) => haystack.includes(term)); }); }, [customerSearch, customers]); const filteredPlantOptions = useMemo(() => { const terms = plantSearch.trim().toLowerCase().split(/\s+/).filter(Boolean); return plants .filter((plant) => !plant.archived) .filter((plant) => { if (terms.length === 0) return true; const haystack = [plant.name, plant.description] .map((value) => String(value || '').toLowerCase()) .join(' '); return terms.every((term) => haystack.includes(term)); }); }, [plantSearch, plants]); const loadProjects = useCallback(async (showSpinner = true) => { if (!token) return; if (showSpinner) setLoading(true); setError(null); try { const [projectRows, customerRows, plantRows] = await Promise.all([ fetchProjects(token, true), fetchCustomers(token, true), fetchPlants(token, true), ]); setProjects(projectRows); setCustomers(customerRows); setPlants(plantRows); } catch (err) { setError(err instanceof Error ? err.message : 'Projekte konnten nicht geladen werden.'); } finally { setLoading(false); setRefreshing(false); } }, [token]); useEffect(() => { if (!token) return; void loadProjects(true); }, [loadProjects, token]); async function onRefresh() { setRefreshing(true); await loadProjects(false); } function closeCreateModal() { setCreateOpen(false); setCreateError(null); setNameInput(''); setProjectNumberInput(''); setNotesInput(''); setSelectedCustomerId(null); setSelectedPlantId(null); setCustomerSearch(''); setPlantSearch(''); setPickerMode(null); } async function onCreateProject() { if (!token) return; const name = nameInput.trim(); if (!name) { setCreateError('Bitte einen Projektnamen eingeben.'); return; } setSaving(true); setCreateError(null); try { await createProject(token, { name, projectNumber: projectNumberInput.trim() || null, customer: selectedCustomerId, plant: selectedPlantId, notes: notesInput.trim() || null, }); closeCreateModal(); await loadProjects(false); } catch (err) { setCreateError(err instanceof Error ? err.message : 'Projekt konnte nicht erstellt werden.'); } finally { setSaving(false); } } return ( setShowArchived((prev) => !prev)}> Abgeschlossene anzeigen }> {error ? {error} : null} {loading ? ( Projekte werden geladen... ) : null} {!loading && filteredProjects.length === 0 ? Keine Projekte gefunden. : null} {!loading && filteredProjects.map((project) => ( router.push(`/project/${project.id}`)}> {getProjectLine(project)} {isProjectCompletedByPhase(project) ? Abgeschlossen : null} {getActivePhaseLabel(project) ? ( Phase: {getActivePhaseLabel(project)} ) : null} {project.notes ? {String(project.notes)} : null} ))} setCreateOpen(true)}> + Neues Projekt setPickerMode('customer')}> {selectedCustomerLabel} setPickerMode('plant')}> {selectedPlantLabel} {pickerMode === 'customer' ? ( Kunde auswählen { setSelectedCustomerId(null); setPickerMode(null); }}> Keine Auswahl {filteredCustomerOptions.map((customer) => ( { setSelectedCustomerId(Number(customer.id)); setPickerMode(null); }}> {customer.name} {customer.customerNumber ? `Nr. ${customer.customerNumber}` : `ID ${customer.id}`} ))} ) : null} {pickerMode === 'plant' ? ( Objekt auswählen { setSelectedPlantId(null); setPickerMode(null); }}> Keine Auswahl {filteredPlantOptions.map((plant) => ( { setSelectedPlantId(Number(plant.id)); setPickerMode(null); }}> {plant.name} ID {plant.id} ))} ) : null} {createError ? {createError} : null} Abbrechen {saving ? 'Speichere...' : 'Anlegen'} ); } const styles = StyleSheet.create({ screen: { flex: 1, backgroundColor: '#ffffff' }, searchWrap: { paddingHorizontal: 16, paddingTop: 12, paddingBottom: 10, borderBottomWidth: 1, borderBottomColor: '#e5e7eb', backgroundColor: '#ffffff', gap: 8, }, searchInput: { borderWidth: 1, borderColor: '#d1d5db', borderRadius: 10, paddingHorizontal: 12, paddingVertical: 10, fontSize: 15, color: '#111827', backgroundColor: '#ffffff', }, list: { flex: 1, backgroundColor: '#ffffff' }, listContent: { paddingBottom: 96 }, row: { paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: '#e5e7eb', backgroundColor: '#ffffff', }, rowHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 8, }, rowTitle: { flex: 1, color: '#111827', fontSize: 15, fontWeight: '600' }, archivedBadge: { color: '#3d7a30', fontSize: 11, fontWeight: '600', backgroundColor: '#eff9ea', borderRadius: 999, paddingHorizontal: 8, paddingVertical: 3, overflow: 'hidden', }, rowSubtitle: { color: '#6b7280', fontSize: 13, marginTop: 2 }, phaseText: { color: '#6b7280', fontSize: 12, marginTop: 2 }, toggleButton: { alignSelf: 'flex-start', borderWidth: 1, borderColor: '#d1d5db', borderRadius: 999, paddingHorizontal: 10, paddingVertical: 6, backgroundColor: '#ffffff', }, toggleButtonActive: { borderColor: PRIMARY, backgroundColor: '#eff9ea' }, toggleButtonText: { color: '#374151', fontSize: 12, fontWeight: '600' }, toggleButtonTextActive: { color: '#3d7a30' }, loadingBox: { paddingVertical: 20, alignItems: 'center', gap: 8 }, loadingText: { color: '#6b7280' }, empty: { color: '#6b7280', textAlign: 'center', paddingVertical: 20 }, error: { color: '#dc2626', fontSize: 13, paddingHorizontal: 16, paddingTop: 10 }, fab: { position: 'absolute', right: 18, bottom: 20, width: 56, height: 56, borderRadius: 28, backgroundColor: PRIMARY, alignItems: 'center', justifyContent: 'center', shadowColor: '#111827', shadowOpacity: 0.25, shadowRadius: 8, shadowOffset: { width: 0, height: 4 }, elevation: 5, }, fabText: { color: '#ffffff', fontSize: 30, lineHeight: 30, marginTop: -2 }, modalOverlay: { flex: 1, backgroundColor: 'rgba(17, 24, 39, 0.45)', justifyContent: 'center', padding: 20, }, modalKeyboardWrap: { width: '100%' }, modalCard: { backgroundColor: '#ffffff', borderRadius: 14, padding: 16, gap: 10 }, modalTitle: { color: '#111827', fontSize: 18, fontWeight: '700' }, multilineInput: { minHeight: 92, textAlignVertical: 'top' }, selectButton: { borderWidth: 1, borderColor: '#d1d5db', borderRadius: 10, paddingHorizontal: 12, paddingVertical: 10, backgroundColor: '#ffffff', }, selectButtonText: { color: '#111827', fontSize: 15 }, inlinePickerBox: { borderWidth: 1, borderColor: '#e5e7eb', borderRadius: 10, padding: 10, gap: 8, backgroundColor: '#fafafa', }, inlinePickerTitle: { color: '#111827', fontSize: 14, fontWeight: '700' }, pickerList: { maxHeight: 220 }, pickerRow: { borderWidth: 1, borderColor: '#e5e7eb', borderRadius: 10, paddingHorizontal: 12, paddingVertical: 10, marginBottom: 8, backgroundColor: '#ffffff', }, pickerRowTitle: { color: '#111827', fontSize: 14, fontWeight: '600' }, pickerRowMeta: { color: '#6b7280', fontSize: 12, marginTop: 2 }, modalActions: { flexDirection: 'row', justifyContent: 'flex-end', gap: 8, marginTop: 4 }, secondaryButton: { borderWidth: 1, borderColor: '#d1d5db', borderRadius: 10, minHeight: 40, paddingHorizontal: 14, alignItems: 'center', justifyContent: 'center', }, secondaryButtonText: { color: '#374151', fontWeight: '600' }, primaryButton: { borderRadius: 10, minHeight: 40, paddingHorizontal: 14, alignItems: 'center', justifyContent: 'center', backgroundColor: PRIMARY, }, primaryButtonText: { color: '#ffffff', fontWeight: '700' }, buttonDisabled: { opacity: 0.6 }, });