import { useState } from 'react'; import { View, Text, TextInput, TouchableOpacity, ScrollView, Alert, ActivityIndicator } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { router } from 'expo-router'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { api } from '../../../services/api'; const PRIORITIES = ['NORMAL', 'HIGH']; const PRIORITY_COLOR: Record = { HIGH: '#DC2626', NORMAL: '#0891B2' }; const PRIORITY_BG: Record = { HIGH: '#FEE2E2', NORMAL: '#ECFEFF' }; const TYPES = [ { value: 'SUPPORT', label: 'Support' }, { value: 'INSTALLATION', label: 'Installation' }, { value: 'BILLING', label: 'Billing' }, ]; export default function NewTaskScreen() { const qc = useQueryClient(); const [subject, setSubject] = useState(''); const [description, setDescription] = useState(''); const [priority, setPriority] = useState('NORMAL'); const [type, setType] = useState('SUPPORT'); const [search, setSearch] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); const [client, setClient] = useState(null); const [loading, setLoading] = useState(false); const { data: searchResults, isFetching: searching } = useQuery({ queryKey: ['client-search', debouncedSearch], queryFn: () => api.get(`/api/v1/clients?search=${debouncedSearch}&limit=8`).then(r => r.data?.data ?? r.data ?? []), enabled: debouncedSearch.trim().length >= 2, }); const handleSearchChange = (v: string) => { setSearch(v); setTimeout(() => setDebouncedSearch(v), 400); }; const submit = async () => { if (!subject.trim()) return Alert.alert('Required', 'Please enter a subject.'); if (!client) return Alert.alert('Required', 'Please select a client.'); setLoading(true); try { await api.post('/api/v1/tickets', { subject: subject.trim(), description: description.trim() || undefined, priority, type, clientId: client.id, }); qc.invalidateQueries({ queryKey: ['tasks'] }); qc.invalidateQueries({ queryKey: ['dashboard'] }); Alert.alert('Task Created', subject, [{ text: 'OK', onPress: () => router.back() }]); } catch (e: any) { Alert.alert('Error', e?.response?.data?.message ?? 'Could not create task.'); } finally { setLoading(false); } }; return ( router.back()} style={{ marginBottom: 12 }} activeOpacity={0.7}> ← Back New Ticket {/* Client Search */} Client * {client ? ( {client.firstName} {client.lastName} {client.accountNumber} { setClient(null); setSearch(''); setDebouncedSearch(''); }} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}> Change ) : ( {search.length > 0 && ( { setSearch(''); setDebouncedSearch(''); }} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}> × )} {searching && } {debouncedSearch.trim().length >= 2 && ( {(searchResults ?? []).length === 0 && !searching ? ( No clients found ) : ( (searchResults ?? []).map((c: any, i: number, arr: any[]) => ( { setClient(c); setSearch(''); setDebouncedSearch(''); }} activeOpacity={0.7} > {c.firstName} {c.lastName} {c.accountNumber} )) )} )} )} {/* Subject */} Subject * {/* Type */} Type {TYPES.map(t => ( setType(t.value)} style={{ borderRadius: 20, paddingHorizontal: 16, paddingVertical: 10, marginRight: 8, marginBottom: 8, backgroundColor: type === t.value ? '#0891B2' : '#FFF', borderWidth: 1.5, borderColor: type === t.value ? '#0891B2' : '#E2E8F0' }} activeOpacity={0.7} > {t.label} ))} {/* Priority */} Priority {PRIORITIES.map(p => { const isSelected = priority === p; const isHigh = p === 'HIGH'; const selectedBg = isHigh ? '#DC2626' : '#0891B2'; const unselectedBg = isHigh ? '#FEF2F2' : '#F0F9FF'; const selectedText = '#FFF'; const unselectedText = isHigh ? '#DC2626' : '#0891B2'; const desc = isHigh ? 'Urgent, escalate' : 'Standard queue'; return ( setPriority(p)} style={{ flex: 1, borderRadius: 14, paddingVertical: 16, alignItems: 'center', marginHorizontal: 4, backgroundColor: isSelected ? selectedBg : unselectedBg, borderWidth: 1.5, borderColor: isSelected ? selectedBg : '#E2E8F0' }} activeOpacity={0.7} > {p} {desc} ); })} {/* Description */} Description (optional) {loading ? : Create Ticket} ); }