import { useState } from 'react';
import {
View, Text, TextInput, TouchableOpacity, ScrollView,
ActivityIndicator, Alert, KeyboardAvoidingView, Platform
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { router } from 'expo-router';
import { useQuery } from '@tanstack/react-query';
import { api } from '../../../services/api';
import { Icon } from '../../../components/Icon';
const STEP_LABELS = ['Client Info', 'Plan', 'Confirm'];
// ── Step indicator ─────────────────────────────────────────────────────────────
function StepBar({ step }: { step: number }) {
return (
{STEP_LABELS.map((label, i) => {
const active = i === step;
const done = i < step;
const circleColor = done || active ? '#0891B2' : '#CBD5E1';
const textColor = done || active ? '#0891B2' : '#94A3B8';
return (
{i > 0 && (
)}
{done
? ✓
: {i + 1}
}
{i < STEP_LABELS.length - 1 && (
)}
{label}
);
})}
);
}
// ── Field ──────────────────────────────────────────────────────────────────────
function Field({
label, value, onChangeText, placeholder, required = false,
keyboardType = 'default', autoCapitalize = 'words',
}: any) {
return (
{label}{required && *}
);
}
export default function NewClientScreen() {
const [step, setStep] = useState(0);
const [submitting, setSubmitting] = useState(false);
// Step 1 fields
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const [address, setAddress] = useState('');
const [areaId, setAreaId] = useState('');
// Step 2
const [planId, setPlanId] = useState('');
const { data: areas = [] } = useQuery({
queryKey: ['areas'],
queryFn: () => api.get('/api/v1/areas').then(r => r.data?.data ?? r.data ?? []),
});
const { data: plans = [], isLoading: plansLoading } = useQuery({
queryKey: ['plans'],
queryFn: () => api.get('/api/v1/plans').then(r => r.data?.data ?? r.data ?? []),
});
const selectedPlan = (plans as any[]).find((p: any) => p.id === planId);
// ── Validation ─────────────────────────────────────────────────────────────
const validateStep1 = () => {
if (!firstName.trim()) { Alert.alert('Required', 'First name is required.'); return false; }
if (!lastName.trim()) { Alert.alert('Required', 'Last name is required.'); return false; }
if (!phone.trim()) { Alert.alert('Required', 'Contact number is required.'); return false; }
return true;
};
const validateStep2 = () => {
if (!planId) { Alert.alert('Required', 'Please select a plan.'); return false; }
return true;
};
const next = () => {
if (step === 0 && !validateStep1()) return;
if (step === 1 && !validateStep2()) return;
setStep(s => s + 1);
};
const back = () => {
if (step === 0) router.back();
else setStep(s => s - 1);
};
// ── Submit ─────────────────────────────────────────────────────────────────
const submit = async () => {
setSubmitting(true);
try {
// 1. Create client
const clientRes = await api.post('/api/v1/clients', {
firstName: firstName.trim(),
lastName: lastName.trim(),
phone: phone.trim(),
...(email.trim() ? { email: email.trim() } : {}),
...(address.trim() ? { address: address.trim() } : {}),
...(areaId ? { areaId } : {}),
});
const client = clientRes.data;
// 2. Create subscription (PENDING until installation confirmed)
await api.post('/api/v1/subscriptions', {
clientId: client.id,
planId,
type: 'POSTPAID',
status: 'PENDING',
billingDay: 5,
startDate: new Date().toISOString(),
});
// 3. Create installation ticket
const ticketRes = await api.post('/api/v1/tickets', {
clientId: client.id,
subject: `New Installation — ${firstName.trim()} ${lastName.trim()}`,
type: 'INSTALLATION',
priority: 'NORMAL',
});
const ticket = ticketRes.data;
Alert.alert(
'Client Onboarded! 🎉',
`${firstName} ${lastName} has been registered.\n\nAn installation ticket has been created.`,
[{
text: 'View Ticket',
onPress: () => {
router.replace('/(app)/clients');
setTimeout(() => router.push(`/(app)/tasks/${ticket.id}`), 300);
},
}, {
text: 'Done',
onPress: () => router.replace('/(app)/clients'),
}]
);
} catch (e: any) {
const msg = e?.response?.data?.message ?? 'Something went wrong. Please try again.';
Alert.alert('Error', Array.isArray(msg) ? msg.join('\n') : msg);
} finally {
setSubmitting(false);
}
};
return (
{/* Header */}
New Client Onboarding
{/* ── Step 1: Client Info ─────────────────────────────────────────── */}
{step === 0 && (
Client Information
Fill in the new subscriber's details.
{/* Area picker */}
Area / Zone
{(areas as any[]).map((a: any) => (
setAreaId(areaId === a.id ? '' : a.id)}
style={{
paddingHorizontal: 14, paddingVertical: 8, borderRadius: 20,
backgroundColor: areaId === a.id ? '#0891B2' : '#F1F5F9',
borderWidth: 1, borderColor: areaId === a.id ? '#0891B2' : '#E2E8F0',
}}
>
{a.name}
))}
)}
{/* ── Step 2: Plan Selection ──────────────────────────────────────── */}
{step === 1 && (
Select a Plan
Choose the internet plan for this subscriber.
{plansLoading
?
: (plans as any[]).filter((p: any) => p.isActive !== false).map((p: any) => {
const selected = planId === p.id;
return (
setPlanId(p.id)}
style={{
backgroundColor: selected ? '#ECFEFF' : '#fff',
borderWidth: 2, borderColor: selected ? '#0891B2' : '#E2E8F0',
borderRadius: 14, padding: 18, marginBottom: 12,
flexDirection: 'row', alignItems: 'center',
}}
>
{p.name}
↓ {p.speedDownMbps} Mbps ↑ {p.speedUpMbps} Mbps
{p.description ? (
{p.description}
) : null}
₱{Number(p.monthlyPrice).toLocaleString()}
/month
{selected && (
✓
)}
);
})
}
)}
{/* ── Step 3: Confirm ─────────────────────────────────────────────── */}
{step === 2 && (
Confirm & Submit
Review the details before creating the account.
{/* Client card */}
CLIENT
{email ?
: null}
{address ?
: null}
{areaId ? a.id === areaId)?.name ?? ''} /> : null}
{/* Plan card */}
PLAN
{/* What will happen */}
What happens next
{['Client record will be created', 'Subscription set to Pending (activates after install)', 'Installation ticket created automatically'].map((s, i) => (
•
{s}
))}
)}
{/* Footer buttons */}
{step > 0 && (
Back
)}
{step < 2
? (
{step === 0 ? 'Next: Select Plan →' : 'Next: Review →'}
) : (
{submitting
?
: ✓ Create & Schedule Install
}
)
}
);
}
function Row({ label, value, isLast = false }: { label: string; value: string; isLast?: boolean }) {
if (!value) return null;
return (
{label}
{value}
);
}