diff --git a/package-lock.json b/package-lock.json index 5969817..3ebedf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,16 @@ "name": "mpaspor", "version": "0.0.1", "dependencies": { + "@react-native-async-storage/async-storage": "^2.1.2", "@react-native-community/datetimepicker": "^8.3.0", "@react-navigation/elements": "^2.3.8", "@react-navigation/native": "^7.1.6", "@react-navigation/native-stack": "^7.3.10", "dayjs": "^1.11.13", + "moment": "^2.30.1", "react": "19.0.0", "react-native": "0.78.0", + "react-native-calendars": "^1.1311.1", "react-native-element-dropdown": "^2.12.4", "react-native-paper": "^5.13.2", "react-native-safe-area-context": "^5.4.0", @@ -2609,6 +2612,18 @@ "node": ">= 8" } }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.1.2.tgz", + "integrity": "sha512-dvlNq4AlGWC+ehtH12p65+17V0Dx7IecOWl6WanF2ja38O1Dcjjvn7jVzkUHJ5oWkQBlyASurTPlTHgKXyYiow==", + "license": "MIT", + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || >=0.65 <1.0" + } + }, "node_modules/@react-native-community/cli": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-15.0.1.tgz", @@ -7576,6 +7591,15 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -9182,6 +9206,18 @@ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -9678,6 +9714,15 @@ "node": ">=10" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -10557,6 +10602,38 @@ } } }, + "node_modules/react-native-calendars": { + "version": "1.1311.1", + "resolved": "https://registry.npmjs.org/react-native-calendars/-/react-native-calendars-1.1311.1.tgz", + "integrity": "sha512-/he1+4irh/643SOnnWlEfoAsHhKq2v8WFRMbcXr+dGgf5hcl/TeVgUINw3Vb2SsAxYM2dV+mVCaNJTbMjS025Q==", + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.1", + "lodash": "^4.17.15", + "memoize-one": "^5.2.1", + "prop-types": "^15.5.10", + "react-native-safe-area-context": "4.5.0", + "react-native-swipe-gestures": "^1.0.5", + "recyclerlistview": "^4.0.0", + "xdate": "^0.8.0" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "moment": "^2.29.4" + } + }, + "node_modules/react-native-calendars/node_modules/react-native-safe-area-context": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.5.0.tgz", + "integrity": "sha512-0WORnk9SkREGUg2V7jHZbuN5x4vcxj/1B0QOcXJjdYWrzZHgLcUzYWWIUecUPJh747Mwjt/42RZDOaFn3L8kPQ==", + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-element-dropdown": { "version": "2.12.4", "resolved": "https://registry.npmjs.org/react-native-element-dropdown/-/react-native-element-dropdown-2.12.4.tgz", @@ -10662,6 +10739,12 @@ "react-native-svg": ">=12.0.0" } }, + "node_modules/react-native-swipe-gestures": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz", + "integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw==", + "license": "MIT" + }, "node_modules/react-native-vector-icons": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.2.0.tgz", @@ -10832,6 +10915,21 @@ "node": ">= 4" } }, + "node_modules/recyclerlistview": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/recyclerlistview/-/recyclerlistview-4.2.3.tgz", + "integrity": "sha512-STR/wj/FyT8EMsBzzhZ1l2goYirMkIgfV3gYEPxI3Kf3lOnu6f7Dryhyw7/IkQrgX5xtTcDrZMqytvteH9rL3g==", + "license": "Apache-2.0", + "dependencies": { + "lodash.debounce": "4.0.8", + "prop-types": "15.8.1", + "ts-object-utils": "0.0.5" + }, + "peerDependencies": { + "react": ">= 15.2.1", + "react-native": ">= 0.30.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -12058,6 +12156,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-object-utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/ts-object-utils/-/ts-object-utils-0.0.5.tgz", + "integrity": "sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA==", + "license": "ISC" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -12569,6 +12673,12 @@ "async-limiter": "~1.0.0" } }, + "node_modules/xdate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/xdate/-/xdate-0.8.3.tgz", + "integrity": "sha512-1NhJWPJwN+VjbkACT9XHbQK4o6exeSVtS2CxhMPwUE7xQakoEFTlwra9YcqV/uHQVyeEUYoYC46VGDJ+etnIiw==", + "license": "(MIT OR GPL-2.0)" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 1bfc8fa..a0a0ef7 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,16 @@ "test": "jest" }, "dependencies": { + "@react-native-async-storage/async-storage": "^2.1.2", "@react-native-community/datetimepicker": "^8.3.0", "@react-navigation/elements": "^2.3.8", "@react-navigation/native": "^7.1.6", "@react-navigation/native-stack": "^7.3.10", "dayjs": "^1.11.13", + "moment": "^2.30.1", "react": "19.0.0", "react-native": "0.78.0", + "react-native-calendars": "^1.1311.1", "react-native-element-dropdown": "^2.12.4", "react-native-paper": "^5.13.2", "react-native-safe-area-context": "^5.4.0", diff --git a/src/components/PassportAppointmentCard.tsx b/src/components/PassportAppointmentCard.tsx index 5be15bc..c0cd9ac 100644 --- a/src/components/PassportAppointmentCard.tsx +++ b/src/components/PassportAppointmentCard.tsx @@ -5,15 +5,15 @@ import FontFamily from '../../assets/styles/FontFamily'; import Colors from '../../assets/styles/Colors'; type PassportAppointmentCardProps = { - applicantName: string; - applicantCode: string; - appointmentDate: string; - appointmentTime: string; - serviceUnit: string; - status: string; + applicantName: string | undefined; + applicantCode: string | undefined; + appointmentDate: string | undefined; + appointmentTime: string | undefined; + serviceUnit: string | undefined; + status: string | undefined; }; -const renderStatusContent = (status: string) => { +const renderStatusContent = (status: string | undefined) => { let backgroundColor; let IconComponent; diff --git a/src/components/TextInput.tsx b/src/components/TextInput.tsx index e4b0056..24eaa9b 100644 --- a/src/components/TextInput.tsx +++ b/src/components/TextInput.tsx @@ -38,8 +38,8 @@ interface TextInputComponentProps { supportText?: string; containerHeight?: any; isMultiline?: boolean; - isDropdownSearchLocation?: boolean; - handlePressSearchLocation?: () => void; + isDropdownPressedSheet?: boolean; + handleDropdownPressed?: () => void; } const TextInputComponent = (props: TextInputComponentProps) => { @@ -59,8 +59,8 @@ const TextInputComponent = (props: TextInputComponentProps) => { supportText, containerHeight, isMultiline = false, - isDropdownSearchLocation = false, - handlePressSearchLocation, + isDropdownPressedSheet = false, + handleDropdownPressed, } = props; const [secureText, setSecureText] = useState(isPassword); @@ -281,7 +281,7 @@ const TextInputComponent = (props: TextInputComponentProps) => { ); } - if (isDropdownSearchLocation) { + if (isDropdownPressedSheet) { return ( {title && ( @@ -291,7 +291,7 @@ const TextInputComponent = (props: TextInputComponentProps) => { )} ({ transform: [{scale: pressed ? 0.99 : 1}], })}> @@ -303,7 +303,14 @@ const TextInputComponent = (props: TextInputComponentProps) => { placeholderTextColor={Colors.primary60.color} editable={false} value={formattedDate} - right={} + right={ + + } multiline={false} textColor="#48454E" disabled={isDisabled} diff --git a/src/components/dialog/DialogLogout.tsx b/src/components/dialog/DialogLogout.tsx new file mode 100644 index 0000000..04fd906 --- /dev/null +++ b/src/components/dialog/DialogLogout.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import {View, Text, StyleSheet} from 'react-native'; +import {Dialog, Portal, Button} from 'react-native-paper'; +import Colors from '../../../assets/styles/Colors'; +import FontFamily from '../../../assets/styles/FontFamily'; + +interface DialogLogoutProps { + visible: boolean; + hideDialog: () => void; + onNavigate: () => void; +} + +const DialogLogout = (props: DialogLogoutProps) => { + const {visible, hideDialog, onNavigate} = props; + + return ( + + + + Apakah Anda yakin akan menutup akun? + + + + + + + + ); +}; + +export default DialogLogout; + +const styles = StyleSheet.create({ + container: { + backgroundColor: 'white', + elevation: 0, + shadowColor: 'transparent', + borderRadius: 20, + }, + title: { + fontSize: 22, + color: Colors.indicatorRed.color, + }, + content: { + marginHorizontal: 24, + marginBottom: 24, + gap: 16, + }, + message: { + fontSize: 14, + ...FontFamily.notoSansRegular, + includeFontPadding: false, + lineHeight: 22, + color: Colors.primary30.color, + }, + buttonContained: { + backgroundColor: Colors.indicatorRed.color, + }, + buttonOutlined: { + borderColor: Colors.indicatorRed.color, + }, +}); diff --git a/src/components/dialog/DialogWarningApplication.tsx b/src/components/dialog/DialogWarningApplication.tsx new file mode 100644 index 0000000..471c16f --- /dev/null +++ b/src/components/dialog/DialogWarningApplication.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import {View, Text, StyleSheet} from 'react-native'; +import {Dialog, Portal, Button} from 'react-native-paper'; +import Colors from '../../../assets/styles/Colors'; +import FontFamily from '../../../assets/styles/FontFamily'; + +interface DialogWarningApplicationProps { + visible: boolean; + hideDialog: () => void; + onNavigate: () => void; +} + +const DialogWarningApplication = (props: DialogWarningApplicationProps) => { + const {visible, hideDialog, onNavigate} = props; + + return ( + + + Peringatan + + + Silakan melakukan pengisian kuesioner. + + + Pastikan data dan jawaban yang Anda berikan benar. + + + Pemberian keterangan yang tidak benar merupakan pelanggaran + keimigrasian sebagaimana ketentuan Pasal 126 huruf c UU No. 6 tahun + 2011 tentang Keimigrasian dan akan mengakibatkan permohonan paspor + Anda ditolak dan pembayaran tidak dapat dikembalikan. + + + + + + ); +}; + +export default DialogWarningApplication; + +const styles = StyleSheet.create({ + container: { + backgroundColor: 'white', + elevation: 0, + shadowColor: 'transparent', + borderRadius: 20, + }, + title: { + fontSize: 22, + color: Colors.secondary30.color, + }, + content: { + marginHorizontal: 24, + marginBottom: 24, + gap: 16, + }, + message: { + fontSize: 14, + ...FontFamily.notoSansRegular, + includeFontPadding: false, + lineHeight: 22, + color: Colors.primary30.color, + }, + button: { + backgroundColor: Colors.primary30.color, + marginTop: 12, + }, +}); diff --git a/src/components/sheet/SheetSelectDate.tsx b/src/components/sheet/SheetSelectDate.tsx index 569cff1..dab5adb 100644 --- a/src/components/sheet/SheetSelectDate.tsx +++ b/src/components/sheet/SheetSelectDate.tsx @@ -1,95 +1,353 @@ -import {StyleSheet, Text, View} from 'react-native'; -import {Dialog, Portal, Button} from 'react-native-paper'; +import { + FlatList, + Modal, + Pressable, + ScrollView, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import {Portal, Button, Divider, RadioButton} from 'react-native-paper'; import Colors from '../../../assets/styles/Colors'; import FontFamily from '../../../assets/styles/FontFamily'; +import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; +import {useState} from 'react'; +import {Calendar} from 'react-native-calendars'; +import moment from 'moment'; +import markedDatesData from '../../data/Steps/MarkedDatesData'; +import arrivalTimesData from '../../data/Steps/ArrivalTimesData'; type Props = { visible: boolean; onClose: () => void; + onContinue: () => void; }; +const LegendItem = ({color, label, border}: any) => ( + + + {label} + +); + const SheetSelectDate = (props: Props) => { - const {visible, onClose} = props; + const {visible, onClose, onContinue} = props; + const [selectedDate, setSelectedDate] = useState(''); + const [currentMonth, setCurrentMonth] = useState('2025-05-01'); + const [selectedTimeSlot, setSelectedTimeSlot] = useState(''); + + const markedDates = { + ...markedDatesData, + [selectedDate]: { + selected: true, + selectedColor: Colors.indicatorOrange.color, + selectedTextColor: Colors.neutral100.color, + }, + }; + + const renderHeaderCalendar = () => { + const headerText = moment(currentMonth).format('MMMM YYYY'); + + const handleMonthChange = (amount: number) => { + const updatedMonth = moment(currentMonth) + .add(amount, 'months') + .format('YYYY-MM-DD'); + setCurrentMonth(updatedMonth); + }; + + return ( + + {headerText} + + handleMonthChange(-1)}> + + + handleMonthChange(1)}> + + + + + ); + }; + + const renderTimeSlotItem = ({item}: any) => { + return ( + + Jam Kedatangan + + + setSelectedTimeSlot(item.id)} + color={Colors.secondary30.color} + uncheckedColor={Colors.secondary30.color} + /> + {item.arrivalTime} + + + + Jumlah Antrian + {item.queueCount} + + + Sisa Kuota + {item.remainingQuota} + + + + + ); + }; return ( - - - Jenis dan Manfaat Paspor - - - - 1. Paspor Biasa - - - Biaya pemrosesan lebih murah, tersedia di seluruh lokasi Kanim. - {'\n'}Biaya pembuatan: Rp350.000 - - - 2. Paspor Elektronik - - - Terdapat chip pada cover paspor yang menyimpan data pemegang paspor - sehingga meningkatkan fitur keamanan, tersedia di 35 Kanim.{'\n'} - Biaya pembuatan: Rp650.000 - - - 3. Paspor Elektronik Polikarbonat - - - Berbahan polikarbonat (PC) pada halaman data pemegang paspor - (halaman 2) yang memiliki fitur keamanan tinggi dan lebih kuat, - tersedia di Kanim Jakarta Selatan, Kanim Jakarta Barat, dan Kanim - Soekarno Hatta.{'\n'}Biaya pembuatan: Rp650.000 - - - - + + + + + + [ + styles.closeButton, + pressed && styles.closeButtonPressed, + ]} + onPress={onClose}> + + + + + + setSelectedDate(day.dateString)} + markedDates={markedDates} + hideArrows={true} + current={currentMonth} + onMonthChange={month => { + const newMonth = `${month.year}-${String( + month.month, + ).padStart(2, '0')}-01`; + setCurrentMonth(newMonth); + }} + renderHeader={renderHeaderCalendar} + /> + + + + + + + + + + + + + + + + + + item.id} + scrollEnabled={false} + renderItem={renderTimeSlotItem} + /> + + + + + - + ); }; const styles = StyleSheet.create({ - dialogContainer: { - backgroundColor: 'white', - elevation: 0, - shadowColor: 'transparent', - borderRadius: 20, + modalBackdrop: { + flex: 1, + backgroundColor: 'rgba(0,0,0,0.3)', }, - dialogContentContainer: { - marginHorizontal: 24, - marginBottom: 24, + modalContentContainer: { + backgroundColor: 'white', + borderTopLeftRadius: 20, + borderTopRightRadius: 20, + paddingTop: 24, + paddingHorizontal: 16, + position: 'absolute', + bottom: 0, + height: '90%', + width: '100%', + maxHeight: '90%', + }, + modalScrollViewContent: { + paddingBottom: 24, + }, + modalCloseButtonContainer: { + flexDirection: 'row', + justifyContent: 'flex-end', + }, + closeButton: { + transform: [{scale: 1}], + }, + closeButtonPressed: { + transform: [{scale: 0.925}], + }, + calendarContainer: { + marginTop: 8, + }, + calendarHeaderContainer: { + width: '100%', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: 16, + }, + calendarHeaderText: { + fontSize: 14, + ...FontFamily.notoSansBold, + color: Colors.secondary30.color, + includeFontPadding: false, + }, + calendarNavigation: { + flexDirection: 'row', + gap: 12, + }, + divider: { + marginVertical: 16, + height: 1, + backgroundColor: Colors.primary70.color, + }, + legendMainContainer: { + flexDirection: 'row', + gap: 32, + }, + legendColumn: { gap: 16, }, - dialogTitle: { - fontSize: 22, - color: Colors.secondary30.color, + legendItemContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, }, - dialogDesc: { - fontSize: 14, + legendColorBox: { + width: 16, + height: 16, + marginRight: 6, + borderRadius: 8, + }, + legendColorBoxBorder: { + borderWidth: 1, + borderColor: '#ccc', + }, + legendLabel: { + fontSize: 11, ...FontFamily.notoSansRegular, includeFontPadding: false, color: Colors.primary30.color, - lineHeight: 22, }, - dialogDescRed: { - ...FontFamily.notoSansBold, - color: Colors.indicatorRed.color, - includeFontPadding: false, + continueButtonWrapper: { + marginTop: 16, }, - buttonContinue: { + continueButton: { backgroundColor: Colors.primary30.color, - marginTop: 12, + }, + timeSlotTitle: { + color: Colors.primary30.color, + includeFontPadding: false, + fontSize: 12, + ...FontFamily.notoSansSemiBold, + }, + timeSlotRadioGroup: { + flexDirection: 'row', + justifyContent: 'space-between', + }, + timeSlotItem: { + flexDirection: 'row', + gap: 12, + alignItems: 'center', + marginVertical: 8, + }, + timeSlotLabel: { + ...FontFamily.notoSansMedium, + fontSize: 12, + includeFontPadding: false, + color: Colors.secondary30.color, + marginStart: -8, + }, + timeSlotQueueInfo: { + flexDirection: 'row', + gap: 12, + alignItems: 'center', + }, + queueDetail: { + gap: 6, + }, + queueDetailTitle: { + fontSize: 10.5, + includeFontPadding: false, + color: Colors.primary30.color, + ...FontFamily.notoSansBold, + }, + queueDetailValue: { + fontSize: 10.5, + includeFontPadding: false, + color: Colors.primary30.color, + ...FontFamily.notoSansRegular, }, }); diff --git a/src/data/DropdownData/PassportTypeData.tsx b/src/data/DropdownData/PassportTypeData.tsx new file mode 100644 index 0000000..907d0e2 --- /dev/null +++ b/src/data/DropdownData/PassportTypeData.tsx @@ -0,0 +1,7 @@ +const passportTypeData = [ + {label: 'Paspor Biasa', value: '1'}, + {label: 'Paspor Elektronik', value: '2'}, + {label: 'Paspor Elektronik Polikarbonat', value: '3'}, +]; + +export default passportTypeData; diff --git a/src/data/History/PassportAppointmentData.tsx b/src/data/History/PassportAppointmentData.tsx index a8372ea..eb47c91 100644 --- a/src/data/History/PassportAppointmentData.tsx +++ b/src/data/History/PassportAppointmentData.tsx @@ -41,17 +41,17 @@ const passportAppointmentData = [ }, { id: '3', - applicantName: 'Salwa Aisyah Adhani', + applicantName: 'Mochammad Adhi Buchori', applicantCode: '1038000002223344', appointmentDate: 'Senin, 14 April 2025', appointmentTime: '08:00 - 09:00 WIB', - serviceUnit: 'Kantor Imigrasi Depok', + serviceUnit: 'Kantor Imigrasi Jakarta', status: 'Menunggu Pembayaran', submissionDate: 'Sabtu, 12 April 2025 18:30', serviceCode: 'EH-GT4JWR', applicationDetails: { nationalIdNumber: '3271234560009120003', - gender: 'Wanita', + gender: 'Pria', applicationType: 'Baru', replacementReason: 'Sekolah di Luar Negeri', applicationPurpose: 'Wisata/Liburan', @@ -101,26 +101,6 @@ const passportAppointmentData = [ }, { id: '6', - applicantName: 'Fadlan Ramadhan', - applicantCode: '1038000008885566', - appointmentDate: 'Selasa, 18 April 2025', - appointmentTime: '09:00 - 10:00 WIB', - serviceUnit: 'Kantor Imigrasi Jakarta Barat', - status: 'Sudah Terbayar', - submissionDate: 'Senin, 14 April 2025 20:00', - serviceCode: 'EH-QZ5TVN', - applicationDetails: { - nationalIdNumber: '3271234560009120006', - gender: 'Pria', - applicationType: 'Baru', - replacementReason: 'Hilang', - applicationPurpose: 'Tugas Kantor', - passportType: 'PASPOR BIASA NON ELEKTRONIK', - fee: '350.000', - }, - }, - { - id: '7', applicantName: 'Nabila Khairunisa', applicantCode: '1038000007773344', appointmentDate: 'Rabu, 19 April 2025', @@ -139,6 +119,26 @@ const passportAppointmentData = [ fee: '650.000', }, }, + { + id: '7', + applicantName: 'Ayaka Haishima', + applicantCode: '1038000008885566', + appointmentDate: 'Selasa, 18 April 2025', + appointmentTime: '09:00 - 10:00 WIB', + serviceUnit: 'Kantor Imigrasi Jakarta Barat', + status: 'Sudah Terbayar', + submissionDate: 'Senin, 14 April 2025 20:00', + serviceCode: 'EH-QZ5TVN', + applicationDetails: { + nationalIdNumber: '3271234560009120006', + gender: 'Wanita', + applicationType: 'Baru', + replacementReason: 'Hilang', + applicationPurpose: 'Kuliah di Luar Negeri', + passportType: 'PASPOR BIASA NON ELEKTRONIK', + fee: '350.000', + }, + }, ]; export default passportAppointmentData; diff --git a/src/data/Steps/ArrivalTimesData.tsx b/src/data/Steps/ArrivalTimesData.tsx new file mode 100644 index 0000000..eabb710 --- /dev/null +++ b/src/data/Steps/ArrivalTimesData.tsx @@ -0,0 +1,64 @@ +const arrivalTimesData = [ + { + id: '1', + arrivalTime: '09:01-10:00', + queueCount: '4 people', + remainingQuota: '1', + }, + { + id: '2', + arrivalTime: '10:01-11:00', + queueCount: '4 people', + remainingQuota: '1', + }, + { + id: '3', + arrivalTime: '11:01-12:00', + queueCount: '3 people', + remainingQuota: '2', + }, + { + id: '4', + arrivalTime: '13:01-14:00', + queueCount: '1 person', + remainingQuota: '4', + }, + { + id: '5', + arrivalTime: '14:01-15:00', + queueCount: '2 people', + remainingQuota: '3', + }, + { + id: '6', + arrivalTime: '15:01-16:00', + queueCount: '5 people', + remainingQuota: '0', + }, + { + id: '7', + arrivalTime: '16:01-17:00', + queueCount: '3 people', + remainingQuota: '2', + }, + { + id: '8', + arrivalTime: '17:01-18:00', + queueCount: '2 people', + remainingQuota: '3', + }, + { + id: '9', + arrivalTime: '18:01-19:00', + queueCount: '1 person', + remainingQuota: '4', + }, + { + id: '10', + arrivalTime: '19:01-20:00', + queueCount: '0 people', + remainingQuota: '5', + }, +]; + +export default arrivalTimesData; diff --git a/src/data/Steps/MarkedDatesData.tsx b/src/data/Steps/MarkedDatesData.tsx new file mode 100644 index 0000000..8a58018 --- /dev/null +++ b/src/data/Steps/MarkedDatesData.tsx @@ -0,0 +1,129 @@ +import Colors from '../../../assets/styles/Colors'; + +const markedDatesData = { + // Kuota penuh (merah) + '2025-05-02': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-03': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-04': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-08': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-09': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-10': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-11': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-17': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-18': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-24': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-25': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-30': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-31': { + selectedColor: Colors.indicatorRed.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + + // Kuota tersedia (hijau) + '2025-05-05': { + selectedColor: Colors.indicatorGreen.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-14': { + selectedColor: Colors.indicatorGreen.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-15': { + selectedColor: Colors.indicatorGreen.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-16': { + selectedColor: Colors.indicatorGreen.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-20': { + selectedColor: Colors.indicatorGreen.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-28': { + selectedColor: Colors.indicatorGreen.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, + '2025-05-29': { + selectedColor: Colors.indicatorGreen.color, + disableTouchEvent: true, + selected: true, + textColor: Colors.neutral100.color, + }, +}; + +export default markedDatesData; diff --git a/src/helper/asyncStorageHelper.tsx b/src/helper/asyncStorageHelper.tsx new file mode 100644 index 0000000..8a516d3 --- /dev/null +++ b/src/helper/asyncStorageHelper.tsx @@ -0,0 +1,40 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; + +// Fungsi untuk menyimpan data +export const storeData = async (key: string, value: T): Promise => { + try { + await AsyncStorage.setItem(key, JSON.stringify(value)); + console.log('Data berhasil disimpan!'); + } catch (e) { + console.error('Gagal menyimpan data:', e); + } +}; + +// Fungsi untuk mengambil data +export const getData = async (key: string): Promise => { + try { + const storedData = await AsyncStorage.getItem(key); + if (storedData !== null) { + return JSON.parse(storedData) as T; + } + return null; + } catch (e) { + console.error('Gagal mengambil data:', e); + return null; + } +}; + +// Fungsi untuk menambah data +export const addData = async (key: string, newData: T): Promise => { + try { + const storedData = await AsyncStorage.getItem(key); + if (storedData !== null) { + const parsedData: T[] = JSON.parse(storedData); + parsedData.push(newData); + await AsyncStorage.setItem(key, JSON.stringify(parsedData)); + console.log('Data berhasil ditambahkan!'); + } + } catch (e) { + console.error('Gagal menambah data:', e); + } +}; diff --git a/src/navigation/RootStack.tsx b/src/navigation/RootStack.tsx index 6d9c660..b5fa938 100644 --- a/src/navigation/RootStack.tsx +++ b/src/navigation/RootStack.tsx @@ -54,7 +54,9 @@ function RootStack() { options={{headerShown: false}} /> - {() => console.log('Show dialog!')} visible />} + {() => ( + {}} visible /> + )} - + + {() => ( + {}} visible /> + )} + ([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchData = async () => { + try { + setIsLoading(true); // mulai loading + const data = await getData('passportAppointments'); + if (Array.isArray(data) && data.length > 0) { + setAppointments(data); + } else { + setAppointments([]); // kosongin kalau tidak ada + } + } catch (error) { + console.error('Error fetching data: ', error); + } finally { + setIsLoading(false); // selesai loading + } + }; + fetchData(); + }, []); + useFocusEffect( useCallback(() => { StatusBar.setBackgroundColor(Colors.secondary30.color); @@ -60,31 +84,37 @@ function HistoryScreen() { )} - - ( - - navigation.navigate('ApplicationDetail', {data: item}) - } - style={({pressed}) => ({ - transform: [{scale: pressed ? 0.975 : 1}], - })}> - - - )} - keyExtractor={item => item.id} - ItemSeparatorComponent={ItemSeparator} - /> - + {isLoading ? ( + + + + ) : ( + + ( + + navigation.navigate('ApplicationDetail', {data: item}) + } + style={({pressed}) => ({ + transform: [{scale: pressed ? 0.975 : 1}], + })}> + + + )} + keyExtractor={item => item.id ?? ''} + ItemSeparatorComponent={ItemSeparator} + /> + + )} ); } diff --git a/src/screens/home/index.tsx b/src/screens/home/index.tsx index 89dcbd2..32846fd 100644 --- a/src/screens/home/index.tsx +++ b/src/screens/home/index.tsx @@ -13,15 +13,15 @@ import { import Colors from '../../../assets/styles/Colors'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import {NativeStackNavigationProp} from '@react-navigation/native-stack'; -import {RootStackParamList} from '../../navigation/type'; +import {PassportAppointment, RootStackParamList} from '../../navigation/type'; import {useFocusEffect, useNavigation} from '@react-navigation/native'; import RegularPassportIcon from '../../../assets/icons/regular_passport.svg'; import ExpressPassportIcon from '../../../assets/icons/express_passport.svg'; import GuidebookIcon from '../../../assets/icons/guidebook.svg'; import EazyPassportIcon from '../../../assets/icons/eazy_passport.svg'; -import passportAppointmentData from '../../data/History/PassportAppointmentData'; import PassportAppointmentCard from '../../components/PassportAppointmentCard'; -import {useCallback, useRef} from 'react'; +import {useCallback, useEffect, useRef, useState} from 'react'; +import {getData} from '../../helper/asyncStorageHelper'; type HomeScreenNavigationProp = NativeStackNavigationProp< RootStackParamList, @@ -32,6 +32,7 @@ const ItemSeparator = () => ; type RenderContentProps = { showDialog: () => void; + lastTwoAppointments: PassportAppointment[]; }; const RenderBanner = () => { @@ -147,7 +148,10 @@ const RenderBanner = () => { ); }; -const RenderContent = ({showDialog}: RenderContentProps) => { +const RenderContent = ({ + showDialog, + lastTwoAppointments, +}: RenderContentProps) => { const navigation = useNavigation(); return ( @@ -223,8 +227,8 @@ const RenderContent = ({showDialog}: RenderContentProps) => { ( + data={lastTwoAppointments} + renderItem={({item}: any) => ( navigation.navigate('ApplicationDetail', {data: item}) @@ -242,7 +246,7 @@ const RenderContent = ({showDialog}: RenderContentProps) => { /> )} - keyExtractor={item => item.id} + keyExtractor={item => item.id ?? ''} ItemSeparatorComponent={ItemSeparator} /> @@ -256,9 +260,28 @@ type HomeScreenProps = { readonly visible: boolean; }; -function HomeScreen({showDialog, visible}: HomeScreenProps) { +function HomeScreen(props: HomeScreenProps) { + const {showDialog, visible} = props; const navigation = useNavigation(); + const [lastTwoAppointments, setLastTwoAppointments] = useState< + PassportAppointment[] + >([]); + + useEffect(() => { + const fetchData = async () => { + try { + const data = await getData('passportAppointments'); + if (Array.isArray(data) && data.length > 0) { + setLastTwoAppointments(data.slice(-2)); + } + } catch (error) { + console.error('Error fetching data: ', error); + } + }; + fetchData(); + }, []); + useFocusEffect( useCallback(() => { StatusBar.setBackgroundColor( @@ -276,7 +299,9 @@ function HomeScreen({showDialog, visible}: HomeScreenProps) { return ( - Halo, X! + + Halo, Salwa Aisyah Adhani! + } + renderItem={() => ( + + )} /> ); diff --git a/src/screens/home/styles.tsx b/src/screens/home/styles.tsx index 213ea4b..9039cc6 100644 --- a/src/screens/home/styles.tsx +++ b/src/screens/home/styles.tsx @@ -16,6 +16,8 @@ const styles = StyleSheet.create({ fontSize: 28, marginVertical: 14, includeFontPadding: false, + flex: 1, + marginEnd: 16, }, appBarContainer: { height: 72, diff --git a/src/screens/login/index.tsx b/src/screens/login/index.tsx index e635039..1550720 100644 --- a/src/screens/login/index.tsx +++ b/src/screens/login/index.tsx @@ -13,8 +13,10 @@ import TextInputComponent from '../../components/TextInput'; import Icon from 'react-native-vector-icons/MaterialIcons'; import {useNavigation} from '@react-navigation/native'; import {NativeStackNavigationProp} from '@react-navigation/native-stack'; -import {RootStackParamList} from '../../navigation/type'; +import {PassportAppointment, RootStackParamList} from '../../navigation/type'; import Colors from '../../../assets/styles/Colors'; +import {getData, storeData} from '../../helper/asyncStorageHelper'; +import passportAppointmentData from '../../data/History/PassportAppointmentData'; type LoginScreenNavigationProp = NativeStackNavigationProp< RootStackParamList, @@ -48,12 +50,18 @@ function LoginScreen() { diff --git a/src/screens/navigationRoute/index.tsx b/src/screens/navigationRoute/index.tsx index 302eeef..b2d47b6 100644 --- a/src/screens/navigationRoute/index.tsx +++ b/src/screens/navigationRoute/index.tsx @@ -1,23 +1,16 @@ import * as React from 'react'; -import { - BottomNavigation, - Button, - Dialog, - PaperProvider, - Portal, - Text, -} from 'react-native-paper'; +import {BottomNavigation, PaperProvider, Text} from 'react-native-paper'; import Colors from '../../../assets/styles/Colors'; import ProfileScreen from '../profile'; import styles from './styles'; import HomeScreen from '../home'; import HistoryScreen from '../history'; -import {View} from 'react-native'; import {useState} from 'react'; import {NativeStackNavigationProp} from '@react-navigation/native-stack'; import {RootStackParamList} from '../../navigation/type'; import {useNavigation} from '@react-navigation/native'; -import FontFamily from '../../../assets/styles/FontFamily'; +import DialogWarningApplication from '../../components/dialog/DialogWarningApplication'; +import DialogLogout from '../../components/dialog/DialogLogout'; type NavigationRouteScreenNavigationProp = NativeStackNavigationProp< RootStackParamList, @@ -26,7 +19,11 @@ type NavigationRouteScreenNavigationProp = NativeStackNavigationProp< function NavigationRouteScreen() { const navigation = useNavigation(); - const [visible, setVisible] = useState(false); + + const [visibleWarningApplicationDialog, setVisibleWarningApplicationDialog] = + useState(false); + const [visibleLogoutDialog, setVisibleLogoutDialog] = useState(false); + const [index, setIndex] = useState(0); const [routes] = useState([ { @@ -48,17 +45,32 @@ function NavigationRouteScreen() { }, ]); - const showDialog = () => setVisible(true); - const hideDialog = () => setVisible(false); + const showWarningApplicationDialog = () => + setVisibleWarningApplicationDialog(true); + const hideWarningApplicationDialog = () => + setVisibleWarningApplicationDialog(false); + + const showLogoutDialog = () => setVisibleLogoutDialog(true); + const hideLogoutDialog = () => setVisibleLogoutDialog(false); const renderScene = ({route}: {route: {key: string}}) => { switch (route.key) { case 'home': - return ; + return ( + + ); case 'history': return ; case 'profile': - return ; + return ( + + ); default: return null; } @@ -82,35 +94,25 @@ function NavigationRouteScreen() { {route.title} )} /> - - - Peringatan - - - Silakan melakukan pengisian kuesioner. - - - Pastikan data dan jawaban yang Anda berikan benar. - - - Pemberian keterangan yang tidak benar merupakan pelanggaran - keimigrasian sebagaimana ketentuan Pasal 126 huruf c UU No. 6 - tahun 2011 tentang Keimigrasian dan akan mengakibatkan permohonan - paspor Anda ditolak dan pembayaran tidak dapat dikembalikan. - - - - - + {visibleWarningApplicationDialog && ( + navigation.navigate('RegularPassport')} + /> + )} + {visibleLogoutDialog && ( + + navigation.reset({ + index: 0, + routes: [{name: 'Login'}], + }) + } + /> + )} ); } diff --git a/src/screens/navigationRoute/styles.tsx b/src/screens/navigationRoute/styles.tsx index efe44d1..6a38d34 100644 --- a/src/screens/navigationRoute/styles.tsx +++ b/src/screens/navigationRoute/styles.tsx @@ -10,32 +10,6 @@ const styles = StyleSheet.create({ fontSize: 12, position: 'absolute', }, - 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, - lineHeight: 22, - color: Colors.primary30.color, - }, - buttonContinue: { - backgroundColor: Colors.primary30.color, - marginTop: 12, - }, }); export default styles; diff --git a/src/screens/profile/index.tsx b/src/screens/profile/index.tsx index 56360e4..e1fdfad 100644 --- a/src/screens/profile/index.tsx +++ b/src/screens/profile/index.tsx @@ -13,20 +13,29 @@ type ProfileScreenNavigationProp = NativeStackNavigationProp< 'Profile' >; -function ProfileScreen() { +type ProfileScreenProps = { + readonly showDialog: () => void; + readonly visible: boolean; +}; + +function ProfileScreen(props: ProfileScreenProps) { + const {showDialog, visible} = props; + const placeholderProfileImage = require('../../../assets/images/placeholderProfileImage.png'); const navigation = useNavigation(); useFocusEffect( useCallback(() => { - StatusBar.setBackgroundColor(Colors.secondary30.color); + StatusBar.setBackgroundColor( + visible ? '#295E70' : Colors.secondary30.color, + ); StatusBar.setBarStyle('light-content'); return () => { StatusBar.setBackgroundColor(Colors.secondary30.color); StatusBar.setBarStyle('light-content'); }; - }, []), + }, [visible]), ); return ( @@ -35,7 +44,7 @@ function ProfileScreen() { - X + Salwa Aisyah Adhani 3271234560009123456 @@ -80,12 +89,7 @@ function ProfileScreen() { mode="outlined" textColor={Colors.indicatorRed.color} style={styles.logoutButton} - onPress={() => - navigation.reset({ - index: 0, - routes: [{name: 'Login'}], - }) - }> + onPress={showDialog}> Keluar diff --git a/src/screens/regularPassport/index.tsx b/src/screens/regularPassport/index.tsx index 4934ee5..e1e2609 100644 --- a/src/screens/regularPassport/index.tsx +++ b/src/screens/regularPassport/index.tsx @@ -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} /> @@ -501,7 +510,11 @@ function RegularPassportScreen() { { - navigation.goBack(), hideSubmitSuccessDialog(); + navigation.reset({ + index: 0, + routes: [{name: 'NavigationRoute'}], + }); + hideSubmitSuccessDialog(); }} /> )} @@ -542,6 +555,14 @@ function RegularPassportScreen() { onClose={hideSearchLocationSheet} /> )} + + {visibleSelectDateSheet && ( + + )} ) : ( 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( + '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 diff --git a/src/screens/regularPassport/steps/Step5Verification/Step5Verification.tsx b/src/screens/regularPassport/steps/Step5Verification/Step5Verification.tsx index e09f374..ec54f6c 100644 --- a/src/screens/regularPassport/steps/Step5Verification/Step5Verification.tsx +++ b/src/screens/regularPassport/steps/Step5Verification/Step5Verification.tsx @@ -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(); + + 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 ( @@ -36,7 +53,7 @@ const Step5Content = (props: Step5VerificationProps) => { styles.applicantDetailTextDesc, {textTransform: 'uppercase', flex: 0}, ]}> - {lastAppointment.applicantName} + {lastAppointment?.applicantName} @@ -63,27 +80,27 @@ const Step5Content = (props: Step5VerificationProps) => { @@ -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}) => ( {label} {value} diff --git a/src/screens/regularPassport/steps/Step6Processing/Step6Processing.tsx b/src/screens/regularPassport/steps/Step6Processing/Step6Processing.tsx index 0adb0f9..b00a12f 100644 --- a/src/screens/regularPassport/steps/Step6Processing/Step6Processing.tsx +++ b/src/screens/regularPassport/steps/Step6Processing/Step6Processing.tsx @@ -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 ( @@ -30,13 +36,12 @@ const Step6Processing = (props: Step6ProcessingProps) => { - {/* Trigger Search Location Bottom Sheet */} { placeholder="Pilih satu jenis paspor" isRequired isDropdown + dropdownItemData={passportTypeData} onIconButtonPress={showPassportTypeInfoDialog} /> @@ -80,12 +86,12 @@ const Step6Processing = (props: Step6ProcessingProps) => { - {/* TODO: Add calendar functionality here. */}