update logo, limit step navigation (1-6), and add custom toast
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 22 KiB |
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#2B3A51</color>
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
</resources>
|
</resources>
|
2
package-lock.json
generated
@ -30,7 +30,7 @@
|
|||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
"@babel/preset-env": "^7.25.3",
|
"@babel/preset-env": "^7.25.3",
|
||||||
"@babel/runtime": "^7.25.0",
|
"@babel/runtime": "^7.25.0",
|
||||||
"@react-native-community/cli": "15.0.1",
|
"@react-native-community/cli": "^15.0.1",
|
||||||
"@react-native-community/cli-platform-android": "15.0.1",
|
"@react-native-community/cli-platform-android": "15.0.1",
|
||||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
"@react-native-community/cli-platform-ios": "15.0.1",
|
||||||
"@react-native/babel-preset": "0.78.0",
|
"@react-native/babel-preset": "0.78.0",
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
"@babel/preset-env": "^7.25.3",
|
"@babel/preset-env": "^7.25.3",
|
||||||
"@babel/runtime": "^7.25.0",
|
"@babel/runtime": "^7.25.0",
|
||||||
"@react-native-community/cli": "15.0.1",
|
"@react-native-community/cli": "^15.0.1",
|
||||||
"@react-native-community/cli-platform-android": "15.0.1",
|
"@react-native-community/cli-platform-android": "15.0.1",
|
||||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
"@react-native-community/cli-platform-ios": "15.0.1",
|
||||||
"@react-native/babel-preset": "0.78.0",
|
"@react-native/babel-preset": "0.78.0",
|
||||||
|
53
src/components/InfoToast.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {StyleSheet, Text, View} from 'react-native';
|
||||||
|
import {Snackbar} from 'react-native-paper';
|
||||||
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import Colors from '../../assets/styles/Colors';
|
||||||
|
|
||||||
|
type InfoToastProps = {
|
||||||
|
visible: boolean;
|
||||||
|
message: string;
|
||||||
|
onDismiss: () => void;
|
||||||
|
duration?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const InfoToast = ({
|
||||||
|
visible,
|
||||||
|
message,
|
||||||
|
onDismiss,
|
||||||
|
duration = 2000,
|
||||||
|
}: InfoToastProps) => {
|
||||||
|
return (
|
||||||
|
<Snackbar
|
||||||
|
visible={visible}
|
||||||
|
onDismiss={onDismiss}
|
||||||
|
duration={duration}
|
||||||
|
style={styles.snackbar}>
|
||||||
|
<View style={styles.contentContainer}>
|
||||||
|
<Icon name="information" size={20} color="white" style={styles.icon} />
|
||||||
|
<Text style={styles.message}>{message}</Text>
|
||||||
|
</View>
|
||||||
|
</Snackbar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
snackbar: {
|
||||||
|
backgroundColor: Colors.secondary10.color,
|
||||||
|
borderRadius: 100,
|
||||||
|
margin: 16,
|
||||||
|
alignSelf: 'center',
|
||||||
|
},
|
||||||
|
contentContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default InfoToast;
|
@ -194,7 +194,10 @@ const TextInputComponent = (props: TextInputComponentProps) => {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
style={[styles.dropdown, isDisabled && styles.outlineColorDisabledDropdown]}
|
style={[
|
||||||
|
styles.dropdown,
|
||||||
|
isDisabled && styles.outlineColorDisabledDropdown,
|
||||||
|
]}
|
||||||
placeholderStyle={styles.placeholderDropdownStyle}
|
placeholderStyle={styles.placeholderDropdownStyle}
|
||||||
selectedTextStyle={styles.selectedTextStyle}
|
selectedTextStyle={styles.selectedTextStyle}
|
||||||
iconStyle={styles.iconStyle}
|
iconStyle={styles.iconStyle}
|
||||||
@ -264,7 +267,10 @@ const TextInputComponent = (props: TextInputComponentProps) => {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<Dropdown
|
<Dropdown
|
||||||
style={[styles.dropdown, isDisabled && styles.outlineColorDisabledDropdown]}
|
style={[
|
||||||
|
styles.dropdown,
|
||||||
|
isDisabled && styles.outlineColorDisabledDropdown,
|
||||||
|
]}
|
||||||
selectedTextStyle={styles.selectedTextStyle}
|
selectedTextStyle={styles.selectedTextStyle}
|
||||||
placeholderStyle={styles.placeholderDropdownStyle}
|
placeholderStyle={styles.placeholderDropdownStyle}
|
||||||
inputSearchStyle={styles.inputSearchStyle}
|
inputSearchStyle={styles.inputSearchStyle}
|
||||||
@ -311,11 +317,15 @@ const TextInputComponent = (props: TextInputComponentProps) => {
|
|||||||
<TextInput
|
<TextInput
|
||||||
mode="outlined"
|
mode="outlined"
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
style={[inputStyle, isDisabled && styles.outlineColorDisabledDropdown]}
|
style={[
|
||||||
|
inputStyle,
|
||||||
|
isDisabled && styles.outlineColorDisabledDropdown,
|
||||||
|
]}
|
||||||
theme={{roundness: 12}}
|
theme={{roundness: 12}}
|
||||||
placeholderTextColor={Colors.primary60.color}
|
placeholderTextColor={Colors.primary60.color}
|
||||||
editable={false}
|
editable={false}
|
||||||
value={formattedDate}
|
value={value}
|
||||||
|
onChangeText={onChangeText}
|
||||||
right={
|
right={
|
||||||
<TextInput.Icon
|
<TextInput.Icon
|
||||||
icon="menu-down"
|
icon="menu-down"
|
||||||
|
@ -101,26 +101,6 @@ const passportAppointmentData = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '6',
|
id: '6',
|
||||||
applicantName: 'Nabila Khairunisa',
|
|
||||||
applicantCode: '1038000007773344',
|
|
||||||
appointmentDate: 'Rabu, 19 April 2025',
|
|
||||||
appointmentTime: '13:00 - 14:00 WIB',
|
|
||||||
serviceUnit: 'Kantor Imigrasi Bekasi',
|
|
||||||
status: 'Menunggu Pembayaran',
|
|
||||||
submissionDate: 'Senin, 14 April 2025 23:45',
|
|
||||||
serviceCode: 'EH-MK2YPQ',
|
|
||||||
applicationDetails: {
|
|
||||||
nationalIdNumber: '3271234560009120007',
|
|
||||||
gender: 'Wanita',
|
|
||||||
applicationType: 'Penggantian Paspor',
|
|
||||||
replacementReason: 'Hilang',
|
|
||||||
applicationPurpose: 'Kuliah di Luar Negeri',
|
|
||||||
passportType: 'PASPOR BIASA ELEKTRONIK 5 TAHUN',
|
|
||||||
fee: '650.000',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '7',
|
|
||||||
applicantName: 'Ayaka Haishima',
|
applicantName: 'Ayaka Haishima',
|
||||||
applicantCode: '1038000008885566',
|
applicantCode: '1038000008885566',
|
||||||
appointmentDate: 'Selasa, 18 April 2025',
|
appointmentDate: 'Selasa, 18 April 2025',
|
||||||
@ -139,6 +119,26 @@ const passportAppointmentData = [
|
|||||||
fee: '350.000',
|
fee: '350.000',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
applicantName: 'Nabila Khairunisa',
|
||||||
|
applicantCode: '1038000007773344',
|
||||||
|
appointmentDate: 'Rabu, 19 April 2025',
|
||||||
|
appointmentTime: '13:00 - 14:00 WIB',
|
||||||
|
serviceUnit: 'Kantor Imigrasi Bekasi',
|
||||||
|
status: 'Menunggu Pembayaran',
|
||||||
|
submissionDate: 'Senin, 14 April 2025 23:45',
|
||||||
|
serviceCode: 'EH-MK2YPQ',
|
||||||
|
applicationDetails: {
|
||||||
|
nationalIdNumber: '3271234560009120007',
|
||||||
|
gender: 'Wanita',
|
||||||
|
applicationType: 'Penggantian Paspor',
|
||||||
|
replacementReason: 'Hilang',
|
||||||
|
applicationPurpose: 'Kuliah di Luar Negeri',
|
||||||
|
passportType: 'PASPOR BIASA ELEKTRONIK 5 TAHUN',
|
||||||
|
fee: '650.000',
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default passportAppointmentData;
|
export default passportAppointmentData;
|
||||||
|
@ -1,27 +1,31 @@
|
|||||||
import React, {useEffect, useRef, useState} from 'react';
|
// React & React Native Core
|
||||||
import {BackHandler, StatusBar, Text, View} from 'react-native';
|
import React, {RefObject, useEffect, useRef, useState} from 'react';
|
||||||
import styles from './styles';
|
import {BackHandler, StatusBar, Text, ToastAndroid, View} from 'react-native';
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
||||||
|
// React Navigation
|
||||||
import {useNavigation} from '@react-navigation/native';
|
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 {NativeStackNavigationProp} from '@react-navigation/native-stack';
|
||||||
|
|
||||||
|
// Third-Party Libraries
|
||||||
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import {PaperProvider} from 'react-native-paper';
|
import {PaperProvider} from 'react-native-paper';
|
||||||
|
|
||||||
|
// Components
|
||||||
import StepIndicator from '../../components/StepIndicator';
|
import StepIndicator from '../../components/StepIndicator';
|
||||||
|
import RadioButtonOptionComponent from '../../components/RadioButtonOption';
|
||||||
import DialogApplicationPassport from '../../components/dialog/DialogApplicationPassport';
|
import DialogApplicationPassport from '../../components/dialog/DialogApplicationPassport';
|
||||||
import DialogDontHaveYetPassport from '../../components/dialog/DialogDontHaveYetPassport';
|
import DialogDontHaveYetPassport from '../../components/dialog/DialogDontHaveYetPassport';
|
||||||
import DialogLostOrDamagedPassport from '../../components/dialog/DialogLostOrDamagedPassport';
|
import DialogLostOrDamagedPassport from '../../components/dialog/DialogLostOrDamagedPassport';
|
||||||
import Step7ApplicationFeeDetails from './steps/Step7ApplicationFeeDetails/Step7ApplicationFeeDetails';
|
import DialogCivilStatusDocumentsInfo from '../../components/dialog/DialogCivilStatusDocumentsInfo';
|
||||||
import Step6ApplicationTypeAndApplicantData from './steps/Step6ApplicationTypeAndApplicantData/Step6ApplicationTypeAndApplicantData';
|
import DialogSubmitSuccess from '../../components/dialog/DialogSubmitSuccess';
|
||||||
import Step5ApplicationTypeAndApplicantData from './steps/Step5ApplicationTypeAndApplicantData/Step5ApplicationTypeAndApplicantData';
|
import DialogFinalizationConfirmation from '../../components/dialog/DialogFinalizationConfirmation';
|
||||||
import Step3UploadDocuments from './steps/Step3UploadDocuments/Step3UploadDocuments';
|
import DialogPassportConditionInfo from '../../components/dialog/DialogPassportConditionInfo';
|
||||||
import {ToastAndroid} from 'react-native';
|
import DialogPassportTypeInfo from '../../components/dialog/DialogPassportTypeInfo';
|
||||||
|
import SheetEditData from '../../components/sheet/SheetEditData';
|
||||||
|
import SheetSearchLocation from '../../components/sheet/SheetSearchLocation';
|
||||||
|
import SheetSelectDate from '../../components/sheet/SheetSelectDate';
|
||||||
|
|
||||||
// Options Data
|
// Steps - Step Screens
|
||||||
import passportForOptions from '../../data/Options/PassportForOptions';
|
|
||||||
import Step4ApplicantAdditionalDataSubStep2 from './steps/Step4ApplicantAdditionalData/Step4ApplicantAdditionalDataSubStep2';
|
|
||||||
import Step4ApplicantAdditionalDataSubStep1 from './steps/Step4ApplicantAdditionalData/Step4ApplicantAdditionalDataSubStep1';
|
|
||||||
import Step1VerifyNikSubStep1 from './steps/Step1VerifyNik/Step1VerifyNikSubStep1';
|
import Step1VerifyNikSubStep1 from './steps/Step1VerifyNik/Step1VerifyNikSubStep1';
|
||||||
import Step1VerifyNikSubStep2 from './steps/Step1VerifyNik/Step1VerifyNikSubStep2';
|
import Step1VerifyNikSubStep2 from './steps/Step1VerifyNik/Step1VerifyNikSubStep2';
|
||||||
import Step1VerifyNikSubStep3 from './steps/Step1VerifyNik/Step1VerifyNikSubStep3';
|
import Step1VerifyNikSubStep3 from './steps/Step1VerifyNik/Step1VerifyNikSubStep3';
|
||||||
@ -36,14 +40,28 @@ import Step2PassportApplicationQuestionnaireSubStep8 from './steps/Step2Passport
|
|||||||
import Step2PassportApplicationQuestionnaireSubStep9 from './steps/Step2PassportApplicationQuestionnaire/Step2PassportApplicationQuestionnaireSubStep9';
|
import Step2PassportApplicationQuestionnaireSubStep9 from './steps/Step2PassportApplicationQuestionnaire/Step2PassportApplicationQuestionnaireSubStep9';
|
||||||
import Step2PassportApplicationQuestionnaireSubStep10 from './steps/Step2PassportApplicationQuestionnaire/Step2PassportApplicationQuestionnaireSubStep10';
|
import Step2PassportApplicationQuestionnaireSubStep10 from './steps/Step2PassportApplicationQuestionnaire/Step2PassportApplicationQuestionnaireSubStep10';
|
||||||
import Step2PassportApplicationQuestionnaireSubStep11 from './steps/Step2PassportApplicationQuestionnaire/Step2PassportApplicationQuestionnaireSubStep11';
|
import Step2PassportApplicationQuestionnaireSubStep11 from './steps/Step2PassportApplicationQuestionnaire/Step2PassportApplicationQuestionnaireSubStep11';
|
||||||
import DialogCivilStatusDocumentsInfo from '../../components/dialog/DialogCivilStatusDocumentsInfo';
|
import Step3UploadDocuments from './steps/Step3UploadDocuments/Step3UploadDocuments';
|
||||||
import DialogSubmitSuccess from '../../components/dialog/DialogSubmitSuccess';
|
import Step4ApplicantAdditionalDataSubStep1 from './steps/Step4ApplicantAdditionalData/Step4ApplicantAdditionalDataSubStep1';
|
||||||
import DialogFinalizationConfirmation from '../../components/dialog/DialogFinalizationConfirmation';
|
import Step4ApplicantAdditionalDataSubStep2 from './steps/Step4ApplicantAdditionalData/Step4ApplicantAdditionalDataSubStep2';
|
||||||
import DialogPassportConditionInfo from '../../components/dialog/DialogPassportConditionInfo';
|
import Step5ApplicationTypeAndApplicantData from './steps/Step5ApplicationTypeAndApplicantData/Step5ApplicationTypeAndApplicantData';
|
||||||
import DialogPassportTypeInfo from '../../components/dialog/DialogPassportTypeInfo';
|
import Step6ApplicationTypeAndApplicantData from './steps/Step6ApplicationTypeAndApplicantData/Step6ApplicationTypeAndApplicantData';
|
||||||
import SheetEditData from '../../components/sheet/SheetEditData';
|
import Step7ApplicationFeeDetails from './steps/Step7ApplicationFeeDetails/Step7ApplicationFeeDetails';
|
||||||
import SheetSearchLocation from '../../components/sheet/SheetSearchLocation';
|
|
||||||
import SheetSelectDate from '../../components/sheet/SheetSelectDate';
|
// Navigation
|
||||||
|
import {RootStackParamList} from '../../navigation/type';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {
|
||||||
|
StepValidationStatus,
|
||||||
|
StepValidationStatusSetter,
|
||||||
|
} from '../../../types/step';
|
||||||
|
|
||||||
|
// Data & Styles
|
||||||
|
import passportForOptions from '../../data/Options/PassportForOptions';
|
||||||
|
import Colors from '../../../assets/styles/Colors';
|
||||||
|
import styles from './styles';
|
||||||
|
import {changeStep} from '../../utils/stepNavigation';
|
||||||
|
import InfoToast from '../../components/InfoToast';
|
||||||
|
|
||||||
type RegularPassportScreenNavigationProp = NativeStackNavigationProp<
|
type RegularPassportScreenNavigationProp = NativeStackNavigationProp<
|
||||||
RootStackParamList,
|
RootStackParamList,
|
||||||
@ -51,6 +69,7 @@ type RegularPassportScreenNavigationProp = NativeStackNavigationProp<
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
type RenderApplicationStepsContentProps = {
|
type RenderApplicationStepsContentProps = {
|
||||||
|
navigation: any;
|
||||||
step: number;
|
step: number;
|
||||||
subStep: number;
|
subStep: number;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
@ -74,15 +93,16 @@ type RenderApplicationStepsContentProps = {
|
|||||||
showSelectDateSheet: () => void;
|
showSelectDateSheet: () => void;
|
||||||
selectedDestinationCountryOption: string;
|
selectedDestinationCountryOption: string;
|
||||||
setSelectedDestinationCountryOption: (val: string) => void;
|
setSelectedDestinationCountryOption: (val: string) => void;
|
||||||
setStepValidationStatus: React.Dispatch<
|
stepValidationStatus: StepValidationStatus;
|
||||||
React.SetStateAction<Record<number, 'incomplete' | 'completed' | 'invalid'>>
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
>;
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RenderApplicationStepsContent = (
|
const RenderApplicationStepsContent = (
|
||||||
props: RenderApplicationStepsContentProps,
|
props: RenderApplicationStepsContentProps,
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
|
navigation,
|
||||||
step,
|
step,
|
||||||
subStep,
|
subStep,
|
||||||
setStep,
|
setStep,
|
||||||
@ -107,6 +127,7 @@ const RenderApplicationStepsContent = (
|
|||||||
showSearchLocationSheet,
|
showSearchLocationSheet,
|
||||||
showSelectDateSheet,
|
showSelectDateSheet,
|
||||||
setStepValidationStatus,
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
if (step === 1) {
|
if (step === 1) {
|
||||||
@ -118,14 +139,17 @@ const RenderApplicationStepsContent = (
|
|||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<Step1VerifyNikSubStep3
|
<Step1VerifyNikSubStep3
|
||||||
|
step={step}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
setSubStep={setSubStep}
|
||||||
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
onSubStepValidation={isValid => {
|
onSubStepValidation={isValid => {
|
||||||
setStepValidationStatus(prev => ({
|
setStepValidationStatus(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
1: isValid ? 'completed' : 'invalid',
|
1: isValid ? 'completed' : 'invalid',
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
@ -138,11 +162,14 @@ const RenderApplicationStepsContent = (
|
|||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Step2PassportApplicationQuestionnaireSubStep1
|
<Step2PassportApplicationQuestionnaireSubStep1
|
||||||
|
step={step}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
setSubStep={setSubStep}
|
||||||
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
selectedPassportOption={selectedPassportOption}
|
selectedPassportOption={selectedPassportOption}
|
||||||
setSelectedPassportOption={setSelectedPassportOption}
|
setSelectedPassportOption={setSelectedPassportOption}
|
||||||
showDontHaveYetDialog={showDontHaveYetDialog}
|
showDontHaveYetDialog={showDontHaveYetDialog}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
@ -209,16 +236,27 @@ const RenderApplicationStepsContent = (
|
|||||||
case 10:
|
case 10:
|
||||||
return (
|
return (
|
||||||
<Step2PassportApplicationQuestionnaireSubStep10
|
<Step2PassportApplicationQuestionnaireSubStep10
|
||||||
|
step={step}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
setSubStep={setSubStep}
|
||||||
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
selectedDestinationCountryOption={selectedDestinationCountryOption}
|
selectedDestinationCountryOption={selectedDestinationCountryOption}
|
||||||
|
onSubStepValidation={isValid => {
|
||||||
|
setStepValidationStatus(prev => ({
|
||||||
|
...prev,
|
||||||
|
2: isValid ? 'completed' : 'invalid',
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 11:
|
case 11:
|
||||||
return (
|
return (
|
||||||
<Step2PassportApplicationQuestionnaireSubStep11
|
<Step2PassportApplicationQuestionnaireSubStep11
|
||||||
|
step={step}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
setSubStep={setSubStep}
|
||||||
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
selectedOption={selectedOption}
|
selectedOption={selectedOption}
|
||||||
setSelectedOption={setSelectedOption}
|
setSelectedOption={setSelectedOption}
|
||||||
onSubStepValidation={isValid => {
|
onSubStepValidation={isValid => {
|
||||||
@ -227,6 +265,7 @@ const RenderApplicationStepsContent = (
|
|||||||
2: isValid ? 'completed' : 'invalid',
|
2: isValid ? 'completed' : 'invalid',
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
@ -239,17 +278,29 @@ const RenderApplicationStepsContent = (
|
|||||||
case 1:
|
case 1:
|
||||||
return (
|
return (
|
||||||
<Step4ApplicantAdditionalDataSubStep1
|
<Step4ApplicantAdditionalDataSubStep1
|
||||||
|
step={step}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
setSubStep={setSubStep}
|
||||||
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
checkedOption={checkedOption}
|
checkedOption={checkedOption}
|
||||||
setCheckedOption={setCheckedOption}
|
setCheckedOption={setCheckedOption}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return (
|
return (
|
||||||
<Step4ApplicantAdditionalDataSubStep2
|
<Step4ApplicantAdditionalDataSubStep2
|
||||||
|
step={step}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
setSubStep={setSubStep}
|
||||||
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
|
onSubStepValidation={isValid => {
|
||||||
|
setStepValidationStatus(prev => ({
|
||||||
|
...prev,
|
||||||
|
4: isValid ? 'completed' : 'invalid',
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -258,8 +309,10 @@ const RenderApplicationStepsContent = (
|
|||||||
case 3:
|
case 3:
|
||||||
return (
|
return (
|
||||||
<Step3UploadDocuments
|
<Step3UploadDocuments
|
||||||
|
step={step}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
setSubStep={setSubStep}
|
||||||
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
selectedPassportOption={selectedPassportOption}
|
selectedPassportOption={selectedPassportOption}
|
||||||
showCivilStatusDocumentsInfoDialog={
|
showCivilStatusDocumentsInfoDialog={
|
||||||
showCivilStatusDocumentsInfoDialog
|
showCivilStatusDocumentsInfoDialog
|
||||||
@ -271,14 +324,21 @@ const RenderApplicationStepsContent = (
|
|||||||
3: isValid ? 'completed' : 'invalid',
|
3: isValid ? 'completed' : 'invalid',
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 5:
|
case 5:
|
||||||
return (
|
return (
|
||||||
<Step5ApplicationTypeAndApplicantData
|
<Step5ApplicationTypeAndApplicantData
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
setSubStep={setSubStep}
|
|
||||||
showEditDataSheet={showEditDataSheet}
|
showEditDataSheet={showEditDataSheet}
|
||||||
|
navigation={navigation}
|
||||||
|
onSubStepValidation={() => {
|
||||||
|
setStepValidationStatus(prev => ({
|
||||||
|
...prev,
|
||||||
|
5: 'completed',
|
||||||
|
}));
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 6:
|
case 6:
|
||||||
@ -297,6 +357,12 @@ const RenderApplicationStepsContent = (
|
|||||||
<Step7ApplicationFeeDetails
|
<Step7ApplicationFeeDetails
|
||||||
showSubmitSuccessDialog={showSubmitSuccessDialog}
|
showSubmitSuccessDialog={showSubmitSuccessDialog}
|
||||||
setLastCompletedSteps={setLastCompletedSteps}
|
setLastCompletedSteps={setLastCompletedSteps}
|
||||||
|
onSubStepValidation={() => {
|
||||||
|
setStepValidationStatus(prev => ({
|
||||||
|
...prev,
|
||||||
|
7: 'completed',
|
||||||
|
}));
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
@ -362,9 +428,12 @@ function RegularPassportScreen() {
|
|||||||
const [checkedOption, setCheckedOption] = useState(false);
|
const [checkedOption, setCheckedOption] = useState(false);
|
||||||
const [showApplicationStepsContent, setShowApplicationStepsContent] =
|
const [showApplicationStepsContent, setShowApplicationStepsContent] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
|
const [toastVisible, setToastVisible] = useState(false);
|
||||||
|
const [toastMessage, setToastMessage] = useState('');
|
||||||
|
|
||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
const [subStep, setSubStep] = useState(1);
|
const [subStep, setSubStep] = useState(1);
|
||||||
|
|
||||||
const [completedSteps, setCompletedSteps] = useState<number[]>(
|
const [completedSteps, setCompletedSteps] = useState<number[]>(
|
||||||
[...Array(step - 1)].map((_, i) => i + 1),
|
[...Array(step - 1)].map((_, i) => i + 1),
|
||||||
);
|
);
|
||||||
@ -512,6 +581,11 @@ function RegularPassportScreen() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showInfoToast = (msg: string) => {
|
||||||
|
setToastMessage(msg);
|
||||||
|
setToastVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
// Render steps or questionnaire
|
// Render steps or questionnaire
|
||||||
const renderApplicationStepsContent = showApplicationStepsContent ? (
|
const renderApplicationStepsContent = showApplicationStepsContent ? (
|
||||||
<>
|
<>
|
||||||
@ -523,67 +597,33 @@ function RegularPassportScreen() {
|
|||||||
completedSteps={completedSteps}
|
completedSteps={completedSteps}
|
||||||
validationStatus={stepValidationStatus}
|
validationStatus={stepValidationStatus}
|
||||||
onStepPress={(targetStep: number) => {
|
onStepPress={(targetStep: number) => {
|
||||||
const isCurrentStepIn5to7 = step >= 5 && step <= 7;
|
const isCurrentStep7 = step === 7;
|
||||||
const isTargetStepIn1to4 = targetStep >= 1 && targetStep <= 4;
|
const isTargetStepIn1to6 = targetStep >= 1 && targetStep <= 6;
|
||||||
const isTargetStepIn5to7 = targetStep >= 5 && targetStep <= 7;
|
|
||||||
|
|
||||||
const isStep1to4Completed = [1, 2, 3, 4].every(
|
const toastMessage = isCurrentStep7
|
||||||
s => stepValidationStatus[s] === 'completed',
|
? 'Tak dapat kembali – langkah terakhir.'
|
||||||
);
|
: !isTargetStepIn1to6
|
||||||
|
? 'Lengkapi langkah 1 – 6 dulu'
|
||||||
|
: null;
|
||||||
|
|
||||||
if (!isCurrentStepIn5to7 && isTargetStepIn5to7) {
|
if (toastMessage) {
|
||||||
ToastAndroid.show(
|
showInfoToast(toastMessage);
|
||||||
'Lengkapi langkah 1 – 4 dulu',
|
|
||||||
ToastAndroid.SHORT,
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
changeStep({
|
||||||
isCurrentStepIn5to7 &&
|
currentStep: step,
|
||||||
isStep1to4Completed &&
|
targetStep: targetStep,
|
||||||
isTargetStepIn1to4
|
setStep,
|
||||||
) {
|
setSubStep: () => setSubStep(1),
|
||||||
ToastAndroid.show(
|
setStepValidationStatus,
|
||||||
'Hanya dapat berpindah di langkah 5 – 7.',
|
editedCompletedRef,
|
||||||
ToastAndroid.SHORT,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setStepValidationStatus(prev => {
|
|
||||||
const next = {...prev};
|
|
||||||
|
|
||||||
if (step !== targetStep && editedCompletedRef.current.has(step)) {
|
|
||||||
next[step] = 'completed';
|
|
||||||
editedCompletedRef.current.delete(step);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev[targetStep] === 'completed') {
|
|
||||||
editedCompletedRef.current.add(targetStep);
|
|
||||||
}
|
|
||||||
|
|
||||||
next[targetStep] = 'incomplete';
|
|
||||||
|
|
||||||
if (targetStep > step) {
|
|
||||||
for (let s = 1; s < targetStep; s++) {
|
|
||||||
if (next[s] !== 'completed') next[s] = 'invalid';
|
|
||||||
}
|
|
||||||
} else if (targetStep < step) {
|
|
||||||
for (let s = step; s > targetStep; s--) {
|
|
||||||
if (next[s] !== 'completed') next[s] = 'invalid';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return next;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setStep(targetStep);
|
|
||||||
setSubStep(1);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RenderApplicationStepsContent
|
<RenderApplicationStepsContent
|
||||||
|
navigation={navigation}
|
||||||
step={step}
|
step={step}
|
||||||
subStep={subStep}
|
subStep={subStep}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
@ -613,7 +653,9 @@ function RegularPassportScreen() {
|
|||||||
showEditDataSheet={showEditDataSheet}
|
showEditDataSheet={showEditDataSheet}
|
||||||
showSearchLocationSheet={showSearchLocationSheet}
|
showSearchLocationSheet={showSearchLocationSheet}
|
||||||
showSelectDateSheet={showSelectDateSheet}
|
showSelectDateSheet={showSelectDateSheet}
|
||||||
|
stepValidationStatus={stepValidationStatus}
|
||||||
setStepValidationStatus={setStepValidationStatus}
|
setStepValidationStatus={setStepValidationStatus}
|
||||||
|
editedCompletedRef={editedCompletedRef}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@ -672,7 +714,19 @@ function RegularPassportScreen() {
|
|||||||
visible={visibleFinalizationConfirmationDialog}
|
visible={visibleFinalizationConfirmationDialog}
|
||||||
onClose={hideFinalizationConfirmationDialog}
|
onClose={hideFinalizationConfirmationDialog}
|
||||||
onContinue={() => {
|
onContinue={() => {
|
||||||
setStep(7);
|
setStepValidationStatus(prev => ({
|
||||||
|
...prev,
|
||||||
|
6: 'completed',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const canProceedToStep7 = [1, 2, 3, 4, 5, 6].every(
|
||||||
|
s => stepValidationStatus[s] === 'completed',
|
||||||
|
);
|
||||||
|
|
||||||
|
!canProceedToStep7
|
||||||
|
? showInfoToast('Lengkapi semua langkah terlebih dahulu.')
|
||||||
|
: setStep(7);
|
||||||
|
|
||||||
hideFinalizationConfirmationDialog();
|
hideFinalizationConfirmationDialog();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -753,6 +807,11 @@ function RegularPassportScreen() {
|
|||||||
setShowApplicationStepsContent(true);
|
setShowApplicationStepsContent(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<InfoToast
|
||||||
|
visible={toastVisible}
|
||||||
|
message={toastMessage}
|
||||||
|
onDismiss={() => setToastVisible(false)}
|
||||||
|
/>
|
||||||
</PaperProvider>
|
</PaperProvider>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, {RefObject, useState} from 'react';
|
||||||
import {ScrollView, View} from 'react-native';
|
import {ScrollView, View} from 'react-native';
|
||||||
import {Button} from 'react-native-paper';
|
import {Button} from 'react-native-paper';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
@ -6,23 +6,31 @@ import TextInputComponent from '../../../../components/TextInput';
|
|||||||
import genderData from '../../../../data/DropdownData/GenderData';
|
import genderData from '../../../../data/DropdownData/GenderData';
|
||||||
import civilStatusData from '../../../../data/DropdownData/CivilStatusData';
|
import civilStatusData from '../../../../data/DropdownData/CivilStatusData';
|
||||||
import Colors from '../../../../../assets/styles/Colors';
|
import Colors from '../../../../../assets/styles/Colors';
|
||||||
|
import {changeStep} from '../../../../utils/stepNavigation';
|
||||||
|
import {StepValidationStatusSetter} from '../../../../../types/step';
|
||||||
|
|
||||||
type Step1VerifyNikSubStep3Props = {
|
type Step1VerifyNikSubStep3Props = {
|
||||||
|
step: number;
|
||||||
setStep: (val: number) => void;
|
setStep: (val: number) => void;
|
||||||
setSubStep: (val: number) => void;
|
setSubStep: (val: number) => void;
|
||||||
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
onSubStepValidation: (isValid: boolean) => void;
|
onSubStepValidation: (isValid: boolean) => void;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step1VerifyNikSubStep3 = ({
|
const Step1VerifyNikSubStep3 = ({
|
||||||
|
step,
|
||||||
setStep,
|
setStep,
|
||||||
setSubStep,
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
onSubStepValidation,
|
onSubStepValidation,
|
||||||
|
editedCompletedRef,
|
||||||
}: Step1VerifyNikSubStep3Props) => {
|
}: Step1VerifyNikSubStep3Props) => {
|
||||||
const [fullName, setFullName] = React.useState('');
|
const [fullName, setFullName] = useState('');
|
||||||
const [nik, setNik] = React.useState('');
|
const [nik, setNik] = useState('');
|
||||||
const [birthDate, setBirthDate] = React.useState('');
|
const [birthDate, setBirthDate] = useState('');
|
||||||
const [gender, setGender] = React.useState('');
|
const [gender, setGender] = useState('');
|
||||||
const [civilStatus, setCivilStatus] = React.useState('');
|
const [civilStatus, setCivilStatus] = useState('');
|
||||||
|
|
||||||
const onNextPress = () => {
|
const onNextPress = () => {
|
||||||
const isFormValid =
|
const isFormValid =
|
||||||
@ -38,8 +46,14 @@ const Step1VerifyNikSubStep3 = ({
|
|||||||
onSubStepValidation(false);
|
onSubStepValidation(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setStep(2);
|
changeStep({
|
||||||
setSubStep(1);
|
currentStep: step,
|
||||||
|
targetStep: 2,
|
||||||
|
setStep,
|
||||||
|
setSubStep: () => setSubStep(1),
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,36 +1,52 @@
|
|||||||
import React from 'react';
|
import React, {RefObject, useRef} from 'react';
|
||||||
import {View, Pressable, Text} from 'react-native';
|
import {View, Pressable, Text} from 'react-native';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import RadioButtonOptionComponent from '../../../../components/RadioButtonOption';
|
import RadioButtonOptionComponent from '../../../../components/RadioButtonOption';
|
||||||
import hasHadPassportBeforeOptions from '../../../../data/Options/HasHadPassportBeforeOptions';
|
import hasHadPassportBeforeOptions from '../../../../data/Options/HasHadPassportBeforeOptions';
|
||||||
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 {changeStep} from '../../../../utils/stepNavigation';
|
||||||
|
import {StepValidationStatusSetter} from '../../../../../types/step';
|
||||||
|
|
||||||
type Step2PassportApplicationQuestionnaireSubStep1Props = {
|
type Step2PassportApplicationQuestionnaireSubStep1Props = {
|
||||||
|
step: number;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setSubStep: (subStep: number) => void;
|
setSubStep: (subStep: number) => void;
|
||||||
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
selectedPassportOption: string;
|
selectedPassportOption: string;
|
||||||
setSelectedPassportOption: (value: string) => void;
|
setSelectedPassportOption: (value: string) => void;
|
||||||
showDontHaveYetDialog: () => void;
|
showDontHaveYetDialog: () => void;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step2PassportApplicationQuestionnaireSubStep1 = ({
|
const Step2PassportApplicationQuestionnaireSubStep1 = ({
|
||||||
|
step,
|
||||||
setStep,
|
setStep,
|
||||||
setSubStep,
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
selectedPassportOption,
|
selectedPassportOption,
|
||||||
setSelectedPassportOption,
|
setSelectedPassportOption,
|
||||||
showDontHaveYetDialog,
|
showDontHaveYetDialog,
|
||||||
|
editedCompletedRef,
|
||||||
}: Step2PassportApplicationQuestionnaireSubStep1Props) => {
|
}: Step2PassportApplicationQuestionnaireSubStep1Props) => {
|
||||||
|
const onBackPress = () => {
|
||||||
|
changeStep({
|
||||||
|
currentStep: step,
|
||||||
|
targetStep: 1,
|
||||||
|
setStep,
|
||||||
|
setSubStep: () => setSubStep(3),
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.subStepContainer}>
|
<View style={styles.subStepContainer}>
|
||||||
<Pressable
|
<Pressable
|
||||||
style={({pressed}) => ({
|
style={({pressed}) => ({
|
||||||
transform: [{scale: pressed ? 0.99 : 1}],
|
transform: [{scale: pressed ? 0.99 : 1}],
|
||||||
})}
|
})}
|
||||||
onPress={() => {
|
onPress={onBackPress}>
|
||||||
setStep(1);
|
|
||||||
setSubStep(3);
|
|
||||||
}}>
|
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon="chevron-left"
|
icon="chevron-left"
|
||||||
|
@ -1,21 +1,62 @@
|
|||||||
import React from 'react';
|
import React, {RefObject, useState} from 'react';
|
||||||
import {ScrollView, View, Text, Pressable} from 'react-native';
|
import {ScrollView, View, Text, Pressable} from 'react-native';
|
||||||
import {Button} from 'react-native-paper';
|
import {Button} from 'react-native-paper';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import TextInputComponent from '../../../../components/TextInput';
|
import TextInputComponent from '../../../../components/TextInput';
|
||||||
import Colors from '../../../../../assets/styles/Colors';
|
import Colors from '../../../../../assets/styles/Colors';
|
||||||
import familyRelationshipData from '../../../../data/DropdownData/FamilyRelationshipData';
|
import familyRelationshipData from '../../../../data/DropdownData/FamilyRelationshipData';
|
||||||
|
import {changeStep} from '../../../../utils/stepNavigation';
|
||||||
|
import {StepValidationStatusSetter} from '../../../../../types/step';
|
||||||
|
|
||||||
type Step2PassportApplicationQuestionnaireSubStep10Props = {
|
type Step2PassportApplicationQuestionnaireSubStep10Props = {
|
||||||
selectedDestinationCountryOption: string;
|
step: number;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setSubStep: (step: number) => void;
|
setSubStep: (step: number) => void;
|
||||||
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
|
selectedDestinationCountryOption: string;
|
||||||
|
onSubStepValidation: (isValid: boolean) => void;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step2PassportApplicationQuestionnaireSubStep10 = (
|
const Step2PassportApplicationQuestionnaireSubStep10 = (
|
||||||
props: Step2PassportApplicationQuestionnaireSubStep10Props,
|
props: Step2PassportApplicationQuestionnaireSubStep10Props,
|
||||||
) => {
|
) => {
|
||||||
const {selectedDestinationCountryOption, setStep, setSubStep} = props;
|
const {
|
||||||
|
step,
|
||||||
|
setStep,
|
||||||
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
selectedDestinationCountryOption,
|
||||||
|
onSubStepValidation,
|
||||||
|
editedCompletedRef,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const [relativeName, setRelativeName] = useState('');
|
||||||
|
const [relativePhone, setRelativePhone] = useState('');
|
||||||
|
const [relativeRelationship, setRelativeRelationship] = useState('');
|
||||||
|
|
||||||
|
const onNextPress = () => {
|
||||||
|
const isFormValid =
|
||||||
|
relativeName.trim() !== '' &&
|
||||||
|
relativePhone.trim() !== '' &&
|
||||||
|
relativeRelationship.trim() !== '';
|
||||||
|
|
||||||
|
if (selectedDestinationCountryOption === 'destination_country_not_set') {
|
||||||
|
if (isFormValid) {
|
||||||
|
onSubStepValidation(true);
|
||||||
|
}
|
||||||
|
changeStep({
|
||||||
|
currentStep: step,
|
||||||
|
targetStep: 3,
|
||||||
|
setStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setSubStep(11);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.subStepContainer}>
|
<View style={styles.subStepContainer}>
|
||||||
@ -47,12 +88,16 @@ const Step2PassportApplicationQuestionnaireSubStep10 = (
|
|||||||
title="Nama Kerabat"
|
title="Nama Kerabat"
|
||||||
placeholder="Masukkan Nama Kerabat Anda"
|
placeholder="Masukkan Nama Kerabat Anda"
|
||||||
isRequired
|
isRequired
|
||||||
|
value={relativeName}
|
||||||
|
onChangeText={setRelativeName}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInputComponent
|
<TextInputComponent
|
||||||
title="Nomor Telepon"
|
title="Nomor Telepon"
|
||||||
placeholder="Contoh: 08513456789"
|
placeholder="Contoh: 08513456789"
|
||||||
isRequired
|
isRequired
|
||||||
|
value={relativePhone}
|
||||||
|
onChangeText={setRelativePhone}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInputComponent
|
<TextInputComponent
|
||||||
@ -61,16 +106,14 @@ const Step2PassportApplicationQuestionnaireSubStep10 = (
|
|||||||
isRequired
|
isRequired
|
||||||
isDropdown
|
isDropdown
|
||||||
dropdownItemData={familyRelationshipData}
|
dropdownItemData={familyRelationshipData}
|
||||||
|
value={relativeRelationship}
|
||||||
|
onChangeText={setRelativeRelationship}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => {
|
onPress={onNextPress}
|
||||||
selectedDestinationCountryOption === 'destination_country_not_set'
|
|
||||||
? setStep(3)
|
|
||||||
: setSubStep(11);
|
|
||||||
}}
|
|
||||||
style={styles.subStepButtonContained}
|
style={styles.subStepButtonContained}
|
||||||
textColor={Colors.neutral100.color}>
|
textColor={Colors.neutral100.color}>
|
||||||
Lanjut
|
Lanjut
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, {useState} from 'react';
|
import React, {RefObject, useState} from 'react';
|
||||||
import {ScrollView, View, Text, Pressable} from 'react-native';
|
import {ScrollView, View, Text, Pressable} from 'react-native';
|
||||||
import {Button} from 'react-native-paper';
|
import {Button} from 'react-native-paper';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
@ -8,21 +8,29 @@ import destinationFamilyContactOptions from '../../../../data/Options/Destinatio
|
|||||||
import familyRelationshipData from '../../../../data/DropdownData/FamilyRelationshipData';
|
import familyRelationshipData from '../../../../data/DropdownData/FamilyRelationshipData';
|
||||||
import RadioButtonOptionComponent from '../../../../components/RadioButtonOption';
|
import RadioButtonOptionComponent from '../../../../components/RadioButtonOption';
|
||||||
import Colors from '../../../../../assets/styles/Colors';
|
import Colors from '../../../../../assets/styles/Colors';
|
||||||
|
import {changeStep} from '../../../../utils/stepNavigation';
|
||||||
|
import {StepValidationStatusSetter} from '../../../../../types/step';
|
||||||
|
|
||||||
type Step2PassportApplicationQuestionnaireSubStep11Props = {
|
type Step2PassportApplicationQuestionnaireSubStep11Props = {
|
||||||
|
step: number;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setSubStep: (subStep: number) => void;
|
setSubStep: (subStep: number) => void;
|
||||||
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
selectedOption: string;
|
selectedOption: string;
|
||||||
setSelectedOption: (value: string) => void;
|
setSelectedOption: (value: string) => void;
|
||||||
onSubStepValidation: (isValid: boolean) => void;
|
onSubStepValidation: (isValid: boolean) => void;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step2PassportApplicationQuestionnaireSubStep11 = ({
|
const Step2PassportApplicationQuestionnaireSubStep11 = ({
|
||||||
|
step,
|
||||||
setStep,
|
setStep,
|
||||||
setSubStep,
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
selectedOption,
|
selectedOption,
|
||||||
setSelectedOption,
|
setSelectedOption,
|
||||||
onSubStepValidation,
|
onSubStepValidation,
|
||||||
|
editedCompletedRef,
|
||||||
}: Step2PassportApplicationQuestionnaireSubStep11Props) => {
|
}: Step2PassportApplicationQuestionnaireSubStep11Props) => {
|
||||||
const [relativeName, setRelativeName] = useState('');
|
const [relativeName, setRelativeName] = useState('');
|
||||||
const [phoneNumber, setPhoneNumber] = useState('');
|
const [phoneNumber, setPhoneNumber] = useState('');
|
||||||
@ -41,7 +49,13 @@ const Step2PassportApplicationQuestionnaireSubStep11 = ({
|
|||||||
onSubStepValidation(false);
|
onSubStepValidation(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setStep(3);
|
changeStep({
|
||||||
|
currentStep: step,
|
||||||
|
targetStep: 3,
|
||||||
|
setStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, {useState} from 'react';
|
import React, {RefObject, useState} from 'react';
|
||||||
import {ScrollView, View, Text, Pressable} from 'react-native';
|
import {ScrollView, View, Text, Pressable} from 'react-native';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import Colors from '../../../../../assets/styles/Colors';
|
import Colors from '../../../../../assets/styles/Colors';
|
||||||
@ -7,6 +7,8 @@ import genderData from '../../../../data/DropdownData/GenderData';
|
|||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import {Button} from 'react-native-paper';
|
import {Button} from 'react-native-paper';
|
||||||
import TextInputComponent from '../../../../components/TextInput';
|
import TextInputComponent from '../../../../components/TextInput';
|
||||||
|
import {changeStep} from '../../../../utils/stepNavigation';
|
||||||
|
import { StepValidationStatusSetter } from '../../../../../types/step';
|
||||||
|
|
||||||
interface BackButtonProps {
|
interface BackButtonProps {
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
@ -22,12 +24,15 @@ interface DocumentUploadSectionProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Step3UploadDocumentsProps {
|
interface Step3UploadDocumentsProps {
|
||||||
|
step: number;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setSubStep: (subStep: number) => void;
|
setSubStep: (subStep: number) => void;
|
||||||
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
selectedPassportOption: string;
|
selectedPassportOption: string;
|
||||||
selectedDestinationCountryOption: string;
|
selectedDestinationCountryOption: string;
|
||||||
showCivilStatusDocumentsInfoDialog: () => void;
|
showCivilStatusDocumentsInfoDialog: () => void;
|
||||||
onSubStepValidation: (isValid: boolean) => void;
|
onSubStepValidation: (isValid: boolean) => void;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BackButton = (props: BackButtonProps) => {
|
const BackButton = (props: BackButtonProps) => {
|
||||||
@ -149,12 +154,15 @@ const DocumentUploadSection = (props: DocumentUploadSectionProps) => {
|
|||||||
|
|
||||||
const Step3UploadDocuments = (props: Step3UploadDocumentsProps) => {
|
const Step3UploadDocuments = (props: Step3UploadDocumentsProps) => {
|
||||||
const {
|
const {
|
||||||
|
step,
|
||||||
setStep,
|
setStep,
|
||||||
setSubStep,
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
selectedPassportOption,
|
selectedPassportOption,
|
||||||
selectedDestinationCountryOption,
|
selectedDestinationCountryOption,
|
||||||
showCivilStatusDocumentsInfoDialog,
|
showCivilStatusDocumentsInfoDialog,
|
||||||
onSubStepValidation,
|
onSubStepValidation,
|
||||||
|
editedCompletedRef,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [isKTPUploaded, setIsKTPUploaded] = useState(false);
|
const [isKTPUploaded, setIsKTPUploaded] = useState(false);
|
||||||
@ -162,6 +170,22 @@ const Step3UploadDocuments = (props: Step3UploadDocumentsProps) => {
|
|||||||
const [isCivilStatusUploaded, setIsCivilStatusUploaded] = useState(false);
|
const [isCivilStatusUploaded, setIsCivilStatusUploaded] = useState(false);
|
||||||
const [isOldPassportUploaded, setIsOldPassportUploaded] = useState(false);
|
const [isOldPassportUploaded, setIsOldPassportUploaded] = useState(false);
|
||||||
|
|
||||||
|
const onBackPress = () => {
|
||||||
|
changeStep({
|
||||||
|
currentStep: step,
|
||||||
|
targetStep: 2,
|
||||||
|
setStep,
|
||||||
|
setSubStep: () =>
|
||||||
|
setSubStep(
|
||||||
|
selectedDestinationCountryOption === 'destination_country_not_set'
|
||||||
|
? 10
|
||||||
|
: 11,
|
||||||
|
),
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onNextPress = () => {
|
const onNextPress = () => {
|
||||||
const isFormValid =
|
const isFormValid =
|
||||||
isKTPUploaded &&
|
isKTPUploaded &&
|
||||||
@ -175,23 +199,20 @@ const Step3UploadDocuments = (props: Step3UploadDocumentsProps) => {
|
|||||||
onSubStepValidation(false);
|
onSubStepValidation(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setStep(4);
|
changeStep({
|
||||||
setSubStep(1);
|
currentStep: step,
|
||||||
|
targetStep: 4,
|
||||||
|
setStep,
|
||||||
|
setSubStep: () => setSubStep(1),
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.subStepContainer}>
|
<View style={styles.subStepContainer}>
|
||||||
<BackButton
|
<BackButton onPress={onBackPress} />
|
||||||
onPress={() => {
|
|
||||||
setStep(2);
|
|
||||||
setSubStep(
|
|
||||||
selectedDestinationCountryOption === 'destination_country_not_set'
|
|
||||||
? 10
|
|
||||||
: 11,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<View style={{marginTop: 12, marginBottom: 16, gap: 4}}>
|
<View style={{marginTop: 12, marginBottom: 16, gap: 4}}>
|
||||||
<Text style={styles.subStepDesc}>
|
<Text style={styles.subStepDesc}>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, {RefObject} from 'react';
|
||||||
import {ScrollView, View, Pressable, Text} from 'react-native';
|
import {ScrollView, View, Pressable, Text} from 'react-native';
|
||||||
import {Checkbox, Button} from 'react-native-paper';
|
import {Checkbox, Button} from 'react-native-paper';
|
||||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
@ -9,17 +9,40 @@ import postalCodeData from '../../../../data/DropdownData/PostalCodeData';
|
|||||||
import districtData from '../../../../data/DropdownData/DistrictData';
|
import districtData from '../../../../data/DropdownData/DistrictData';
|
||||||
import cityData from '../../../../data/DropdownData/CityData';
|
import cityData from '../../../../data/DropdownData/CityData';
|
||||||
import provinceData from '../../../../data/DropdownData/ProvinceData';
|
import provinceData from '../../../../data/DropdownData/ProvinceData';
|
||||||
|
import {changeStep} from '../../../../utils/stepNavigation';
|
||||||
|
import { StepValidationStatusSetter } from '../../../../../types/step';
|
||||||
|
|
||||||
type Step4ApplicantAdditionalDataSubStep1Props = {
|
type Step4ApplicantAdditionalDataSubStep1Props = {
|
||||||
|
step: number;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setSubStep: (subStep: number) => void;
|
setSubStep: (subStep: number) => void;
|
||||||
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
checkedOption: boolean;
|
checkedOption: boolean;
|
||||||
setCheckedOption: React.Dispatch<React.SetStateAction<boolean>>;
|
setCheckedOption: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step4ApplicantAdditionalDataSubStep1: React.FC<
|
const Step4ApplicantAdditionalDataSubStep1: React.FC<
|
||||||
Step4ApplicantAdditionalDataSubStep1Props
|
Step4ApplicantAdditionalDataSubStep1Props
|
||||||
> = ({setStep, setSubStep, checkedOption, setCheckedOption}) => {
|
> = ({
|
||||||
|
step,
|
||||||
|
setStep,
|
||||||
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
checkedOption,
|
||||||
|
setCheckedOption,
|
||||||
|
editedCompletedRef,
|
||||||
|
}) => {
|
||||||
|
const onBackPress = () => {
|
||||||
|
changeStep({
|
||||||
|
currentStep: step,
|
||||||
|
targetStep: 3,
|
||||||
|
setStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.subStepContainer}>
|
<View style={styles.subStepContainer}>
|
||||||
@ -28,9 +51,7 @@ const Step4ApplicantAdditionalDataSubStep1: React.FC<
|
|||||||
transform: [{scale: pressed ? 0.99 : 1}],
|
transform: [{scale: pressed ? 0.99 : 1}],
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
})}
|
})}
|
||||||
onPress={() => {
|
onPress={onBackPress}>
|
||||||
setStep(3);
|
|
||||||
}}>
|
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
icon="chevron-left"
|
icon="chevron-left"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React, {RefObject, useState} 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 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';
|
||||||
@ -9,14 +8,96 @@ import jobData from '../../../../data/DropdownData/JobData';
|
|||||||
import nationalityData from '../../../../data/DropdownData/NationalityData';
|
import nationalityData from '../../../../data/DropdownData/NationalityData';
|
||||||
import {PassportAppointment} from '../../../../navigation/type';
|
import {PassportAppointment} from '../../../../navigation/type';
|
||||||
import {addData, getData} from '../../../../helper/asyncStorageHelper';
|
import {addData, getData} from '../../../../helper/asyncStorageHelper';
|
||||||
|
import {changeStep} from '../../../../utils/stepNavigation';
|
||||||
|
import { StepValidationStatusSetter } from '../../../../../types/step';
|
||||||
|
|
||||||
const Step4ApplicantAdditionalDataSubStep2 = ({
|
type Step4ApplicantAdditionalDataSubStep2Props = {
|
||||||
setStep,
|
step: number;
|
||||||
setSubStep,
|
|
||||||
}: {
|
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setSubStep: (subStep: number) => void;
|
setSubStep: (subStep: number) => void;
|
||||||
}) => {
|
setStepValidationStatus: StepValidationStatusSetter;
|
||||||
|
onSubStepValidation: (isValid: boolean) => void;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Step4ApplicantAdditionalDataSubStep2 = ({
|
||||||
|
step,
|
||||||
|
setStep,
|
||||||
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
onSubStepValidation,
|
||||||
|
editedCompletedRef,
|
||||||
|
}: Step4ApplicantAdditionalDataSubStep2Props) => {
|
||||||
|
const [job, setJob] = useState('');
|
||||||
|
const [phone, setPhone] = useState('');
|
||||||
|
const [motherName, setMotherName] = useState('');
|
||||||
|
const [motherNation, setMotherNation] = useState('');
|
||||||
|
const [motherAddress, setMotherAddress] = useState('');
|
||||||
|
|
||||||
|
const isFormValid =
|
||||||
|
job.trim() !== '' &&
|
||||||
|
phone.trim() !== '' &&
|
||||||
|
motherName.trim() !== '' &&
|
||||||
|
motherNation.trim() !== '' &&
|
||||||
|
motherAddress.trim() !== '';
|
||||||
|
|
||||||
|
const handleSaveDraft = async () => {
|
||||||
|
if (isFormValid) {
|
||||||
|
onSubStepValidation(true);
|
||||||
|
|
||||||
|
// Ambil data appointment yang sudah tersimpan
|
||||||
|
const storedAppointments: PassportAppointment[] =
|
||||||
|
(await getData('passportAppointments')) || [];
|
||||||
|
|
||||||
|
// Ambil ID terakhir dan hitung ID baru
|
||||||
|
const lastId = storedAppointments.length
|
||||||
|
? Math.max(...storedAppointments.map(item => Number(item.id)))
|
||||||
|
: 0;
|
||||||
|
const nextId = (lastId + 1).toString();
|
||||||
|
|
||||||
|
// Buat appointment baru dengan ID yang sudah dihitung
|
||||||
|
const newAppointment: PassportAppointment = {
|
||||||
|
id: nextId,
|
||||||
|
applicantName: 'Salwa Aisyah Adhani',
|
||||||
|
applicantCode: '1038000008887777',
|
||||||
|
appointmentDate: 'Selasa, 20 Mei 2025',
|
||||||
|
appointmentTime: '10:00-11:00 WIB',
|
||||||
|
serviceUnit: 'Kantor Imigrasi Depok',
|
||||||
|
status: 'Menunggu Pembayaran',
|
||||||
|
submissionDate: 'Kamis, 15 Mei 2025 21:30',
|
||||||
|
serviceCode: 'EH-LP7RNC',
|
||||||
|
applicationDetails: {
|
||||||
|
nationalIdNumber: '3271234560009123456',
|
||||||
|
gender: 'Wanita',
|
||||||
|
applicationType: 'Penggantian Paspor',
|
||||||
|
replacementReason: 'Penuh/Halaman Penuh',
|
||||||
|
applicationPurpose: 'Wisata/Liburan',
|
||||||
|
passportType: 'PASPOR ELEKTRONIK POLIKARBONAT 5 TAHUN',
|
||||||
|
fee: '650.000',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simpan appointment baru
|
||||||
|
await addData<PassportAppointment>(
|
||||||
|
'passportAppointments',
|
||||||
|
newAppointment,
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedAppointments = await getData('passportAppointments');
|
||||||
|
console.log('Data yang berhasil ditambahkan:', updatedAppointments);
|
||||||
|
} else {
|
||||||
|
onSubStepValidation(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStep({
|
||||||
|
currentStep: step,
|
||||||
|
targetStep: 5,
|
||||||
|
setStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.subStepContainer}>
|
<View style={styles.subStepContainer}>
|
||||||
@ -37,6 +118,7 @@ const Step4ApplicantAdditionalDataSubStep2 = ({
|
|||||||
Kembali
|
Kembali
|
||||||
</Button>
|
</Button>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
<Text style={styles.subStepDesc}>
|
<Text style={styles.subStepDesc}>
|
||||||
Data di bawah ini harus sesuai dengan keterangan pada KTP pemohon.
|
Data di bawah ini harus sesuai dengan keterangan pada KTP pemohon.
|
||||||
Data yang bertanda (
|
Data yang bertanda (
|
||||||
@ -58,11 +140,15 @@ const Step4ApplicantAdditionalDataSubStep2 = ({
|
|||||||
isRequired
|
isRequired
|
||||||
isDropdown
|
isDropdown
|
||||||
dropdownItemData={jobData}
|
dropdownItemData={jobData}
|
||||||
|
value={job}
|
||||||
|
onChangeText={setJob}
|
||||||
/>
|
/>
|
||||||
<TextInputComponent
|
<TextInputComponent
|
||||||
title="Nomor Telepon"
|
title="Nomor Telepon"
|
||||||
placeholder="Contoh: 08513456789"
|
placeholder="Contoh: 08513456789"
|
||||||
isRequired
|
isRequired
|
||||||
|
value={phone}
|
||||||
|
onChangeText={setPhone}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@ -78,6 +164,8 @@ const Step4ApplicantAdditionalDataSubStep2 = ({
|
|||||||
title="Nama Ibu"
|
title="Nama Ibu"
|
||||||
placeholder="Masukkan nama lengkap ibu"
|
placeholder="Masukkan nama lengkap ibu"
|
||||||
isRequired
|
isRequired
|
||||||
|
value={motherName}
|
||||||
|
onChangeText={setMotherName}
|
||||||
/>
|
/>
|
||||||
<TextInputComponent
|
<TextInputComponent
|
||||||
title="Kewarganegaraan Ibu"
|
title="Kewarganegaraan Ibu"
|
||||||
@ -85,6 +173,8 @@ const Step4ApplicantAdditionalDataSubStep2 = ({
|
|||||||
isRequired
|
isRequired
|
||||||
isDropdown
|
isDropdown
|
||||||
dropdownItemData={nationalityData}
|
dropdownItemData={nationalityData}
|
||||||
|
value={motherNation}
|
||||||
|
onChangeText={setMotherNation}
|
||||||
/>
|
/>
|
||||||
<TextInputComponent
|
<TextInputComponent
|
||||||
title="Alamat Ibu"
|
title="Alamat Ibu"
|
||||||
@ -93,6 +183,8 @@ const Step4ApplicantAdditionalDataSubStep2 = ({
|
|||||||
supportText="0/100 karakter"
|
supportText="0/100 karakter"
|
||||||
containerHeight={90}
|
containerHeight={90}
|
||||||
isMultiline
|
isMultiline
|
||||||
|
value={motherAddress}
|
||||||
|
onChangeText={setMotherAddress}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@ -152,49 +244,7 @@ const Step4ApplicantAdditionalDataSubStep2 = ({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={async () => {
|
onPress={handleSaveDraft}
|
||||||
// Ambil data appointment yang sudah tersimpan
|
|
||||||
const storedAppointments: PassportAppointment[] =
|
|
||||||
(await getData('passportAppointments')) || [];
|
|
||||||
|
|
||||||
// Ambil ID terakhir dan hitung ID baru
|
|
||||||
const lastId = storedAppointments.length
|
|
||||||
? Math.max(...storedAppointments.map(item => Number(item.id)))
|
|
||||||
: 0;
|
|
||||||
const nextId = (lastId + 1).toString();
|
|
||||||
|
|
||||||
// Buat appointment baru dengan ID yang sudah dihitung
|
|
||||||
const newAppointment: PassportAppointment = {
|
|
||||||
id: nextId,
|
|
||||||
applicantName: 'Salwa Aisyah Adhani',
|
|
||||||
applicantCode: '1038000008887777',
|
|
||||||
appointmentDate: 'Selasa, 20 Mei 2025',
|
|
||||||
appointmentTime: '10:00-11:00 WIB',
|
|
||||||
serviceUnit: 'Kantor Imigrasi Depok',
|
|
||||||
status: 'Menunggu Pembayaran',
|
|
||||||
submissionDate: 'Kamis, 15 Mei 2025 21:30',
|
|
||||||
serviceCode: 'EH-LP7RNC',
|
|
||||||
applicationDetails: {
|
|
||||||
nationalIdNumber: '3271234560009123456',
|
|
||||||
gender: 'Wanita',
|
|
||||||
applicationType: 'Penggantian Paspor',
|
|
||||||
replacementReason: 'Penuh/Halaman Penuh',
|
|
||||||
applicationPurpose: 'Wisata/Liburan',
|
|
||||||
passportType: 'PASPOR ELEKTRONIK POLIKARBONAT 5 TAHUN',
|
|
||||||
fee: '650.000',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Simpan appointment baru
|
|
||||||
await addData<PassportAppointment>(
|
|
||||||
'passportAppointments',
|
|
||||||
newAppointment,
|
|
||||||
);
|
|
||||||
|
|
||||||
const updatedAppointments = await getData('passportAppointments');
|
|
||||||
console.log('Data yang berhasil ditambahkan:', updatedAppointments);
|
|
||||||
setStep(5);
|
|
||||||
}}
|
|
||||||
style={[styles.subStepButtonContained, {marginBottom: 8}]}
|
style={[styles.subStepButtonContained, {marginBottom: 8}]}
|
||||||
textColor={Colors.neutral100.color}>
|
textColor={Colors.neutral100.color}>
|
||||||
Simpan Draft
|
Simpan Draft
|
||||||
|
@ -9,12 +9,15 @@ import {PassportAppointment} from '../../../../navigation/type';
|
|||||||
|
|
||||||
type Step5ApplicationTypeAndApplicantDataProps = {
|
type Step5ApplicationTypeAndApplicantDataProps = {
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
setSubStep: (subStep: number) => void;
|
|
||||||
showEditDataSheet: () => void;
|
showEditDataSheet: () => void;
|
||||||
|
navigation: any;
|
||||||
|
onSubStepValidation: (isValid: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step5Content = (props: Step5ApplicationTypeAndApplicantDataProps) => {
|
const Step5ApplicationTypeAndApplicantData = (
|
||||||
const {setStep, setSubStep, showEditDataSheet} = props;
|
props: Step5ApplicationTypeAndApplicantDataProps,
|
||||||
|
) => {
|
||||||
|
const {setStep, showEditDataSheet, navigation, onSubStepValidation} = props;
|
||||||
|
|
||||||
const [lastAppointment, setLastAppointment] = useState<PassportAppointment>();
|
const [lastAppointment, setLastAppointment] = useState<PassportAppointment>();
|
||||||
|
|
||||||
@ -32,6 +35,18 @@ const Step5Content = (props: Step5ApplicationTypeAndApplicantDataProps) => {
|
|||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const onNextPress = () => {
|
||||||
|
onSubStepValidation(true);
|
||||||
|
setStep(6);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBackHomePress = () => {
|
||||||
|
navigation.reset({
|
||||||
|
index: 0,
|
||||||
|
routes: [{name: 'NavigationRoute'}],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.subStepContainer}>
|
<View style={styles.subStepContainer}>
|
||||||
<Text style={styles.subStepDesc}>
|
<Text style={styles.subStepDesc}>
|
||||||
@ -104,20 +119,17 @@ const Step5Content = (props: Step5ApplicationTypeAndApplicantDataProps) => {
|
|||||||
<View style={[styles.subStepButtonContainer, {marginTop: 12}]}>
|
<View style={[styles.subStepButtonContainer, {marginTop: 12}]}>
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => setStep(6)}
|
onPress={onNextPress}
|
||||||
style={styles.subStepButtonContained}
|
style={styles.subStepButtonContained}
|
||||||
textColor={Colors.neutral100.color}>
|
textColor={Colors.neutral100.color}>
|
||||||
Lanjut
|
Lanjut
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
mode="outlined"
|
mode="outlined"
|
||||||
onPress={() => {
|
onPress={onBackHomePress}
|
||||||
setStep(4);
|
|
||||||
setSubStep(2);
|
|
||||||
}}
|
|
||||||
style={styles.subStepButtonOutlined}
|
style={styles.subStepButtonOutlined}
|
||||||
textColor={Colors.primary30.color}>
|
textColor={Colors.primary30.color}>
|
||||||
Kembali
|
Beranda
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@ -137,4 +149,4 @@ const DetailRow = ({
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Step5Content;
|
export default Step5ApplicationTypeAndApplicantData;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, {useState} from 'react';
|
||||||
import {ScrollView, View, Text} from 'react-native';
|
import {ScrollView, View, Text} from 'react-native';
|
||||||
import {Button, Divider} from 'react-native-paper';
|
import {Button, Divider} from 'react-native-paper';
|
||||||
import TextInputComponent from '../../../../components/TextInput';
|
import TextInputComponent from '../../../../components/TextInput';
|
||||||
@ -15,13 +15,16 @@ type Step6ApplicationTypeAndApplicantDataProps = {
|
|||||||
showSelectDateSheet: () => void;
|
showSelectDateSheet: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step6ApplicationTypeAndApplicantData = (props: Step6ApplicationTypeAndApplicantDataProps) => {
|
const Step6ApplicationTypeAndApplicantData = (
|
||||||
|
props: Step6ApplicationTypeAndApplicantDataProps,
|
||||||
|
) => {
|
||||||
const {
|
const {
|
||||||
showFinalizationConfirmationDialog,
|
showFinalizationConfirmationDialog,
|
||||||
showPassportTypeInfoDialog,
|
showPassportTypeInfoDialog,
|
||||||
showSearchLocationSheet,
|
showSearchLocationSheet,
|
||||||
showSelectDateSheet,
|
showSelectDateSheet,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={styles.subStepContainer}>
|
<View style={styles.subStepContainer}>
|
||||||
|
@ -12,10 +12,12 @@ import {getData} from '../../../../helper/asyncStorageHelper';
|
|||||||
type Step7ApplicationFeeDetailsProps = {
|
type Step7ApplicationFeeDetailsProps = {
|
||||||
showSubmitSuccessDialog: () => void;
|
showSubmitSuccessDialog: () => void;
|
||||||
setLastCompletedSteps: () => void;
|
setLastCompletedSteps: () => void;
|
||||||
|
onSubStepValidation: (isValid: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step7ApplicationFeeDetails = (props: Step7ApplicationFeeDetailsProps) => {
|
const Step7ApplicationFeeDetails = (props: Step7ApplicationFeeDetailsProps) => {
|
||||||
const {showSubmitSuccessDialog, setLastCompletedSteps} = props;
|
const {showSubmitSuccessDialog, setLastCompletedSteps, onSubStepValidation} =
|
||||||
|
props;
|
||||||
const [lastAppointment, setLastAppointment] = useState<PassportAppointment>();
|
const [lastAppointment, setLastAppointment] = useState<PassportAppointment>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -32,6 +34,12 @@ const Step7ApplicationFeeDetails = (props: Step7ApplicationFeeDetailsProps) => {
|
|||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const onNextPress = () => {
|
||||||
|
onSubStepValidation(true);
|
||||||
|
showSubmitSuccessDialog();
|
||||||
|
setLastCompletedSteps();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
<View style={[styles.subStepContainer, {paddingBottom: 0}]}>
|
<View style={[styles.subStepContainer, {paddingBottom: 0}]}>
|
||||||
@ -215,10 +223,7 @@ const Step7ApplicationFeeDetails = (props: Step7ApplicationFeeDetailsProps) => {
|
|||||||
mode="contained"
|
mode="contained"
|
||||||
style={styles.subStepButtonContained}
|
style={styles.subStepButtonContained}
|
||||||
textColor={Colors.neutral100.color}
|
textColor={Colors.neutral100.color}
|
||||||
onPress={() => {
|
onPress={onNextPress}>
|
||||||
showSubmitSuccessDialog();
|
|
||||||
setLastCompletedSteps();
|
|
||||||
}}>
|
|
||||||
Kembali ke Halaman Utama
|
Kembali ke Halaman Utama
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
53
src/utils/stepNavigation.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {RefObject} from 'react';
|
||||||
|
export type StepStatus = 'completed' | 'incomplete' | 'invalid';
|
||||||
|
|
||||||
|
interface StepChangeParams {
|
||||||
|
currentStep: number;
|
||||||
|
targetStep: number;
|
||||||
|
setStep: (step: number) => void;
|
||||||
|
setSubStep?: (sub: number) => void;
|
||||||
|
setStepValidationStatus: React.Dispatch<
|
||||||
|
React.SetStateAction<Record<number, StepStatus>>
|
||||||
|
>;
|
||||||
|
editedCompletedRef: RefObject<Set<number>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function changeStep({
|
||||||
|
currentStep,
|
||||||
|
targetStep,
|
||||||
|
setStep,
|
||||||
|
setSubStep,
|
||||||
|
setStepValidationStatus,
|
||||||
|
editedCompletedRef,
|
||||||
|
}: StepChangeParams) {
|
||||||
|
setStepValidationStatus(prev => {
|
||||||
|
const next = {...prev};
|
||||||
|
|
||||||
|
if (currentStep !== targetStep &&
|
||||||
|
editedCompletedRef.current?.has(currentStep)) {
|
||||||
|
next[currentStep] = 'completed';
|
||||||
|
editedCompletedRef.current.delete(currentStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev[targetStep] === 'completed') {
|
||||||
|
editedCompletedRef.current?.add(targetStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
next[targetStep] = 'incomplete';
|
||||||
|
|
||||||
|
if (targetStep > currentStep) {
|
||||||
|
for (let s = 1; s < targetStep; s++) {
|
||||||
|
if (next[s] !== 'completed') next[s] = 'invalid';
|
||||||
|
}
|
||||||
|
} else if (targetStep < currentStep) {
|
||||||
|
for (let s = currentStep; s > targetStep; s--) {
|
||||||
|
if (next[s] !== 'completed') next[s] = 'invalid';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
|
||||||
|
setStep(targetStep);
|
||||||
|
if (setSubStep) setSubStep(1);
|
||||||
|
}
|
7
types/step.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export type StepStatus = 'incomplete' | 'completed' | 'invalid';
|
||||||
|
|
||||||
|
export type StepValidationStatus = Record<number, StepStatus>;
|
||||||
|
|
||||||
|
export type StepValidationStatusSetter = React.Dispatch<
|
||||||
|
React.SetStateAction<StepValidationStatus>
|
||||||
|
>;
|