Finalized all of the passport application flow feature and and adding a feature to add data

This commit is contained in:
Mochammad Adhi Buchori
2025-04-26 09:41:08 +07:00
parent ad67df1461
commit 20cd765338
28 changed files with 1241 additions and 266 deletions

View File

@ -43,6 +43,7 @@ import DialogPassportConditionInfo from '../../components/dialog/DialogPassportC
import DialogPassportTypeInfo from '../../components/dialog/DialogPassportTypeInfo';
import SheetEditData from '../../components/sheet/SheetEditData';
import SheetSearchLocation from '../../components/sheet/SheetSearchLocation';
import SheetSelectDate from '../../components/sheet/SheetSelectDate';
type RegularPassportScreenNavigationProp = NativeStackNavigationProp<
RootStackParamList,
@ -70,6 +71,7 @@ type RenderApplicationStepsContentProps = {
showPassportTypeInfoDialog: () => void;
showEditDataSheet: () => void;
showSearchLocationSheet: () => void;
showSelectDateSheet: () => void;
};
const RenderApplicationStepsContent = (
@ -96,6 +98,7 @@ const RenderApplicationStepsContent = (
showPassportTypeInfoDialog,
showEditDataSheet,
showSearchLocationSheet,
showSelectDateSheet,
} = props;
if (step === 1) {
@ -235,6 +238,7 @@ const RenderApplicationStepsContent = (
}
showPassportTypeInfoDialog={showPassportTypeInfoDialog}
showSearchLocationSheet={showSearchLocationSheet}
showSelectDateSheet={showSelectDateSheet}
/>
);
case 7:
@ -336,6 +340,7 @@ function RegularPassportScreen() {
const [visibleEditDataSheet, setVisibleEditDataSheet] = useState(false);
const [visibleSearchLocationSheet, setVisibleSearchLocationSheet] =
useState(false);
const [visibleSelectDateSheet, setVisibleSelectDateSheet] = useState(false);
// Dialog visibility function
const showDialog = () => setVisible(true);
@ -377,6 +382,9 @@ function RegularPassportScreen() {
const showSearchLocationSheet = () => setVisibleSearchLocationSheet(true);
const hideSearchLocationSheet = () => setVisibleSearchLocationSheet(false);
const showSelectDateSheet = () => setVisibleSelectDateSheet(true);
const hideSelectDateSheet = () => setVisibleSelectDateSheet(false);
const stepTitles: {[key: number]: string} = {
1: 'Informasi Pribadi',
2: 'Dokumen Pendukung',
@ -457,6 +465,7 @@ function RegularPassportScreen() {
showPassportTypeInfoDialog={showPassportTypeInfoDialog}
showEditDataSheet={showEditDataSheet}
showSearchLocationSheet={showSearchLocationSheet}
showSelectDateSheet={showSelectDateSheet}
/>
</View>
@ -501,7 +510,11 @@ function RegularPassportScreen() {
<DialogSubmitSuccess
visible={visibleSubmitSuccessDialog}
onSubmitSuccess={() => {
navigation.goBack(), hideSubmitSuccessDialog();
navigation.reset({
index: 0,
routes: [{name: 'NavigationRoute'}],
});
hideSubmitSuccessDialog();
}}
/>
)}
@ -542,6 +555,14 @@ function RegularPassportScreen() {
onClose={hideSearchLocationSheet}
/>
)}
{visibleSelectDateSheet && (
<SheetSelectDate
visible={visibleSelectDateSheet}
onClose={hideSelectDateSheet}
onContinue={hideSelectDateSheet}
/>
)}
</>
) : (
<Questionnaire
@ -566,7 +587,9 @@ function RegularPassportScreen() {
visibleFinalizationConfirmationDialog ||
visiblePassportTypeInfoDialog
? '#295E70'
: visibleEditDataSheet || visibleSearchLocationSheet
: visibleEditDataSheet ||
visibleSearchLocationSheet ||
visibleSelectDateSheet
? '#185769'
: Colors.secondary30.color
}

View File

@ -7,6 +7,12 @@ import {Button} from 'react-native-paper';
import Colors from '../../../../../assets/styles/Colors';
import jobData from '../../../../data/DropdownData/JobData';
import nationalityData from '../../../../data/DropdownData/NationalityData';
import {PassportAppointment} from '../../../../navigation/type';
import {
addData,
getData,
storeData,
} from '../../../../helper/asyncStorageHelper';
const Step4DataConfirmationSubStep2 = ({
setStep,
@ -148,7 +154,49 @@ const Step4DataConfirmationSubStep2 = ({
<Button
mode="contained"
onPress={() => setStep(5)}
onPress={async () => {
// 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}]}
textColor={Colors.neutral100.color}>
Simpan Draft

View File

@ -1,9 +1,11 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import {View, Text, Pressable} from 'react-native';
import {Button} from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import Colors from '../../../../../assets/styles/Colors';
import styles from '../styles';
import {getData} from '../../../../helper/asyncStorageHelper';
import {PassportAppointment} from '../../../../navigation/type';
type Step5VerificationProps = {
setStep: (step: number) => void;
@ -15,8 +17,23 @@ type Step5VerificationProps = {
const Step5Content = (props: Step5VerificationProps) => {
const {setStep, setSubStep, passportAppointmentData, showEditDataSheet} =
props;
const lastAppointment =
passportAppointmentData[passportAppointmentData.length - 1];
const [lastAppointment, setLastAppointment] =
useState<PassportAppointment>();
useEffect(() => {
const fetchData = async () => {
try {
const data = await getData('passportAppointments');
if (Array.isArray(data) && data.length > 0) {
setLastAppointment(data[data.length - 1]);
}
} catch (error) {
console.error('Error fetching data: ', error);
}
};
fetchData();
}, []);
return (
<View style={styles.subStepContainer}>
@ -36,7 +53,7 @@ const Step5Content = (props: Step5VerificationProps) => {
styles.applicantDetailTextDesc,
{textTransform: 'uppercase', flex: 0},
]}>
{lastAppointment.applicantName}
{lastAppointment?.applicantName}
</Text>
</View>
<View style={styles.applicantDetailIconContentWrapper}>
@ -63,27 +80,27 @@ const Step5Content = (props: Step5VerificationProps) => {
<View style={styles.applicantDetailContentChildContainer}>
<DetailRow
label="NIK"
value={lastAppointment.applicationDetails.nationalIdNumber}
value={lastAppointment?.applicationDetails?.nationalIdNumber}
/>
<DetailRow
label="Jenis Kelamin"
value={lastAppointment.applicationDetails.gender}
value={lastAppointment?.applicationDetails?.gender}
/>
<DetailRow
label="Jenis Permohonan"
value={lastAppointment.applicationDetails.applicationType}
value={lastAppointment?.applicationDetails?.applicationType}
/>
<DetailRow
label="Alasan Penggantian"
value={lastAppointment.applicationDetails.replacementReason}
value={lastAppointment?.applicationDetails?.replacementReason}
/>
<DetailRow
label="Tujuan Permohonan"
value={lastAppointment.applicationDetails.applicationPurpose}
value={lastAppointment?.applicationDetails?.applicationPurpose}
/>
<DetailRow
label="Jenis Paspor"
value={lastAppointment.applicationDetails.passportType}
value={lastAppointment?.applicationDetails?.passportType}
/>
</View>
</View>
@ -110,7 +127,7 @@ const Step5Content = (props: Step5VerificationProps) => {
);
};
const DetailRow = ({label, value}: {label: string; value: string}) => (
const DetailRow = ({label, value}: {label: string; value: string | undefined}) => (
<View style={styles.applicantDetailTextContentWrapper}>
<Text style={styles.applicantDetailTextTitle}>{label}</Text>
<Text style={styles.applicantDetailTextDesc}>{value}</Text>

View File

@ -6,16 +6,22 @@ import styles from '../styles';
import Colors from '../../../../../assets/styles/Colors';
import FontFamily from '../../../../../assets/styles/FontFamily';
import arrivalDateGuidelinesData from '../../../../data/Steps/ArrivalDateGuidelinesData';
import passportTypeData from '../../../../data/DropdownData/PassportTypeData';
type Step6ProcessingProps = {
showFinalizationConfirmationDialog: () => void;
showPassportTypeInfoDialog: () => void;
showSearchLocationSheet: () => void;
showSelectDateSheet: () => void;
};
const Step6Processing = (props: Step6ProcessingProps) => {
const {showFinalizationConfirmationDialog, showPassportTypeInfoDialog, showSearchLocationSheet} =
props;
const {
showFinalizationConfirmationDialog,
showPassportTypeInfoDialog,
showSearchLocationSheet,
showSelectDateSheet,
} = props;
return (
<ScrollView>
<View style={styles.subStepContainer}>
@ -30,13 +36,12 @@ const Step6Processing = (props: Step6ProcessingProps) => {
</View>
<View style={[styles.subStepTextInputContainer, {marginVertical: 16}]}>
{/* Trigger Search Location Bottom Sheet */}
<TextInputComponent
title="Lokasi Kantor Imigrasi"
placeholder="Pilih lokasi kantor imigrasi"
isRequired
isDropdownSearchLocation
handlePressSearchLocation={showSearchLocationSheet}
isDropdownPressedSheet
handleDropdownPressed={showSearchLocationSheet}
/>
<TextInputComponent
title="Jenis Paspor"
@ -44,6 +49,7 @@ const Step6Processing = (props: Step6ProcessingProps) => {
placeholder="Pilih satu jenis paspor"
isRequired
isDropdown
dropdownItemData={passportTypeData}
onIconButtonPress={showPassportTypeInfoDialog}
/>
</View>
@ -80,12 +86,12 @@ const Step6Processing = (props: Step6ProcessingProps) => {
</View>
</View>
{/* TODO: Add calendar functionality here. */}
<TextInputComponent
title="Tanggal dan Waktu Kedatangan"
placeholder="Pilih tanggal dan waktu kedatangan"
isRequired
isDate
isDropdownPressedSheet
handleDropdownPressed={showSelectDateSheet}
/>
<Button

View File

@ -1,12 +1,13 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import {ScrollView, View} from 'react-native';
import {Button, Divider, Text} from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import Colors from '../../../../../assets/styles/Colors';
import styles from '../styles';
import passportAppointmentData from '../../../../data/History/PassportAppointmentData';
import Accordion from '../../../../components/Accordion';
import termsAndConditionsData from '../../../../data/Steps/TermsAndContionsData';
import {PassportAppointment} from '../../../../navigation/type';
import {getData} from '../../../../helper/asyncStorageHelper';
type Step7CompletionProps = {
showSubmitSuccessDialog: () => void;
@ -15,8 +16,21 @@ type Step7CompletionProps = {
const Step7Completion = (props: Step7CompletionProps) => {
const {showSubmitSuccessDialog, setLastCompletedSteps} = props;
const lastAppointment =
passportAppointmentData[passportAppointmentData.length - 1];
const [lastAppointment, setLastAppointment] = useState<PassportAppointment>();
useEffect(() => {
const fetchData = async () => {
try {
const data = await getData('passportAppointments');
if (Array.isArray(data) && data.length > 0) {
setLastAppointment(data[data.length - 1]);
}
} catch (error) {
console.error('Error fetching data: ', error);
}
};
fetchData();
}, []);
return (
<ScrollView>
@ -31,7 +45,7 @@ const Step7Completion = (props: Step7CompletionProps) => {
color={Colors.secondary30.color}
/>
<Text style={styles.midIconContentTextStyle}>
{lastAppointment.appointmentDate}
{lastAppointment?.appointmentDate}
</Text>
</View>
<View style={styles.midIconContentWrapper}>
@ -41,7 +55,7 @@ const Step7Completion = (props: Step7CompletionProps) => {
color={Colors.secondary30.color}
/>
<Text style={styles.midIconContentTextStyle}>
{lastAppointment.appointmentTime}
{lastAppointment?.appointmentTime}
</Text>
</View>
<View style={styles.midIconContentWrapper}>
@ -51,7 +65,7 @@ const Step7Completion = (props: Step7CompletionProps) => {
color={Colors.secondary30.color}
/>
<Text style={styles.midIconContentTextStyle}>
{lastAppointment.serviceUnit}
{lastAppointment?.serviceUnit}
</Text>
</View>
</View>
@ -60,13 +74,13 @@ const Step7Completion = (props: Step7CompletionProps) => {
<View style={styles.midTextContentWrapper}>
<Text style={styles.midTextContentTitle}>Tanggal Pengajuan</Text>
<Text style={styles.midTextContentData}>
{lastAppointment.submissionDate}
{lastAppointment?.submissionDate}
</Text>
</View>
<View style={styles.midTextContentWrapper}>
<Text style={styles.midTextContentTitle}>Kode Layanan</Text>
<Text style={styles.midTextContentData}>
{lastAppointment.serviceCode}
{lastAppointment?.serviceCode}
</Text>
</View>
</View>
@ -130,7 +144,7 @@ const Step7Completion = (props: Step7CompletionProps) => {
styles.applicantDetailTextDesc,
styles.applicantDetailTexDescName,
]}>
{lastAppointment.applicantName}
{lastAppointment?.applicantName}
</Text>
</View>
<View style={styles.applicantDetailTextContentWrapper}>
@ -140,20 +154,20 @@ const Step7Completion = (props: Step7CompletionProps) => {
styles.applicantDetailTextDesc,
styles.applicantDetailTexDescCode,
]}>
{lastAppointment.applicantCode}
{lastAppointment?.applicantCode}
</Text>
</View>
<View style={styles.applicantDetailContentChildContainer}>
<View style={styles.applicantDetailTextContentWrapper}>
<Text style={styles.applicantDetailTextTitle}>NIK</Text>
<Text style={styles.applicantDetailTextDesc}>
{lastAppointment.applicationDetails.nationalIdNumber}
{lastAppointment?.applicationDetails?.nationalIdNumber}
</Text>
</View>
<View style={styles.applicantDetailTextContentWrapper}>
<Text style={styles.applicantDetailTextTitle}>Jenis Kelamin</Text>
<Text style={styles.applicantDetailTextDesc}>
{lastAppointment.applicationDetails.gender}
{lastAppointment?.applicationDetails?.gender}
</Text>
</View>
<View style={styles.applicantDetailTextContentWrapper}>
@ -161,7 +175,7 @@ const Step7Completion = (props: Step7CompletionProps) => {
Jenis Permohonan
</Text>
<Text style={styles.applicantDetailTextDesc}>
{lastAppointment.applicationDetails.applicationType}
{lastAppointment?.applicationDetails?.applicationType}
</Text>
</View>
<View style={styles.applicantDetailTextContentWrapper}>
@ -169,7 +183,7 @@ const Step7Completion = (props: Step7CompletionProps) => {
Alasan Penggantian
</Text>
<Text style={styles.applicantDetailTextDesc}>
{lastAppointment.applicationDetails.replacementReason}
{lastAppointment?.applicationDetails?.replacementReason}
</Text>
</View>
<View style={styles.applicantDetailTextContentWrapper}>
@ -177,16 +191,23 @@ const Step7Completion = (props: Step7CompletionProps) => {
Tujuan Permohonan
</Text>
<Text style={styles.applicantDetailTextDesc}>
{lastAppointment.applicationDetails.applicationPurpose}
{lastAppointment?.applicationDetails?.applicationPurpose}
</Text>
</View>
<View style={styles.applicantDetailTextContentWrapper}>
<Text style={styles.applicantDetailTextTitle}>Jenis Paspor</Text>
<Text style={styles.applicantDetailTextDesc}>
{lastAppointment.applicationDetails.passportType}
{lastAppointment?.applicationDetails?.passportType}
</Text>
</View>
</View>
<Divider style={styles.applicantDetailDividerMargin} />
<View style={styles.applicantDetailBottomContentWrapper}>
<Text style={styles.applicantDetailBottomText}>Biaya</Text>
<Text style={styles.applicantDetailBottomText}>
{lastAppointment?.applicationDetails?.fee}
</Text>
</View>
</View>
</View>
<View style={{margin: 16}}>

View File

@ -200,6 +200,15 @@ const styles = StyleSheet.create({
applicantDetailTexDescCode: {
textAlign: 'right',
},
applicantDetailBottomText: {
fontSize: 14,
...FontFamily.notoSansBold,
color: Colors.primary30.color,
includeFontPadding: false,
},
applicantDetailDividerMargin: {
marginVertical: 4,
},
midContainer: {
backgroundColor: Colors.neutral100.color,
},