Finalized the destination country dropdown feature in step 2, sub-step 7 of the supporting document

This commit is contained in:
Mochammad Adhi Buchori
2025-04-25 08:41:08 +07:00
parent 89f93aa46b
commit 2fa2b05918
6 changed files with 495 additions and 35 deletions

View File

@ -1,18 +1,26 @@
import * as React from 'react'; import * as React from 'react';
import {Platform, Pressable, StyleSheet, Text, View} from 'react-native'; import {Image, Platform, Pressable, StyleSheet, Text, View} from 'react-native';
import {TextInput} from 'react-native-paper'; import {TextInput} from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialIcons'; import Icon from 'react-native-vector-icons/MaterialIcons';
import Colors from '../../assets/styles/Colors'; import Colors from '../../assets/styles/Colors';
import FontFamily from '../../assets/styles/FontFamily'; import FontFamily from '../../assets/styles/FontFamily';
import DateTimePicker from '@react-native-community/datetimepicker'; import DateTimePicker from '@react-native-community/datetimepicker';
import {useState} from 'react'; import {useState} from 'react';
import {Dropdown} from 'react-native-element-dropdown'; import {Dropdown, SelectCountry} from 'react-native-element-dropdown';
type DropdownItem = { type DropdownItem = {
label: string; label: string;
value: string | number; value: string | number;
}; };
type DropdownCountryItem = {
value: string;
label: string;
image: {
uri: string;
};
};
interface TextInputComponentProps { interface TextInputComponentProps {
title?: string; title?: string;
placeholder?: string; placeholder?: string;
@ -20,7 +28,9 @@ interface TextInputComponentProps {
isRequired?: boolean; isRequired?: boolean;
isDate?: boolean; isDate?: boolean;
isDropdown?: boolean; isDropdown?: boolean;
isDropdownCountry?: boolean;
dropdownItemData?: DropdownItem[]; dropdownItemData?: DropdownItem[];
dropdownCountryItemData?: DropdownCountryItem[];
isDisabled?: boolean; isDisabled?: boolean;
supportText?: string; supportText?: string;
containerHeight?: any; containerHeight?: any;
@ -34,7 +44,9 @@ const TextInputComponent: React.FC<TextInputComponentProps> = ({
isRequired = false, isRequired = false,
isDate = false, isDate = false,
isDropdown = false, isDropdown = false,
isDropdownCountry = false,
dropdownItemData, dropdownItemData,
dropdownCountryItemData,
isDisabled = false, isDisabled = false,
supportText, supportText,
containerHeight, containerHeight,
@ -44,7 +56,13 @@ const TextInputComponent: React.FC<TextInputComponentProps> = ({
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined); const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
const [formattedDate, setFormattedDate] = useState<string>(''); const [formattedDate, setFormattedDate] = useState<string>('');
const [showPicker, setShowPicker] = useState(false); const [showPicker, setShowPicker] = useState(false);
const [genderValue, setGenderValue] = useState(null); const [dropdownValue, setDropdownValue] = useState(null);
const [country, setCountry] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const filteredItems = dropdownCountryItemData?.filter(item =>
item.label.toLowerCase().includes(searchQuery.toLowerCase()),
);
const inputStyle = [ const inputStyle = [
styles.containerBackground, styles.containerBackground,
@ -76,7 +94,95 @@ const TextInputComponent: React.FC<TextInputComponentProps> = ({
); );
}; };
const renderDropdownCountryItem = (item: any) => {
return (
<View style={styles.dropdownCountryItem}>
<Image source={{uri: item.image.uri}} style={styles.imageCountryStyle} />
<Text style={styles.dropdownTextItem}>{item.label}</Text>
</View>
);
};
const renderLeftIcon = () => {
const selectedItem = filteredItems?.find(item => item.value === country);
if (!selectedItem?.image?.uri) return null;
return (
<Image
source={{uri: selectedItem.image.uri}}
style={[styles.imageCountryStyle, {marginRight: 12}]}
/>
);
};
const renderInputSearch = (
searchQuery: string,
setSearchQuery: React.Dispatch<React.SetStateAction<string>>,
) => {
return (
<View style={styles.searchContainer}>
<TextInput
mode="outlined"
style={[inputStyle, isDisabled && styles.outlineColorDisabled]}
theme={{roundness: 12}}
placeholderTextColor={Colors.primary60.color}
activeOutlineColor={Colors.primary10.color}
value={searchQuery}
onChangeText={setSearchQuery}
placeholder="Cari"
textColor="#48454E"
contentStyle={{marginLeft: 48}}
left={
<TextInput.Icon
icon="magnify"
color="#48454E"
style={{marginLeft: 8}}
/>
}
/>
</View>
);
};
const renderInput = () => { const renderInput = () => {
if (isDropdownCountry) {
return (
<View>
{title && (
<View style={styles.titleContainer}>
<Text style={styles.title}>{title}</Text>
{isRequired && <Text style={styles.required}>*</Text>}
</View>
)}
<Dropdown
style={[styles.dropdown, isDisabled && styles.outlineColorDisabled]}
selectedTextStyle={styles.selectedTextStyle}
placeholderStyle={styles.placeholderDropdownStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
search
maxHeight={300}
renderInputSearch={() =>
renderInputSearch(searchQuery, setSearchQuery)
}
value={country}
data={filteredItems ?? []}
valueField="value"
labelField="label"
placeholder={placeholder}
searchPlaceholder="Cari"
onChange={item => {
setCountry(item.value);
}}
disable={isDisabled}
renderRightIcon={() => <Icon name="arrow-drop-down" size={20} />}
renderItem={renderDropdownCountryItem}
renderLeftIcon={renderLeftIcon}
/>
</View>
);
}
if (isDropdown) { if (isDropdown) {
return ( return (
<View> <View>
@ -96,9 +202,9 @@ const TextInputComponent: React.FC<TextInputComponentProps> = ({
labelField="label" labelField="label"
valueField="value" valueField="value"
placeholder={placeholder} placeholder={placeholder}
value={genderValue} value={dropdownValue}
onChange={item => { onChange={item => {
setGenderValue(item.value); setDropdownValue(item.value);
}} }}
disable={isDisabled} disable={isDisabled}
renderRightIcon={() => <Icon name="arrow-drop-down" size={20} />} renderRightIcon={() => <Icon name="arrow-drop-down" size={20} />}
@ -233,9 +339,18 @@ const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
}, },
dropdownCountryItem: {
padding: 16,
gap: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
dropdownTextItem: { dropdownTextItem: {
flex: 1, flex: 1,
fontSize: 13, fontSize: 13,
...FontFamily.notoSansRegular,
color: Colors.primary30.color,
}, },
supportText: { supportText: {
marginTop: 8, marginTop: 8,
@ -251,6 +366,21 @@ const styles = StyleSheet.create({
borderRadius: 12, borderRadius: 12,
borderColor: '#e3e3e5', borderColor: '#e3e3e5',
}, },
imageCountryStyle: {
width: 32,
height: 20,
borderRadius: 4,
borderWidth: 0.75,
borderColor: Colors.neutral90.color,
},
inputSearchStyle: {
fontSize: 14,
...FontFamily.notoSansRegular,
includeFontPadding: false,
},
searchContainer: {
margin: 16,
},
}); });
export default TextInputComponent; export default TextInputComponent;

View File

@ -0,0 +1,253 @@
const countryData = [
{
value: '1',
label: 'Indonesia',
image: {uri: 'https://flagcdn.com/w320/id.png'},
},
{
value: '2',
label: 'United States',
image: {uri: 'https://flagcdn.com/w320/us.png'},
},
{value: '3', label: 'Japan', image: {uri: 'https://flagcdn.com/w320/jp.png'}},
{
value: '4',
label: 'Germany',
image: {uri: 'https://flagcdn.com/w320/de.png'},
},
{
value: '5',
label: 'Australia',
image: {uri: 'https://flagcdn.com/w320/au.png'},
},
{
value: '6',
label: 'United Kingdom',
image: {uri: 'https://flagcdn.com/w320/gb.png'},
},
{
value: '7',
label: 'Canada',
image: {uri: 'https://flagcdn.com/w320/ca.png'},
},
{
value: '8',
label: 'South Korea',
image: {uri: 'https://flagcdn.com/w320/kr.png'},
},
{
value: '9',
label: 'France',
image: {uri: 'https://flagcdn.com/w320/fr.png'},
},
{
value: '10',
label: 'India',
image: {uri: 'https://flagcdn.com/w320/in.png'},
},
{
value: '11',
label: 'China',
image: {uri: 'https://flagcdn.com/w320/cn.png'},
},
{
value: '12',
label: 'Brazil',
image: {uri: 'https://flagcdn.com/w320/br.png'},
},
{
value: '13',
label: 'Russia',
image: {uri: 'https://flagcdn.com/w320/ru.png'},
},
{
value: '14',
label: 'Mexico',
image: {uri: 'https://flagcdn.com/w320/mx.png'},
},
{
value: '15',
label: 'Italy',
image: {uri: 'https://flagcdn.com/w320/it.png'},
},
{
value: '16',
label: 'Spain',
image: {uri: 'https://flagcdn.com/w320/es.png'},
},
{
value: '17',
label: 'Netherlands',
image: {uri: 'https://flagcdn.com/w320/nl.png'},
},
{
value: '18',
label: 'Sweden',
image: {uri: 'https://flagcdn.com/w320/se.png'},
},
{
value: '19',
label: 'Switzerland',
image: {uri: 'https://flagcdn.com/w320/ch.png'},
},
{
value: '20',
label: 'Norway',
image: {uri: 'https://flagcdn.com/w320/no.png'},
},
{
value: '21',
label: 'Denmark',
image: {uri: 'https://flagcdn.com/w320/dk.png'},
},
{
value: '22',
label: 'Finland',
image: {uri: 'https://flagcdn.com/w320/fi.png'},
},
{
value: '23',
label: 'New Zealand',
image: {uri: 'https://flagcdn.com/w320/nz.png'},
},
{
value: '24',
label: 'Argentina',
image: {uri: 'https://flagcdn.com/w320/ar.png'},
},
{
value: '25',
label: 'South Africa',
image: {uri: 'https://flagcdn.com/w320/za.png'},
},
{
value: '26',
label: 'Thailand',
image: {uri: 'https://flagcdn.com/w320/th.png'},
},
{
value: '27',
label: 'Malaysia',
image: {uri: 'https://flagcdn.com/w320/my.png'},
},
{
value: '28',
label: 'Philippines',
image: {uri: 'https://flagcdn.com/w320/ph.png'},
},
{
value: '29',
label: 'Vietnam',
image: {uri: 'https://flagcdn.com/w320/vn.png'},
},
{
value: '30',
label: 'Turkey',
image: {uri: 'https://flagcdn.com/w320/tr.png'},
},
{
value: '31',
label: 'Saudi Arabia',
image: {uri: 'https://flagcdn.com/w320/sa.png'},
},
{
value: '32',
label: 'United Arab Emirates',
image: {uri: 'https://flagcdn.com/w320/ae.png'},
},
{
value: '33',
label: 'Qatar',
image: {uri: 'https://flagcdn.com/w320/qa.png'},
},
{
value: '34',
label: 'Egypt',
image: {uri: 'https://flagcdn.com/w320/eg.png'},
},
{
value: '35',
label: 'Pakistan',
image: {uri: 'https://flagcdn.com/w320/pk.png'},
},
{
value: '36',
label: 'Bangladesh',
image: {uri: 'https://flagcdn.com/w320/bd.png'},
},
{
value: '37',
label: 'Nepal',
image: {uri: 'https://flagcdn.com/w320/np.png'},
},
{
value: '38',
label: 'Sri Lanka',
image: {uri: 'https://flagcdn.com/w320/lk.png'},
},
{value: '39', label: 'Iraq', image: {uri: 'https://flagcdn.com/w320/iq.png'}},
{value: '40', label: 'Iran', image: {uri: 'https://flagcdn.com/w320/ir.png'}},
{
value: '41',
label: 'Greece',
image: {uri: 'https://flagcdn.com/w320/gr.png'},
},
{
value: '42',
label: 'Portugal',
image: {uri: 'https://flagcdn.com/w320/pt.png'},
},
{
value: '43',
label: 'Poland',
image: {uri: 'https://flagcdn.com/w320/pl.png'},
},
{
value: '44',
label: 'Czech Republic',
image: {uri: 'https://flagcdn.com/w320/cz.png'},
},
{
value: '45',
label: 'Ukraine',
image: {uri: 'https://flagcdn.com/w320/ua.png'},
},
{
value: '46',
label: 'Hungary',
image: {uri: 'https://flagcdn.com/w320/hu.png'},
},
{
value: '47',
label: 'Austria',
image: {uri: 'https://flagcdn.com/w320/at.png'},
},
{
value: '48',
label: 'Belgium',
image: {uri: 'https://flagcdn.com/w320/be.png'},
},
{
value: '49',
label: 'Chile',
image: {uri: 'https://flagcdn.com/w320/cl.png'},
},
{
value: '50',
label: 'Colombia',
image: {uri: 'https://flagcdn.com/w320/co.png'},
},
{value: '51', label: 'Peru', image: {uri: 'https://flagcdn.com/w320/pe.png'}},
{
value: '52',
label: 'Nigeria',
image: {uri: 'https://flagcdn.com/w320/ng.png'},
},
{
value: '53',
label: 'Kenya',
image: {uri: 'https://flagcdn.com/w320/ke.png'},
},
];
export default countryData;

View File

@ -0,0 +1,9 @@
const jobData = [
{label: 'Software Engineer', value: '1'},
{label: 'Product Manager', value: '2'},
{label: 'UI/UX Designer', value: '3'},
{label: 'Data Analyst', value: '4'},
{label: 'Digital Marketer', value: '5'},
];
export default jobData;

View File

@ -0,0 +1,6 @@
const nationalityData = [
{label: 'Warga Negara Indonesia (WNI)', value: '1'},
{label: 'Warga Negara Asing (WNA)', value: '2'},
];
export default nationalityData;

View File

@ -6,8 +6,8 @@ import styles from '../styles';
import TextInputComponent from '../../../../components/TextInput'; import TextInputComponent from '../../../../components/TextInput';
import RadioButtonOptionComponent from '../../../../components/RadioButtonOption'; import RadioButtonOptionComponent from '../../../../components/RadioButtonOption';
import destinationCountryOptions from '../../../../data/Options/DestinationCountryOptions'; import destinationCountryOptions from '../../../../data/Options/DestinationCountryOptions';
import destinationCountryData from '../../../../data/DropdownData/DestinationCountryData';
import Colors from '../../../../../assets/styles/Colors'; import Colors from '../../../../../assets/styles/Colors';
import countryData from '../../../../data/DropdownData/CountryData';
type Step2SupportingDocsSubStep7Props = { type Step2SupportingDocsSubStep7Props = {
setSubStep: (step: number) => void; setSubStep: (step: number) => void;
@ -39,8 +39,8 @@ const Step2SupportingDocsSubStep7 = ({
<TextInputComponent <TextInputComponent
title="Negara mana yang akan Anda tuju?" title="Negara mana yang akan Anda tuju?"
placeholder="Masukkan negara tujuan" placeholder="Masukkan negara tujuan"
isDropdown isDropdownCountry
dropdownItemData={destinationCountryData} dropdownCountryItemData={countryData}
/> />
{destinationCountryOptions.map(option => ( {destinationCountryOptions.map(option => (
<RadioButtonOptionComponent <RadioButtonOptionComponent

View File

@ -1,10 +1,12 @@
import React from 'react'; import React from 'react';
import { ScrollView, View, Text, Pressable } from 'react-native'; import {ScrollView, View, Text, Pressable} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons'; import Icon from 'react-native-vector-icons/MaterialIcons';
import styles from '../styles'; import styles from '../styles';
import TextInputComponent from '../../../../components/TextInput'; import TextInputComponent from '../../../../components/TextInput';
import { Button } from 'react-native-paper'; import {Button} from 'react-native-paper';
import Colors from '../../../../../assets/styles/Colors'; import Colors from '../../../../../assets/styles/Colors';
import jobData from '../../../../data/DropdownData/JobData';
import nationalityData from '../../../../data/DropdownData/NationalityData';
const Step4DataConfirmationSubStep2 = ({ const Step4DataConfirmationSubStep2 = ({
setStep, setStep,
@ -21,32 +23,65 @@ const Step4DataConfirmationSubStep2 = ({
setStep(4); setStep(4);
setSubStep(1); setSubStep(1);
}} }}
style={({ pressed }) => [ style={({pressed}) => [
styles.subStepButtonBackWrapper, styles.subStepButtonBackWrapper,
{ {
transform: [{ scale: pressed ? 0.99 : 1 }], transform: [{scale: pressed ? 0.99 : 1}],
marginBottom: 8, marginBottom: 8,
}, },
]} ]}>
>
<Icon name="chevron-left" size={24} /> <Icon name="chevron-left" size={24} />
<Text style={styles.subStepButtonBackText}>Kembali</Text> <Text style={styles.subStepButtonBackText}>Kembali</Text>
</Pressable> </Pressable>
<Text style={styles.subStepDesc}> <Text style={styles.subStepDesc}>
Data di bawah ini harus sesuai dengan keterangan pada KTP pemohon. Data yang bertanda ( Data di bawah ini harus sesuai dengan keterangan pada KTP pemohon.
<Text style={{ color: Colors.indicatorRed.color }}>*</Text>) wajib diisi. Data yang bertanda (
<Text style={{color: Colors.indicatorRed.color}}>*</Text>) wajib
diisi.
</Text> </Text>
<View style={[styles.subStepQuestionnaireOptionContainer, { marginBottom: 0 }]}> <View
<Text style={styles.questionnaireDataSecondary}>Keterangan Pemohon</Text> style={[
<TextInputComponent title="Pekerjaan" placeholder="Pilih pekerjaan" isRequired isDropdown /> styles.subStepQuestionnaireOptionContainer,
<TextInputComponent title="Nomor Telepon" placeholder="Contoh: 08513456789" isRequired /> {marginBottom: 0},
]}>
<Text style={styles.questionnaireDataSecondary}>
Keterangan Pemohon
</Text>
<TextInputComponent
title="Pekerjaan"
placeholder="Pilih pekerjaan"
isRequired
isDropdown
dropdownItemData={jobData}
/>
<TextInputComponent
title="Nomor Telepon"
placeholder="Contoh: 08513456789"
isRequired
/>
</View> </View>
<View style={[styles.subStepQuestionnaireOptionContainer, { marginBottom: 0 }]}> <View
<Text style={styles.questionnaireDataSecondary}>Keterangan Ibu Pemohon</Text> style={[
<TextInputComponent title="Nama Ibu" placeholder="Masukkan nama lengkap ibu" isRequired /> styles.subStepQuestionnaireOptionContainer,
<TextInputComponent title="Kewarganegaraan Ibu" placeholder="Pilih kewarganegaraan ibu" isRequired isDropdown /> {marginBottom: 0},
]}>
<Text style={styles.questionnaireDataSecondary}>
Keterangan Ibu Pemohon
</Text>
<TextInputComponent
title="Nama Ibu"
placeholder="Masukkan nama lengkap ibu"
isRequired
/>
<TextInputComponent
title="Kewarganegaraan Ibu"
placeholder="Pilih kewarganegaraan ibu"
isRequired
isDropdown
dropdownItemData={nationalityData}
/>
<TextInputComponent <TextInputComponent
title="Alamat Ibu" title="Alamat Ibu"
placeholder="Masukkan alamat ibu" placeholder="Masukkan alamat ibu"
@ -57,10 +92,24 @@ const Step4DataConfirmationSubStep2 = ({
/> />
</View> </View>
<View style={[styles.subStepQuestionnaireOptionContainer, { marginBottom: 0 }]}> <View
<Text style={styles.questionnaireDataSecondary}>Keterangan Ayah Pemohon (Opsional)</Text> style={[
<TextInputComponent title="Nama Ayah" placeholder="Masukkan nama lengkap ayah" /> styles.subStepQuestionnaireOptionContainer,
<TextInputComponent title="Kewarganegaraan Ayah" placeholder="Pilih kewarganegaraan ayah" isDropdown /> {marginBottom: 0},
]}>
<Text style={styles.questionnaireDataSecondary}>
Keterangan Ayah Pemohon (Opsional)
</Text>
<TextInputComponent
title="Nama Ayah"
placeholder="Masukkan nama lengkap ayah"
/>
<TextInputComponent
title="Kewarganegaraan Ayah"
placeholder="Pilih kewarganegaraan ayah"
isDropdown
dropdownItemData={nationalityData}
/>
<TextInputComponent <TextInputComponent
title="Alamat Ayah" title="Alamat Ayah"
placeholder="Masukkan alamat ayah" placeholder="Masukkan alamat ayah"
@ -70,10 +119,24 @@ const Step4DataConfirmationSubStep2 = ({
/> />
</View> </View>
<View style={[styles.subStepQuestionnaireOptionContainer, { marginBottom: 32 }]}> <View
<Text style={styles.questionnaireDataSecondary}>Keterangan Pasangan Pemohon (Opsional)</Text> style={[
<TextInputComponent title="Nama Pasangan" placeholder="Masukkan nama lengkap pasangan" /> styles.subStepQuestionnaireOptionContainer,
<TextInputComponent title="Kewarganegaraan Pasangan" placeholder="Pilih kewarganegaraan pasangan" isDropdown /> {marginBottom: 32},
]}>
<Text style={styles.questionnaireDataSecondary}>
Keterangan Pasangan Pemohon (Opsional)
</Text>
<TextInputComponent
title="Nama Pasangan"
placeholder="Masukkan nama lengkap pasangan"
/>
<TextInputComponent
title="Kewarganegaraan Pasangan"
placeholder="Pilih kewarganegaraan pasangan"
isDropdown
dropdownItemData={nationalityData}
/>
<TextInputComponent <TextInputComponent
title="Alamat Pasangan" title="Alamat Pasangan"
placeholder="Masukkan alamat pasangan" placeholder="Masukkan alamat pasangan"
@ -86,9 +149,8 @@ const Step4DataConfirmationSubStep2 = ({
<Button <Button
mode="contained" mode="contained"
onPress={() => setStep(5)} onPress={() => setStep(5)}
style={[styles.subStepButtonContained, { marginBottom: 8 }]} style={[styles.subStepButtonContained, {marginBottom: 8}]}
textColor={Colors.neutral100.color} textColor={Colors.neutral100.color}>
>
Simpan Draft Simpan Draft
</Button> </Button>
</View> </View>