native_convex_CRM/app/features/addCustomer/addCustomer.screen.tsx

485 lines
16 KiB
TypeScript

import React, { useEffect, useMemo, useState } from 'react';
import {
View,
Text,
TouchableOpacity,
ScrollView,
KeyboardAvoidingView,
Platform,
Alert,
Switch,
ActivityIndicator,
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { getStyles } from './addCustomer.styles';
import { useTheme } from '@theme';
import { FormInput, FormPicker } from '@components';
import { RootState, useAppDispatch, useAppSelector } from '@store';
import { addCustomer, resetAddCustomerState } from './thunk';
import { getCustomers } from '../customers/thunk';
import { useNavigation } from '@react-navigation/native';
import { customerGroups } from '@mock-data';
export const AddCustomerScreen = () => {
const { theme: colors } = useTheme();
const styles = getStyles(colors);
const dispatch = useAppDispatch();
const navigation = useNavigation();
const userData = useAppSelector((state: RootState) => state.auth.user_data);
const { loading, successMessage, error } = useAppSelector(
(state: RootState) => state.addCustomer,
);
const { items: countryItems } = useAppSelector(
(state: RootState) => state.countryList,
);
const { items: languageItems } = useAppSelector(
(state: RootState) => state.languageList,
);
const { currencies: currencyItems } = useAppSelector(
(state: RootState) => state.currencyList,
);
// Form inputs
const [company, setCompany] = useState('');
const [vat, setVat] = useState('');
const [phonenumber, setPhonenumber] = useState('');
const [website, setWebsite] = useState('');
const [selectedGroups, setSelectedGroups] = useState<number[]>([1]); // default group 1 selected
const [defaultLanguage, setDefaultLanguage] = useState('system_default');
const [defaultCurrency, setDefaultCurrency] = useState('1'); // default to USD
// Address
const [address, setAddress] = useState('');
const [city, setCity] = useState('');
const [state, setState] = useState('');
const [zip, setZip] = useState('');
const [country, setCountry] = useState('');
// Billing Address
const [billingStreet, setBillingStreet] = useState('');
const [billingCity, setBillingCity] = useState('');
const [billingState, setBillingState] = useState('');
const [billingZip, setBillingZip] = useState('');
const [billingCountry, setBillingCountry] = useState('');
// Shipping Address
const [shippingStreet, setShippingStreet] = useState('');
const [shippingCity, setShippingCity] = useState('');
const [shippingState, setShippingState] = useState('');
const [shippingZip, setShippingZip] = useState('');
const [shippingCountry, setShippingCountry] = useState('');
// Auto-fill switches
const [billingSameAsGeneral, setBillingSameAsGeneral] = useState(false);
const [shippingSameAsBilling, setShippingSameAsBilling] = useState(false);
const countryOptions = useMemo(
() => countryItems.map(c => ({ label: c.short_name, value: c.country_id })),
[countryItems],
);
const languageOptions = useMemo(
() => languageItems.map(lang => ({ label: lang.value, value: lang.id })),
[languageItems],
);
const currencyOptions = useMemo(
() => currencyItems.map(c => ({ label: `${c.name} (${c.symbol})`, value: c.id })),
[currencyItems],
);
// Handle auto-fill logic for Billing
useEffect(() => {
if (billingSameAsGeneral) {
setBillingStreet(address);
setBillingCity(city);
setBillingState(state);
setBillingZip(zip);
setBillingCountry(country);
}
}, [billingSameAsGeneral, address, city, state, zip, country]);
// Handle auto-fill logic for Shipping
useEffect(() => {
if (shippingSameAsBilling) {
setShippingStreet(billingStreet);
setShippingCity(billingCity);
setShippingState(billingState);
setShippingZip(billingZip);
setShippingCountry(billingCountry);
}
}, [shippingSameAsBilling, billingStreet, billingCity, billingState, billingZip, billingCountry]);
// Handle Success/Error from Redux State
useEffect(() => {
if (successMessage) {
Alert.alert('Success', successMessage, [
{
text: 'OK',
onPress: () => {
dispatch(resetAddCustomerState());
dispatch(getCustomers()); // Refresh customers list view
navigation.goBack();
},
},
]);
} else if (error) {
Alert.alert('Error', error);
dispatch(resetAddCustomerState());
}
}, [successMessage, error, dispatch, navigation]);
const toggleGroup = (groupId: number) => {
if (selectedGroups.includes(groupId)) {
setSelectedGroups(selectedGroups.filter(id => id !== groupId));
} else {
setSelectedGroups([...selectedGroups, groupId]);
}
};
const handleSubmit = () => {
if (!company.trim()) {
Alert.alert('Error', 'Company Name is required.');
return;
}
const staffId = userData?.staffid || '';
// Convert selectedGroups array to JSON format matching requirement: {"0":1,"1":2}
const groupsInObj: { [key: string]: number } = {};
selectedGroups.forEach((groupId, index) => {
groupsInObj[index.toString()] = groupId;
});
const payload = {
company: company.trim(),
vat: vat.trim(),
phonenumber: phonenumber.trim(),
website: website.trim(),
groups_in: JSON.stringify(groupsInObj),
default_language: defaultLanguage,
default_currency: defaultCurrency,
address: address.trim(),
city: city.trim(),
state: state.trim(),
zip: zip.trim(),
country: country,
billing_street: billingStreet.trim(),
billing_city: billingCity.trim(),
billing_state: billingState.trim(),
billing_zip: billingZip.trim(),
billing_country: billingCountry,
shipping_street: shippingStreet.trim(),
shipping_city: shippingCity.trim(),
shipping_state: shippingState.trim(),
shipping_zip: shippingZip.trim(),
shipping_country: shippingCountry,
addedfrom: staffId,
};
dispatch(addCustomer(payload));
};
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
style={styles.container}
>
<ScrollView contentContainerStyle={styles.scrollContainer}>
<View style={styles.formCard}>
<Text style={styles.sectionHeader}>Customer Info</Text>
<FormInput
label="Company / Name *"
value={company}
onChangeText={setCompany}
placeholder="Enter company name"
autoCapitalize="words"
/>
<FormInput
label="VAT Number"
value={vat}
onChangeText={setVat}
placeholder="Enter VAT number"
keyboardType="numeric"
/>
<FormInput
label="Phone Number"
value={phonenumber}
onChangeText={setPhonenumber}
placeholder="Enter phone number"
keyboardType="phone-pad"
/>
<FormInput
label="Website"
value={website}
onChangeText={setWebsite}
placeholder="Enter website (e.g. www.google.com)"
keyboardType="url"
autoCapitalize="none"
/>
{/* Groups Selection */}
<Text style={{ fontSize: 13, color: colors.textSecondary, fontWeight: '600', marginBottom: 8 }}>
Groups
</Text>
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginBottom: 16 }}>
{customerGroups.map(group => {
const isSelected = selectedGroups.includes(group.id);
return (
<TouchableOpacity
key={group.id}
onPress={() => toggleGroup(group.id)}
style={{
paddingHorizontal: 12,
paddingVertical: 8,
borderRadius: 20,
borderWidth: 1,
borderColor: isSelected ? colors.icon : colors.border,
backgroundColor: isSelected ? colors.icon : colors.surface,
}}
activeOpacity={0.7}
>
<Text style={{ fontSize: 12, fontWeight: '600', color: isSelected ? '#FFFFFF' : colors.text }}>
{group.name}
</Text>
</TouchableOpacity>
);
})}
</View>
<View style={styles.row}>
<View style={styles.rowHalf}>
<FormPicker
label="Language"
value={defaultLanguage}
onValueChange={setDefaultLanguage}
options={languageOptions}
/>
</View>
<View style={styles.rowHalf}>
<FormPicker
label="Currency"
value={defaultCurrency}
onValueChange={setDefaultCurrency}
options={currencyOptions}
/>
</View>
</View>
</View>
{/* Primary Address */}
<View style={[styles.formCard, styles.cardSpacing]}>
<Text style={styles.sectionHeader}>Primary Address</Text>
<FormInput
label="Street Address"
value={address}
onChangeText={setAddress}
placeholder="Enter street address"
/>
<View style={styles.row}>
<View style={styles.rowHalf}>
<FormInput
label="City"
value={city}
onChangeText={setCity}
placeholder="City"
/>
</View>
<View style={styles.rowHalf}>
<FormInput
label="State"
value={state}
onChangeText={setState}
placeholder="State"
/>
</View>
</View>
<View style={styles.row}>
<View style={styles.rowHalf}>
<FormInput
label="Zip Code"
value={zip}
onChangeText={setZip}
placeholder="Zip"
keyboardType="numeric"
/>
</View>
<View style={styles.rowHalf}>
<FormPicker
label="Country"
value={country}
onValueChange={setCountry}
options={countryOptions}
placeholder="Select Country"
searchable
/>
</View>
</View>
</View>
{/* Billing Address */}
<View style={[styles.formCard, styles.cardSpacing]}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: colors.border, paddingBottom: 10, marginBottom: 20 }}>
<Text style={{ fontSize: 15, fontWeight: '700', color: colors.text }}>Billing Address</Text>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
<Text style={{ fontSize: 12, color: colors.textMuted }}>Same as primary</Text>
<Switch
value={billingSameAsGeneral}
onValueChange={setBillingSameAsGeneral}
trackColor={{ false: colors.border, true: colors.icon }}
thumbColor="#FFFFFF"
/>
</View>
</View>
{!billingSameAsGeneral && (
<>
<FormInput
label="Street Address"
value={billingStreet}
onChangeText={setBillingStreet}
placeholder="Enter billing address"
/>
<View style={styles.row}>
<View style={styles.rowHalf}>
<FormInput
label="City"
value={billingCity}
onChangeText={setBillingCity}
placeholder="City"
/>
</View>
<View style={styles.rowHalf}>
<FormInput
label="State"
value={billingState}
onChangeText={setBillingState}
placeholder="State"
/>
</View>
</View>
<View style={styles.row}>
<View style={styles.rowHalf}>
<FormInput
label="Zip Code"
value={billingZip}
onChangeText={setBillingZip}
placeholder="Zip"
keyboardType="numeric"
/>
</View>
<View style={styles.rowHalf}>
<FormPicker
label="Country"
value={billingCountry}
onValueChange={setBillingCountry}
options={countryOptions}
placeholder="Select Country"
searchable
/>
</View>
</View>
</>
)}
</View>
{/* Shipping Address */}
<View style={[styles.formCard, styles.cardSpacing]}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: colors.border, paddingBottom: 10, marginBottom: 20 }}>
<Text style={{ fontSize: 15, fontWeight: '700', color: colors.text }}>Shipping Address</Text>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
<Text style={{ fontSize: 12, color: colors.textMuted }}>Same as billing</Text>
<Switch
value={shippingSameAsBilling}
onValueChange={setShippingSameAsBilling}
trackColor={{ false: colors.border, true: colors.icon }}
thumbColor="#FFFFFF"
/>
</View>
</View>
{!shippingSameAsBilling && (
<>
<FormInput
label="Street Address"
value={shippingStreet}
onChangeText={setShippingStreet}
placeholder="Enter shipping address"
/>
<View style={styles.row}>
<View style={styles.rowHalf}>
<FormInput
label="City"
value={shippingCity}
onChangeText={setShippingCity}
placeholder="City"
/>
</View>
<View style={styles.rowHalf}>
<FormInput
label="State"
value={shippingState}
onChangeText={setShippingState}
placeholder="State"
/>
</View>
</View>
<View style={styles.row}>
<View style={styles.rowHalf}>
<FormInput
label="Zip Code"
value={shippingZip}
onChangeText={setShippingZip}
placeholder="Zip"
keyboardType="numeric"
/>
</View>
<View style={styles.rowHalf}>
<FormPicker
label="Country"
value={shippingCountry}
onValueChange={setShippingCountry}
options={countryOptions}
placeholder="Select Country"
searchable
/>
</View>
</View>
</>
)}
</View>
{/* Submit Button */}
<TouchableOpacity
style={[styles.submitButton, loading && { opacity: 0.8 }]}
onPress={handleSubmit}
disabled={loading}
activeOpacity={0.8}
>
{loading ? (
<ActivityIndicator size="small" color="#FFFFFF" />
) : (
<>
<Icon name="checkmark" size={20} color="#FFFFFF" style={styles.buttonIcon} />
<Text style={styles.submitButtonText}>Create Customer</Text>
</>
)}
</TouchableOpacity>
</ScrollView>
</KeyboardAvoidingView>
);
};