Some checks failed
ERP Core CI / Backend Lint (push) Has been cancelled
ERP Core CI / Backend Unit Tests (push) Has been cancelled
ERP Core CI / Backend Integration Tests (push) Has been cancelled
ERP Core CI / Frontend Lint (push) Has been cancelled
ERP Core CI / Frontend Unit Tests (push) Has been cancelled
ERP Core CI / Frontend E2E Tests (push) Has been cancelled
ERP Core CI / Database DDL Validation (push) Has been cancelled
ERP Core CI / Backend Build (push) Has been cancelled
ERP Core CI / Frontend Build (push) Has been cancelled
ERP Core CI / CI Success (push) Has been cancelled
Performance Tests / Lighthouse CI (push) Has been cancelled
Performance Tests / Bundle Size Analysis (push) Has been cancelled
Performance Tests / k6 Load Tests (push) Has been cancelled
Performance Tests / Performance Summary (push) Has been cancelled
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Actualizaciones en modulos CRM y OpenAPI Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
300 lines
7.8 KiB
TypeScript
300 lines
7.8 KiB
TypeScript
/**
|
|
* Partners Screen
|
|
*
|
|
* List of customers and suppliers
|
|
*/
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
FlatList,
|
|
TextInput,
|
|
TouchableOpacity,
|
|
RefreshControl,
|
|
ActivityIndicator,
|
|
} from 'react-native';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { partnersApi } from '@/services/api';
|
|
import { Partner } from '@/types';
|
|
|
|
type FilterType = 'all' | 'customers' | 'suppliers';
|
|
|
|
export default function PartnersScreen() {
|
|
const [partners, setPartners] = useState<Partner[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [filter, setFilter] = useState<FilterType>('all');
|
|
|
|
const fetchPartners = async () => {
|
|
try {
|
|
const params: any = { limit: 50 };
|
|
if (searchQuery) params.search = searchQuery;
|
|
if (filter === 'customers') params.isCustomer = true;
|
|
if (filter === 'suppliers') params.isSupplier = true;
|
|
|
|
const response = await partnersApi.list(params);
|
|
setPartners(response.data || []);
|
|
} catch (error) {
|
|
console.error('Error fetching partners:', error);
|
|
// Use mock data for demo
|
|
setPartners([
|
|
{ id: '1', name: 'Empresa ABC', email: 'contacto@abc.com', phone: '+52 55 1234 5678', isCustomer: true, isSupplier: false, active: true },
|
|
{ id: '2', name: 'Proveedor XYZ', email: 'ventas@xyz.com', phone: '+52 55 8765 4321', isCustomer: false, isSupplier: true, active: true },
|
|
{ id: '3', name: 'Cliente 123', email: 'info@cliente123.com', phone: '+52 55 1111 2222', isCustomer: true, isSupplier: false, active: true },
|
|
{ id: '4', name: 'Distribuidor Norte', email: 'norte@dist.com', phone: '+52 81 3333 4444', isCustomer: true, isSupplier: true, active: true },
|
|
]);
|
|
} finally {
|
|
setIsLoading(false);
|
|
setRefreshing(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchPartners();
|
|
}, [filter]);
|
|
|
|
useEffect(() => {
|
|
const timeoutId = setTimeout(() => {
|
|
if (!isLoading) fetchPartners();
|
|
}, 500);
|
|
return () => clearTimeout(timeoutId);
|
|
}, [searchQuery]);
|
|
|
|
const onRefresh = useCallback(() => {
|
|
setRefreshing(true);
|
|
fetchPartners();
|
|
}, [filter, searchQuery]);
|
|
|
|
const filteredPartners = partners.filter((partner) => {
|
|
if (filter === 'customers' && !partner.isCustomer) return false;
|
|
if (filter === 'suppliers' && !partner.isSupplier) return false;
|
|
return true;
|
|
});
|
|
|
|
const renderPartner = ({ item }: { item: Partner }) => (
|
|
<TouchableOpacity style={styles.partnerCard}>
|
|
<View style={styles.partnerAvatar}>
|
|
<Text style={styles.partnerInitial}>
|
|
{item.name.charAt(0).toUpperCase()}
|
|
</Text>
|
|
</View>
|
|
<View style={styles.partnerInfo}>
|
|
<Text style={styles.partnerName}>{item.name}</Text>
|
|
{item.email && <Text style={styles.partnerEmail}>{item.email}</Text>}
|
|
<View style={styles.partnerTags}>
|
|
{item.isCustomer && (
|
|
<View style={[styles.tag, styles.customerTag]}>
|
|
<Text style={styles.tagText}>Cliente</Text>
|
|
</View>
|
|
)}
|
|
{item.isSupplier && (
|
|
<View style={[styles.tag, styles.supplierTag]}>
|
|
<Text style={styles.tagText}>Proveedor</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
|
|
</TouchableOpacity>
|
|
);
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{/* Search */}
|
|
<View style={styles.searchContainer}>
|
|
<Ionicons name="search" size={20} color="#9ca3af" style={styles.searchIcon} />
|
|
<TextInput
|
|
style={styles.searchInput}
|
|
placeholder="Buscar contactos..."
|
|
placeholderTextColor="#9ca3af"
|
|
value={searchQuery}
|
|
onChangeText={setSearchQuery}
|
|
/>
|
|
</View>
|
|
|
|
{/* Filters */}
|
|
<View style={styles.filters}>
|
|
{(['all', 'customers', 'suppliers'] as FilterType[]).map((type) => (
|
|
<TouchableOpacity
|
|
key={type}
|
|
style={[styles.filterButton, filter === type && styles.filterButtonActive]}
|
|
onPress={() => setFilter(type)}
|
|
>
|
|
<Text style={[styles.filterText, filter === type && styles.filterTextActive]}>
|
|
{type === 'all' ? 'Todos' : type === 'customers' ? 'Clientes' : 'Proveedores'}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
|
|
{/* List */}
|
|
{isLoading ? (
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="large" color="#1e40af" />
|
|
</View>
|
|
) : (
|
|
<FlatList
|
|
data={filteredPartners}
|
|
renderItem={renderPartner}
|
|
keyExtractor={(item) => item.id}
|
|
contentContainerStyle={styles.listContent}
|
|
refreshControl={
|
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor="#1e40af" />
|
|
}
|
|
ListEmptyComponent={
|
|
<View style={styles.emptyContainer}>
|
|
<Ionicons name="people-outline" size={48} color="#d1d5db" />
|
|
<Text style={styles.emptyText}>No se encontraron contactos</Text>
|
|
</View>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{/* FAB */}
|
|
<TouchableOpacity style={styles.fab}>
|
|
<Ionicons name="add" size={24} color="#ffffff" />
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: '#f8fafc',
|
|
},
|
|
searchContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: '#ffffff',
|
|
margin: 16,
|
|
borderRadius: 10,
|
|
paddingHorizontal: 12,
|
|
},
|
|
searchIcon: {
|
|
marginRight: 8,
|
|
},
|
|
searchInput: {
|
|
flex: 1,
|
|
paddingVertical: 12,
|
|
fontSize: 16,
|
|
color: '#1f2937',
|
|
},
|
|
filters: {
|
|
flexDirection: 'row',
|
|
paddingHorizontal: 16,
|
|
marginBottom: 8,
|
|
gap: 8,
|
|
},
|
|
filterButton: {
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 8,
|
|
borderRadius: 20,
|
|
backgroundColor: '#ffffff',
|
|
},
|
|
filterButtonActive: {
|
|
backgroundColor: '#1e40af',
|
|
},
|
|
filterText: {
|
|
fontSize: 14,
|
|
color: '#64748b',
|
|
},
|
|
filterTextActive: {
|
|
color: '#ffffff',
|
|
fontWeight: '500',
|
|
},
|
|
listContent: {
|
|
padding: 16,
|
|
paddingTop: 8,
|
|
},
|
|
partnerCard: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: '#ffffff',
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
marginBottom: 12,
|
|
},
|
|
partnerAvatar: {
|
|
width: 48,
|
|
height: 48,
|
|
borderRadius: 24,
|
|
backgroundColor: '#1e40af',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
marginRight: 12,
|
|
},
|
|
partnerInitial: {
|
|
fontSize: 20,
|
|
fontWeight: '600',
|
|
color: '#ffffff',
|
|
},
|
|
partnerInfo: {
|
|
flex: 1,
|
|
},
|
|
partnerName: {
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
color: '#1f2937',
|
|
},
|
|
partnerEmail: {
|
|
fontSize: 14,
|
|
color: '#64748b',
|
|
marginTop: 2,
|
|
},
|
|
partnerTags: {
|
|
flexDirection: 'row',
|
|
marginTop: 6,
|
|
gap: 6,
|
|
},
|
|
tag: {
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 2,
|
|
borderRadius: 4,
|
|
},
|
|
customerTag: {
|
|
backgroundColor: '#dbeafe',
|
|
},
|
|
supplierTag: {
|
|
backgroundColor: '#dcfce7',
|
|
},
|
|
tagText: {
|
|
fontSize: 12,
|
|
fontWeight: '500',
|
|
color: '#374151',
|
|
},
|
|
loadingContainer: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
emptyContainer: {
|
|
alignItems: 'center',
|
|
paddingVertical: 48,
|
|
},
|
|
emptyText: {
|
|
fontSize: 16,
|
|
color: '#9ca3af',
|
|
marginTop: 12,
|
|
},
|
|
fab: {
|
|
position: 'absolute',
|
|
bottom: 24,
|
|
right: 24,
|
|
width: 56,
|
|
height: 56,
|
|
borderRadius: 28,
|
|
backgroundColor: '#1e40af',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
shadowColor: '#000',
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 4,
|
|
elevation: 5,
|
|
},
|
|
});
|