feat: Sprint 1 — Onboarding screen, user persistence, Contact management (list, add, edit, delete, detail, search, filter)

This commit is contained in:
root
2026-02-18 14:35:49 +08:00
parent 8f66a4d4ed
commit 5ddfa9722b
10 changed files with 595 additions and 34 deletions

View File

@@ -0,0 +1,42 @@
import { View, Text, TouchableOpacity } from 'react-native';
import { useRouter } from 'expo-router';
import { ChevronRight } from 'lucide-react-native';
import { Contact } from '@/store/useContactStore';
const statusColors: Record<string, string> = {
'Active': 'bg-green-100 text-green-700',
'Return Visit': 'bg-blue-100 text-blue-700',
'Bible Study': 'bg-purple-100 text-purple-700',
'Not Interested': 'bg-gray-100 text-gray-500',
'Do Not Call': 'bg-red-100 text-red-600',
};
interface Props {
contact: Contact;
onRefresh: () => void;
}
export function ContactCard({ contact, onRefresh }: Props) {
const router = useRouter();
const initials = contact.fullName.split(' ').map((n) => n[0]).join('').substring(0, 2).toUpperCase();
const statusStyle = statusColors[contact.status] ?? 'bg-gray-100 text-gray-500';
return (
<TouchableOpacity
className="bg-white rounded-2xl p-4 flex-row items-center shadow-sm"
onPress={() => router.push({ pathname: '/contact/[id]', params: { id: contact.id } })}
>
<View className="w-12 h-12 rounded-full bg-primary items-center justify-center mr-3">
<Text className="text-white font-bold text-lg">{initials}</Text>
</View>
<View className="flex-1">
<Text className="text-charcoal font-semibold text-base" numberOfLines={1}>{contact.fullName}</Text>
{contact.address ? <Text className="text-gray-500 text-sm" numberOfLines={1}>{contact.address}</Text> : null}
<View className="mt-1">
<Text className={`text-xs font-medium px-2 py-0.5 rounded-full self-start ${statusStyle}`}>{contact.status}</Text>
</View>
</View>
<ChevronRight size={18} color="#9CA3AF" />
</TouchableOpacity>
);
}