Add passport application flow until step 2 'Kuesioner Layanan Permohonan'
This commit is contained in:
@ -1,11 +1,493 @@
|
||||
import React from 'react';
|
||||
import {Text, View} from 'react-native';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {
|
||||
BackHandler,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import styles from './styles';
|
||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import {useNavigation} from '@react-navigation/native';
|
||||
import Colors from '../../../assets/styles/Colors';
|
||||
import RadioButtonOptionComponent from '../../components/RadioButtonOption';
|
||||
import {RootStackParamList} from '../../navigation/type';
|
||||
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
|
||||
import {Button, Dialog, PaperProvider, Portal} from 'react-native-paper';
|
||||
import StepIndicator from '../../components/StepIndicator';
|
||||
import TextInputComponent from '../../components/TextInput';
|
||||
import genderData from '../../data/DropdownData/GenderData';
|
||||
import civilStatusData from '../../data/DropdownData/CivilStatus';
|
||||
|
||||
type RegularPassportScreenNavigationProp = NativeStackNavigationProp<
|
||||
RootStackParamList,
|
||||
'RegularPassport'
|
||||
>;
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: 'Dewasa',
|
||||
description: 'WNI berusia di atas 17 tahun atau sudah menikah',
|
||||
value: 'adult',
|
||||
},
|
||||
{
|
||||
label: 'Anak',
|
||||
description: 'WNI berusia di bawah 17 tahun dan belum menikah',
|
||||
value: 'child',
|
||||
},
|
||||
];
|
||||
|
||||
const hasHadPassportBeforeOptions = [
|
||||
{
|
||||
label: 'Belum',
|
||||
description:
|
||||
'Belum pernah memiliki paspor atau belum pernah mengajukan permohonan paspor',
|
||||
value: 'not_yet',
|
||||
},
|
||||
{
|
||||
label: 'Sudah',
|
||||
description: '',
|
||||
value: 'already',
|
||||
},
|
||||
];
|
||||
|
||||
const previousPassportConditionOptions = [
|
||||
{
|
||||
label: 'Habis masa berlaku',
|
||||
description: 'null',
|
||||
value: 'expired',
|
||||
},
|
||||
{
|
||||
label: 'Penuh/Halaman Penuh',
|
||||
description: 'null',
|
||||
value: 'full_pages',
|
||||
},
|
||||
{
|
||||
label: 'Hilang',
|
||||
description: 'null',
|
||||
value: 'lost',
|
||||
},
|
||||
{
|
||||
label: 'Rusak',
|
||||
description: 'null',
|
||||
value: 'damaged',
|
||||
},
|
||||
{
|
||||
label: 'Hilang karena keadaan kahar',
|
||||
description: 'null',
|
||||
value: 'lost_due_to_force_majeure',
|
||||
},
|
||||
{
|
||||
label: 'Rusak karena keadaan kahar',
|
||||
description: 'null',
|
||||
value: 'damaged_due_to_force_majeure',
|
||||
},
|
||||
];
|
||||
|
||||
const renderApplicationStepsContent = (
|
||||
step: number,
|
||||
subStep: number,
|
||||
setStep: (step: number) => void,
|
||||
setSubStep: (step: number) => void,
|
||||
selectedOption: string,
|
||||
setSelectedOption: (val: string) => void,
|
||||
) => {
|
||||
if (step === 1) {
|
||||
switch (subStep) {
|
||||
case 1:
|
||||
return (
|
||||
<View style={styles.subStepContainer}>
|
||||
<View style={styles.subStepTextWrapper}>
|
||||
<Text style={styles.subStepTitle}>
|
||||
Ambil/Upload Foto KTP Anda
|
||||
</Text>
|
||||
<Text style={styles.subStepDesc}>
|
||||
Pastikan pencahayaan cukup, tulisan pada identitas terlihat
|
||||
jelas, dan jangan gunakan foto dari Live Mode sebelum
|
||||
melanjutkan.
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.nationalIdImage} />
|
||||
<View style={styles.subStepButtonContainer}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => setSubStep(2)}
|
||||
style={styles.subStepButtonActive}
|
||||
textColor={Colors.neutral100.color}>
|
||||
Pilih Foto
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => {}}
|
||||
style={styles.subStepButtonInActive}
|
||||
textColor={Colors.primary30.color}>
|
||||
Ulangi
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.subStepContainer}>
|
||||
<View style={styles.nationalIdImageContainer}>
|
||||
<View style={styles.nationalIdImageCropped} />
|
||||
</View>
|
||||
<View
|
||||
style={[styles.subStepTextInputContainer, {marginBottom: 24}]}>
|
||||
<TextInputComponent
|
||||
title="Nama Lengkap Pemohon"
|
||||
placeholder="Nama Lengkap Anda"
|
||||
isRequired
|
||||
/>
|
||||
<TextInputComponent
|
||||
title="NIK"
|
||||
placeholder="Nama NIK Anda"
|
||||
isRequired
|
||||
/>
|
||||
<View style={styles.subStepTextInputRowContainer}>
|
||||
<View style={styles.subStepTextInputFlex}>
|
||||
<TextInputComponent
|
||||
title="Tanggal Lahir"
|
||||
placeholder="DD/MM/YYYY"
|
||||
isRequired
|
||||
isDate
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.subStepTextInputFlex}>
|
||||
<TextInputComponent
|
||||
title="Jenis Kelamin"
|
||||
placeholder="Jenis Kelamin"
|
||||
isRequired
|
||||
isDropdown
|
||||
dropdownItemData={genderData}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<TextInputComponent
|
||||
title="Status Sipil"
|
||||
placeholder="Pilih Status Sipil Anda"
|
||||
isRequired
|
||||
isDropdown
|
||||
dropdownItemData={civilStatusData}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.subStepButtonContainer}>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => {
|
||||
setStep(2);
|
||||
setSubStep(1);
|
||||
}}
|
||||
style={styles.subStepButtonActive}
|
||||
textColor={Colors.neutral100.color}>
|
||||
Lanjut
|
||||
</Button>
|
||||
<Button
|
||||
mode="outlined"
|
||||
onPress={() => setSubStep(1)}
|
||||
style={styles.subStepButtonInActive}
|
||||
textColor={Colors.primary30.color}>
|
||||
Kembali
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (step === 2) {
|
||||
switch (subStep) {
|
||||
case 1:
|
||||
return (
|
||||
<View style={styles.subStepContainer}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
setStep(1);
|
||||
setSubStep(2);
|
||||
}}
|
||||
style={({pressed}) => [
|
||||
styles.subStepButtonBackWrapper,
|
||||
{
|
||||
transform: [{scale: pressed ? 0.99 : 1}],
|
||||
},
|
||||
]}>
|
||||
<Icon name="chevron-left" size={24} />
|
||||
<Text style={styles.subStepButtonBackText}>Kembali</Text>
|
||||
</Pressable>
|
||||
<View style={styles.subStepQuestionnaireOptionContainer}>
|
||||
<Text style={styles.questionnaireData}>
|
||||
Apakah Anda sudah pernah memiliki paspor?
|
||||
</Text>
|
||||
{hasHadPassportBeforeOptions.map(options => (
|
||||
<RadioButtonOptionComponent
|
||||
key={options.value}
|
||||
label={options.label}
|
||||
description={options.description}
|
||||
value={options.value}
|
||||
selectedValue={selectedOption}
|
||||
onSelect={value => {
|
||||
setSelectedOption(value);
|
||||
value === 'already' && setSubStep(2);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.subStepContainer}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
setSubStep(1);
|
||||
}}
|
||||
style={({pressed}) => [
|
||||
styles.subStepButtonBackWrapper,
|
||||
{
|
||||
transform: [{scale: pressed ? 0.99 : 1}],
|
||||
},
|
||||
]}>
|
||||
<Icon name="chevron-left" size={24} />
|
||||
<Text style={styles.subStepButtonBackText}>Kembali</Text>
|
||||
</Pressable>
|
||||
<View style={styles.subStepQuestionnaireOptionContainer}>
|
||||
<Text style={styles.questionnaireData}>
|
||||
Apakah Anda sudah pernah memiliki paspor?
|
||||
</Text>
|
||||
{previousPassportConditionOptions.map(options => (
|
||||
<RadioButtonOptionComponent
|
||||
key={options.value}
|
||||
label={options.label}
|
||||
description={options.description}
|
||||
value={options.value}
|
||||
selectedValue={selectedOption}
|
||||
onSelect={value => {
|
||||
setSelectedOption(value);
|
||||
value === 'already' && setSubStep(2);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => {
|
||||
setSubStep(3);
|
||||
}}
|
||||
style={styles.subStepButtonActive}
|
||||
textColor={Colors.neutral100.color}>
|
||||
Lanjut
|
||||
</Button>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
case 3:
|
||||
return (
|
||||
<View>
|
||||
<Text>Step 2.3</Text>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={() => {
|
||||
setStep(3);
|
||||
setSubStep(1);
|
||||
}}>
|
||||
Next
|
||||
</Button>
|
||||
<Button onPress={() => setSubStep(2)}>Back</Button>
|
||||
</View>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
switch (step) {
|
||||
case 3:
|
||||
return (
|
||||
<View>
|
||||
<Button mode="contained" onPress={() => setStep(4)}>
|
||||
Next
|
||||
</Button>
|
||||
<Button onPress={() => setStep(2)}>Back</Button>
|
||||
</View>
|
||||
);
|
||||
case 4:
|
||||
return (
|
||||
<View>
|
||||
<Button mode="contained" onPress={() => setStep(5)}>
|
||||
Next
|
||||
</Button>
|
||||
<Button onPress={() => setStep(3)}>Back</Button>
|
||||
</View>
|
||||
);
|
||||
case 5:
|
||||
return (
|
||||
<View>
|
||||
<Button mode="contained" onPress={() => setStep(6)}>
|
||||
Next
|
||||
</Button>
|
||||
<Button onPress={() => setStep(4)}>Back</Button>
|
||||
</View>
|
||||
);
|
||||
case 6:
|
||||
return (
|
||||
<View>
|
||||
<Button mode="contained" onPress={() => setStep(7)}>
|
||||
Next
|
||||
</Button>
|
||||
<Button onPress={() => setStep(5)}>Back</Button>
|
||||
</View>
|
||||
);
|
||||
case 7:
|
||||
return (
|
||||
<View>
|
||||
<Button onPress={() => setStep(6)}>Back</Button>
|
||||
</View>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
function RegularPassportScreen() {
|
||||
const navigation = useNavigation<RegularPassportScreenNavigationProp>();
|
||||
const [selectedOption, setSelectedOption] = useState('');
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [showApplicationStepsContent, setShowApplicationStepsContent] =
|
||||
useState(false);
|
||||
const [step, setStep] = useState(1);
|
||||
const [subStep, setSubStep] = useState(1);
|
||||
|
||||
const showDialog = () => setVisible(true);
|
||||
const hideDialog = () => setVisible(false);
|
||||
const completedSteps = [...Array(step - 1)].map((_, i) => i + 1);
|
||||
|
||||
const stepTitles: {[key: number]: string} = {
|
||||
1: 'Informasi Pribadi',
|
||||
2: 'Dokumen Pendukung',
|
||||
3: 'Pembayaran',
|
||||
4: 'Konfirmasi Data',
|
||||
5: 'Verifikasi',
|
||||
6: 'Pemrosesan',
|
||||
7: 'Selesai',
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (showApplicationStepsContent) {
|
||||
const backAction = () => {
|
||||
setShowApplicationStepsContent(false);
|
||||
return true;
|
||||
};
|
||||
|
||||
const backHandler = BackHandler.addEventListener(
|
||||
'hardwareBackPress',
|
||||
backAction,
|
||||
);
|
||||
|
||||
return () => backHandler.remove();
|
||||
}
|
||||
}, [showApplicationStepsContent]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Regular Passport Screen</Text>
|
||||
<PaperProvider>
|
||||
<StatusBar
|
||||
backgroundColor={visible ? '#295E70' : Colors.secondary30.color}
|
||||
barStyle="light-content"
|
||||
/>
|
||||
<View style={styles.appBarContainer}>
|
||||
<Icon
|
||||
name="arrow-left"
|
||||
size={24}
|
||||
style={styles.appBarIcon}
|
||||
color={Colors.neutral100.color}
|
||||
onPress={() => navigation.goBack()}
|
||||
/>
|
||||
<Text style={styles.appBarTitle}>Pengajuan Paspor Regular</Text>
|
||||
</View>
|
||||
{showApplicationStepsContent ? (
|
||||
<View style={styles.applicationStepsContainer}>
|
||||
<Text style={styles.stepTitle}>{stepTitles[step]}</Text>
|
||||
<StepIndicator
|
||||
currentStep={step}
|
||||
totalSteps={7}
|
||||
completedSteps={completedSteps}
|
||||
/>
|
||||
{renderApplicationStepsContent(
|
||||
step,
|
||||
subStep,
|
||||
setStep,
|
||||
setSubStep,
|
||||
selectedOption,
|
||||
setSelectedOption,
|
||||
)}
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<Text style={styles.questionnaireTitle}>
|
||||
Kuesioner Layanan Permohonan
|
||||
</Text>
|
||||
<View style={styles.questionnaireOptionContainer}>
|
||||
<Text style={styles.questionnaireData}>
|
||||
Untuk siapa paspor ini?
|
||||
</Text>
|
||||
{options.map(option => (
|
||||
<RadioButtonOptionComponent
|
||||
key={option.value}
|
||||
label={option.label}
|
||||
description={option.description}
|
||||
value={option.value}
|
||||
selectedValue={selectedOption}
|
||||
onSelect={value => {
|
||||
setSelectedOption(value);
|
||||
value === 'child'
|
||||
? showDialog()
|
||||
: setShowApplicationStepsContent(true);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
<Portal>
|
||||
<Dialog visible={visible} style={styles.dialogContainer}>
|
||||
<Dialog.Title style={styles.dialogTitle}>
|
||||
Pemberitahuan
|
||||
</Dialog.Title>
|
||||
<View style={styles.dialogContentContainer}>
|
||||
<Text style={styles.dialogDesc}>
|
||||
Permohonan paspor anak diwajibkan untuk didampingi oleh
|
||||
orang tua/wali yang sah pada saat datang ke Kantor Imigrasi
|
||||
untuk pelaksanaan wawancara dan pengambilan foto sidik jari.
|
||||
</Text>
|
||||
<View>
|
||||
<Button
|
||||
style={styles.buttonAgree}
|
||||
mode="contained"
|
||||
textColor={Colors.neutral100.color}
|
||||
onPress={() => {
|
||||
hideDialog();
|
||||
setShowApplicationStepsContent(true);
|
||||
}}>
|
||||
Lanjut
|
||||
</Button>
|
||||
<Button
|
||||
style={styles.buttonDontAgree}
|
||||
mode="outlined"
|
||||
textColor={Colors.primary30.color}
|
||||
onPress={() => hideDialog()}>
|
||||
Kembali
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</Dialog>
|
||||
</Portal>
|
||||
</>
|
||||
)}
|
||||
</PaperProvider>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,164 @@
|
||||
import {StyleSheet} from 'react-native';
|
||||
import Colors from '../../../assets/styles/Colors';
|
||||
import FontFamily from '../../../assets/styles/FontFamily';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignContent: 'center',
|
||||
backgroundColor: Colors.neutral100.color,
|
||||
},
|
||||
appBarTitle: {
|
||||
color: Colors.neutral100.color,
|
||||
...FontFamily.notoSansRegular,
|
||||
fontSize: 16,
|
||||
marginStart: 16,
|
||||
},
|
||||
appBarIcon: {
|
||||
marginLeft: 16,
|
||||
},
|
||||
appBarContainer: {
|
||||
height: 64,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: Colors.secondary30.color,
|
||||
},
|
||||
questionnaireTitle: {
|
||||
...FontFamily.notoSansExtraBold,
|
||||
fontSize: 18,
|
||||
marginTop: 24,
|
||||
marginBottom: 8,
|
||||
marginHorizontal: 16,
|
||||
color: Colors.secondary30.color,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
questionnaireOptionContainer: {
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.secondary50.color,
|
||||
borderRadius: 8,
|
||||
padding: 16,
|
||||
margin: 16,
|
||||
gap: 16,
|
||||
},
|
||||
questionnaireData: {
|
||||
...FontFamily.notoSansBold,
|
||||
fontSize: 14,
|
||||
color: Colors.primary30.color,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
dialogContainer: {
|
||||
backgroundColor: 'white',
|
||||
elevation: 0,
|
||||
shadowColor: 'transparent',
|
||||
borderRadius: 20,
|
||||
},
|
||||
dialogTitle: {
|
||||
fontSize: 22,
|
||||
color: Colors.secondary30.color,
|
||||
},
|
||||
dialogContentContainer: {
|
||||
marginHorizontal: 24,
|
||||
marginBottom: 24,
|
||||
gap: 16,
|
||||
},
|
||||
dialogDesc: {
|
||||
fontSize: 14,
|
||||
...FontFamily.notoSansRegular,
|
||||
includeFontPadding: false,
|
||||
color: Colors.primary30.color,
|
||||
},
|
||||
buttonAgree: {
|
||||
backgroundColor: Colors.primary30.color,
|
||||
marginTop: 12,
|
||||
},
|
||||
buttonDontAgree: {
|
||||
borderColor: Colors.primary30.color,
|
||||
marginTop: 12,
|
||||
},
|
||||
applicationStepsContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
stepTitle: {
|
||||
fontSize: 14,
|
||||
...FontFamily.notoSansBold,
|
||||
includeFontPadding: false,
|
||||
color: Colors.secondary30.color,
|
||||
paddingTop: 16,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: Colors.secondary70.color,
|
||||
},
|
||||
subStepContainer: {
|
||||
backgroundColor: Colors.neutral100.color,
|
||||
padding: 16,
|
||||
},
|
||||
nationalIdImage: {
|
||||
marginVertical: 16,
|
||||
height: 350,
|
||||
backgroundColor: Colors.primary70.color,
|
||||
borderRadius: 20,
|
||||
},
|
||||
subStepButtonContainer: {
|
||||
gap: 16,
|
||||
},
|
||||
subStepButtonActive: {
|
||||
backgroundColor: Colors.primary30.color,
|
||||
},
|
||||
subStepButtonInActive: {
|
||||
borderColor: Colors.primary30.color,
|
||||
},
|
||||
subStepTextWrapper: {
|
||||
gap: 10,
|
||||
},
|
||||
subStepTitle: {
|
||||
fontSize: 16,
|
||||
...FontFamily.notoSansExtraBold,
|
||||
color: Colors.secondary30.color,
|
||||
includeFontPadding: false,
|
||||
},
|
||||
subStepDesc: {
|
||||
includeFontPadding: false,
|
||||
fontSize: 12,
|
||||
color: Colors.primary10.color,
|
||||
...FontFamily.notoSansRegular,
|
||||
lineHeight: 20,
|
||||
},
|
||||
nationalIdImageContainer: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
nationalIdImageCropped: {
|
||||
marginBottom: 16,
|
||||
width: 225,
|
||||
height: 140,
|
||||
backgroundColor: Colors.primary70.color,
|
||||
borderRadius: 20,
|
||||
},
|
||||
subStepTextInputContainer: {
|
||||
gap: 16,
|
||||
},
|
||||
subStepTextInputRowContainer: {
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
},
|
||||
subStepTextInputFlex: {
|
||||
flex: 1,
|
||||
},
|
||||
subStepButtonBackWrapper: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
},
|
||||
subStepButtonBackText: {
|
||||
...FontFamily.notoSansRegular,
|
||||
includeFontPadding: false,
|
||||
fontSize: 10,
|
||||
},
|
||||
subStepQuestionnaireOptionContainer: {
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.secondary50.color,
|
||||
borderRadius: 8,
|
||||
padding: 16,
|
||||
marginVertical: 16,
|
||||
gap: 16,
|
||||
},
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user