From 2c77afb399da6a5b3451abf246eb254a48f403df Mon Sep 17 00:00:00 2001 From: Mochammad Adhi Buchori Date: Tue, 22 Apr 2025 08:06:07 +0700 Subject: [PATCH] Add screens for Regular Passport, Express Passport, Guidebook, Eazy Passport, and Applicant Detail. Then, add several components in the Applicant Detail screen by parsing data from the History screen --- src/components/PassportAppointmentCard.tsx | 10 +- src/data/History/PassportAppointmentData.tsx | 107 ++++++++++++++- src/model/model.ts | 22 +++ src/navigation/RootStack.tsx | 30 ++++ src/navigation/type.ts | 7 + src/screens/applicationDetail/index.tsx | 137 +++++++++++++++++++ src/screens/applicationDetail/styles.tsx | 82 +++++++++++ src/screens/eazyPassport/index.tsx | 13 ++ src/screens/eazyPassport/styles.tsx | 13 ++ src/screens/expressPassport/index.tsx | 13 ++ src/screens/expressPassport/styles.tsx | 13 ++ src/screens/guidebook/index.tsx | 13 ++ src/screens/guidebook/styles.tsx | 13 ++ src/screens/history/index.tsx | 26 ++-- src/screens/home/index.tsx | 70 +++++++--- src/screens/regularPassport/index.tsx | 13 ++ src/screens/regularPassport/styles.tsx | 13 ++ 17 files changed, 557 insertions(+), 38 deletions(-) create mode 100644 src/model/model.ts create mode 100644 src/screens/applicationDetail/index.tsx create mode 100644 src/screens/applicationDetail/styles.tsx create mode 100644 src/screens/eazyPassport/index.tsx create mode 100644 src/screens/eazyPassport/styles.tsx create mode 100644 src/screens/expressPassport/index.tsx create mode 100644 src/screens/expressPassport/styles.tsx create mode 100644 src/screens/guidebook/index.tsx create mode 100644 src/screens/guidebook/styles.tsx create mode 100644 src/screens/regularPassport/index.tsx create mode 100644 src/screens/regularPassport/styles.tsx diff --git a/src/components/PassportAppointmentCard.tsx b/src/components/PassportAppointmentCard.tsx index 5973f37..5be15bc 100644 --- a/src/components/PassportAppointmentCard.tsx +++ b/src/components/PassportAppointmentCard.tsx @@ -6,7 +6,7 @@ import Colors from '../../assets/styles/Colors'; type PassportAppointmentCardProps = { applicantName: string; - applicantCount: number; + applicantCode: string; appointmentDate: string; appointmentTime: string; serviceUnit: string; @@ -53,7 +53,7 @@ const renderStatusContent = (status: string) => { const PassportAppointmentCard: React.FC = ({ applicantName, - applicantCount, + applicantCode, appointmentDate, appointmentTime, serviceUnit, @@ -64,9 +64,7 @@ const PassportAppointmentCard: React.FC = ({ {applicantName} - - {applicantCount} Pemohon - + {applicantCode} @@ -139,7 +137,7 @@ const styles = StyleSheet.create({ textTransform: 'uppercase', color: Colors.secondary30.color, }, - applicantCountText: { + applicantCodeText: { fontSize: 12, ...FontFamily.notoSansRegular, color: Colors.primary30.color, diff --git a/src/data/History/PassportAppointmentData.tsx b/src/data/History/PassportAppointmentData.tsx index ffcb940..bcddc1d 100644 --- a/src/data/History/PassportAppointmentData.tsx +++ b/src/data/History/PassportAppointmentData.tsx @@ -2,47 +2,142 @@ const passportAppointmentData = [ { id: '1', applicantName: 'Irma Wahyudini', - applicantCount: 1, + applicantCode: '1038000001112223', appointmentDate: 'Kamis, 17 April 2025', - appointmentTime: '10.00 - 11.00 WIB', + appointmentTime: '10:00 - 11:00 WIB', serviceUnit: 'Unit Layanan Paspor I Jakarta Selatan (Pondok Pinang)', status: 'Menunggu Pembayaran', + submissionDate: 'Senin, 14 April 2025 19:15', + serviceCode: 'EH-PL8XQM', + applicationDetails: { + nationalIdNumber: '3271234560009120001', + gender: 'Wanita', + applicationType: 'Baru', + replacementReason: '', + applicationPurpose: 'Ibadah Umrah', + passportType: 'PASPOR BIASA NON ELEKTRONIK', + fee: '350.000', + }, }, { id: '2', applicantName: 'Salwa Aisyah Adhani', - applicantCount: 2, + applicantCode: '1038000002223344', appointmentDate: 'Senin, 14 April 2025', appointmentTime: '08:00 - 09:00 WIB', serviceUnit: 'Kantor Imigrasi Depok', status: 'Sudah Terbayar', + submissionDate: 'Minggu, 13 April 2025 22:45', + serviceCode: 'EH-KS9BNV', + applicationDetails: { + nationalIdNumber: '3271234560009120002', + gender: 'Wanita', + applicationType: 'Penggantian Paspor', + replacementReason: 'Hilang', + applicationPurpose: 'Sekolah di Luar Negeri', + passportType: 'PASPOR BIASA ELEKTRONIK POLIKARBONAT 10 TAHUN', + fee: '950.000', + }, }, { id: '3', applicantName: 'Salwa Aisyah Adhani', - applicantCount: 2, + applicantCode: '1038000002223344', appointmentDate: 'Senin, 14 April 2025', appointmentTime: '08:00 - 09:00 WIB', serviceUnit: 'Kantor Imigrasi Depok', status: 'Menunggu Pembayaran', + submissionDate: 'Sabtu, 12 April 2025 18:30', + serviceCode: 'EH-GT4JWR', + applicationDetails: { + nationalIdNumber: '3271234560009120003', + gender: 'Wanita', + applicationType: 'Baru', + replacementReason: '', + applicationPurpose: 'Wisata/Liburan', + passportType: 'PASPOR BIASA NON ELEKTRONIK', + fee: '350.000', + }, }, { id: '4', applicantName: 'Salwa Aisyah Adhani', - applicantCount: 2, + applicantCode: '1038000002223344', appointmentDate: 'Senin, 23 September 2024', appointmentTime: '10:00 - 11:00 WIB', serviceUnit: 'Kantor Imigrasi Depok', status: 'Permohonan Kadaluarsa', + submissionDate: 'Kamis, 19 September 2024 20:05', + serviceCode: 'EH-RN8XLC', + applicationDetails: { + nationalIdNumber: '3271234560009120004', + gender: 'Wanita', + applicationType: 'Penggantian Paspor', + replacementReason: 'Rusak', + applicationPurpose: 'Kerja di Luar Negeri', + passportType: 'PASPOR BIASA ELEKTRONIK 5 TAHUN', + fee: '650.000', + }, }, { id: '5', applicantName: 'Yulfarisa Hasnah', - applicantCount: 2, + applicantCode: '1038000009971388', appointmentDate: 'Senin, 14 April 2025', appointmentTime: '08:00 - 09:00 WIB', serviceUnit: 'Kantor Imigrasi Depok', status: 'Sudah Terbayar', + submissionDate: 'Rabu, 16 April 2025 21:30', + serviceCode: 'EH-LP7RNC', + applicationDetails: { + nationalIdNumber: '3271234560009123456', + gender: 'Wanita', + applicationType: 'Penggantian Paspor', + replacementReason: 'Penuh/Halaman Penuh', + applicationPurpose: 'Wisata/Liburan', + passportType: 'PASPOR BIASA ELEKTRONIK POLIKARBONAT 10 TAHUN', + fee: '950.000', + }, + }, + { + 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: '', + applicationPurpose: 'Tugas Kantor', + passportType: 'PASPOR BIASA NON ELEKTRONIK', + 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', + }, }, ]; diff --git a/src/model/model.ts b/src/model/model.ts new file mode 100644 index 0000000..afc1d84 --- /dev/null +++ b/src/model/model.ts @@ -0,0 +1,22 @@ +export type ApplicationDetails = { + nationalIdNumber: string; + gender: string; + applicationType: string; + replacementReason: string; + applicationPurpose: string; + passportType: string; + fee: string; +}; + +export type PassportAppointmentData = { + id: string; + applicantName: string; + applicantCode: string; + appointmentDate: string; + appointmentTime: string; + serviceUnit: string; + status: string; + submissionDate: string; + serviceCode: string; + applicationDetails: ApplicationDetails; +}; diff --git a/src/navigation/RootStack.tsx b/src/navigation/RootStack.tsx index 11b65d1..c17c557 100644 --- a/src/navigation/RootStack.tsx +++ b/src/navigation/RootStack.tsx @@ -13,6 +13,11 @@ import {RootStackParamList} from './type'; import TermsAndConnditionsScreen from '../screens/termsAndConditions'; import NavigationRouteScreen from '../screens/navigationRoute'; import SetPasswordScreen from '../screens/setPassword'; +import RegularPassportScreen from '../screens/regularPassport'; +import ExpressPassportScreen from '../screens/expressPassport'; +import GuidebookScreen from '../screens/guidebook'; +import EazyPassportScreen from '../screens/eazyPassport'; +import ApplicationDetailScreen from '../screens/applicationDetail'; const Stack = createNativeStackNavigator(); @@ -79,6 +84,31 @@ function RootStack() { component={SetPasswordScreen} options={{headerShown: false}} /> + + + + + ); } diff --git a/src/navigation/type.ts b/src/navigation/type.ts index 116b507..78a8cac 100644 --- a/src/navigation/type.ts +++ b/src/navigation/type.ts @@ -1,3 +1,5 @@ +import {PassportAppointmentData} from '../model/model'; + export type RootStackParamList = { Login: undefined; NavigationRoute: undefined; @@ -11,4 +13,9 @@ export type RootStackParamList = { EditProfile: undefined; CloseAccount: undefined; SetPassword: undefined; + RegularPassport: undefined; + ExpressPassport: undefined; + Guidebook: undefined; + EazyPassport: undefined; + ApplicationDetail: {data: PassportAppointmentData}; }; diff --git a/src/screens/applicationDetail/index.tsx b/src/screens/applicationDetail/index.tsx new file mode 100644 index 0000000..506ee2a --- /dev/null +++ b/src/screens/applicationDetail/index.tsx @@ -0,0 +1,137 @@ +import React from 'react'; +import {ScrollView, StatusBar, Text, View} from 'react-native'; +import styles from './styles'; +import {useNavigation, useRoute} from '@react-navigation/native'; +import {PassportAppointmentData} from '../../model/model'; +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 {Divider} from 'react-native-paper'; + +type ApplicationDetailScreenNavigationProp = NativeStackNavigationProp< + RootStackParamList, + 'ApplicationDetail' +>; + +const renderStatusContent = (status: string) => { + const statusConfig: { + [key: string]: { + backgroundColor: string; + iconName: string; + title: string; + desc: string; + }; + } = { + 'Permohonan Kadaluarsa': { + backgroundColor: Colors.indicatorRed.color, + iconName: 'close-circle', + title: 'Permohonan Anda telah kadaluarsa', + desc: 'Silakan ajukan permohonan baru', + }, + 'Sudah Terbayar': { + backgroundColor: Colors.indicatorGreen.color, + iconName: 'check-circle', + title: 'Pembayaran Anda telah selesai', + desc: 'Cek dokumen yang diperlukan selama kunjungan', + }, + 'Menunggu Pembayaran': { + backgroundColor: Colors.indicatorOrange.color, + iconName: 'alert-circle', + title: '16 April 2025 23:30', + desc: 'Selesaikan pembayaran Anda sebelum', + }, + }; + + const {backgroundColor, iconName, title, desc} = + statusConfig[status] || statusConfig['Menunggu Pembayaran']; + + return ( + + + + {status === 'Menunggu Pembayaran' ? ( + <> + {desc} + {title} + + ) : ( + <> + {title} + {desc} + + )} + + + ); +}; + +function ApplicationDetailScreen() { + const route = useRoute(); + const {data} = route.params as {data: PassportAppointmentData}; + + const navigation = useNavigation(); + + return ( + + + + navigation.goBack()} + /> + Detail Permohonan + + + + + {renderStatusContent(data.status)} + + + Jadwal Kedatangan + + + + + {data.appointmentDate} + + + + + + {data.appointmentTime} + + + + + + {data.serviceUnit} + + + + + + + + ); +} + +export default ApplicationDetailScreen; diff --git a/src/screens/applicationDetail/styles.tsx b/src/screens/applicationDetail/styles.tsx new file mode 100644 index 0000000..fdd2ddf --- /dev/null +++ b/src/screens/applicationDetail/styles.tsx @@ -0,0 +1,82 @@ +import {StyleSheet} from 'react-native'; +import Colors from '../../../assets/styles/Colors'; +import FontFamily from '../../../assets/styles/FontFamily'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: Colors.secondary70.color, + }, + appBarContainer: { + height: 64, + flexDirection: 'row', + alignItems: 'center', + backgroundColor: Colors.secondary30.color, + }, + appBarTitle: { + color: Colors.neutral100.color, + ...FontFamily.notoSansRegular, + fontSize: 22, + marginStart: 16, + }, + appBarIcon: { + marginLeft: 16, + }, + topContainer: { + backgroundColor: Colors.secondary30.color, + height: 85, + }, + statusContentWrapper: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + paddingVertical: 16, + paddingHorizontal: 24, + borderRadius: 8, + }, + statusContentContainer: { + marginTop: -64, + marginHorizontal: 16, + zIndex: 10, + }, + textStatusContentWrapper: { + gap: 4, + }, + textStatusTitle: { + ...FontFamily.notoSansExtraBold, + fontSize: 13, + color: Colors.neutral100.color, + }, + textStatusDesc: { + ...FontFamily.notoSansMedium, + fontSize: 10, + color: Colors.neutral100.color, + }, + midContainer: { + marginTop: -16, + padding: 16, + backgroundColor: Colors.neutral100.color, + }, + midTextTitle: { + ...FontFamily.notoSansExtraBold, + fontSize: 18, + marginVertical: 12, + color: Colors.primary30.color, + }, + midIconContainer: { + gap: 8, + marginVertical: 8, + }, + midIconContentWrapper: { + flexDirection: 'row', + gap: 6, + }, + midIconContentTextStyle: { + fontSize: 12, + ...FontFamily.notoSansRegular, + color: Colors.primary30.color, + flex: 1, + }, +}); + +export default styles; diff --git a/src/screens/eazyPassport/index.tsx b/src/screens/eazyPassport/index.tsx new file mode 100644 index 0000000..e47e169 --- /dev/null +++ b/src/screens/eazyPassport/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import {Text, View} from 'react-native'; +import styles from './styles'; + +function EazyPassportScreen() { + return ( + + Eazy Passport Screen + + ); +} + +export default EazyPassportScreen; diff --git a/src/screens/eazyPassport/styles.tsx b/src/screens/eazyPassport/styles.tsx new file mode 100644 index 0000000..1f6a290 --- /dev/null +++ b/src/screens/eazyPassport/styles.tsx @@ -0,0 +1,13 @@ +import {StyleSheet} from 'react-native'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignContent: 'center', + alignItems: 'center', + backgroundColor: 'white', + justifyContent: 'center', + }, +}); + +export default styles; diff --git a/src/screens/expressPassport/index.tsx b/src/screens/expressPassport/index.tsx new file mode 100644 index 0000000..2dca1a6 --- /dev/null +++ b/src/screens/expressPassport/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import {Text, View} from 'react-native'; +import styles from './styles'; + +function ExpressPassportScreen() { + return ( + + Express Passport Screen + + ); +} + +export default ExpressPassportScreen; diff --git a/src/screens/expressPassport/styles.tsx b/src/screens/expressPassport/styles.tsx new file mode 100644 index 0000000..1f6a290 --- /dev/null +++ b/src/screens/expressPassport/styles.tsx @@ -0,0 +1,13 @@ +import {StyleSheet} from 'react-native'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignContent: 'center', + alignItems: 'center', + backgroundColor: 'white', + justifyContent: 'center', + }, +}); + +export default styles; diff --git a/src/screens/guidebook/index.tsx b/src/screens/guidebook/index.tsx new file mode 100644 index 0000000..c86fcb0 --- /dev/null +++ b/src/screens/guidebook/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import {Text, View} from 'react-native'; +import styles from './styles'; + +function GuidebookScreen() { + return ( + + Guidebook Screen + + ); +} + +export default GuidebookScreen; diff --git a/src/screens/guidebook/styles.tsx b/src/screens/guidebook/styles.tsx new file mode 100644 index 0000000..1f6a290 --- /dev/null +++ b/src/screens/guidebook/styles.tsx @@ -0,0 +1,13 @@ +import {StyleSheet} from 'react-native'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignContent: 'center', + alignItems: 'center', + backgroundColor: 'white', + justifyContent: 'center', + }, +}); + +export default styles; diff --git a/src/screens/history/index.tsx b/src/screens/history/index.tsx index d986832..1a11bd6 100644 --- a/src/screens/history/index.tsx +++ b/src/screens/history/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {FlatList, StatusBar, Text, View} from 'react-native'; +import {FlatList, Pressable, StatusBar, Text, View} from 'react-native'; import styles from './styles'; import Colors from '../../../assets/styles/Colors'; import passportAppointmentData from '../../data/History/PassportAppointmentData'; @@ -52,14 +52,22 @@ function HistoryScreen() { ( - + + 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 097face..40a31e8 100644 --- a/src/screens/home/index.tsx +++ b/src/screens/home/index.tsx @@ -130,27 +130,55 @@ const RenderContent = () => { Layanan - + navigation.navigate('RegularPassport')} + style={({pressed}) => [ + styles.serviceIcon, + { + transform: [{scale: pressed ? 0.925 : 1}], + }, + ]}> - + Paspor Reguler - + navigation.navigate('ExpressPassport')} + style={({pressed}) => [ + styles.serviceIcon, + { + transform: [{scale: pressed ? 0.925 : 1}], + }, + ]}> - + Paspor Percepatan - + navigation.navigate('Guidebook')} + style={({pressed}) => [ + styles.serviceIcon, + { + transform: [{scale: pressed ? 0.925 : 1}], + }, + ]}> - + Buku Panduan - + navigation.navigate('EazyPassport')} + style={({pressed}) => [ + styles.serviceIcon, + { + transform: [{scale: pressed ? 0.925 : 1}], + }, + ]}> - + EAZY Pasport @@ -168,16 +196,24 @@ const RenderContent = () => { ( - + + navigation.navigate('ApplicationDetail', {data: item}) + } + style={({pressed}) => ({ + transform: [{scale: pressed ? 0.975 : 1}], + })}> + + )} keyExtractor={item => item.id} ItemSeparatorComponent={ItemSeparator} diff --git a/src/screens/regularPassport/index.tsx b/src/screens/regularPassport/index.tsx new file mode 100644 index 0000000..f66639b --- /dev/null +++ b/src/screens/regularPassport/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import {Text, View} from 'react-native'; +import styles from './styles'; + +function RegularPassportScreen() { + return ( + + Regular Passport Screen + + ); +} + +export default RegularPassportScreen; diff --git a/src/screens/regularPassport/styles.tsx b/src/screens/regularPassport/styles.tsx new file mode 100644 index 0000000..1f6a290 --- /dev/null +++ b/src/screens/regularPassport/styles.tsx @@ -0,0 +1,13 @@ +import {StyleSheet} from 'react-native'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignContent: 'center', + alignItems: 'center', + backgroundColor: 'white', + justifyContent: 'center', + }, +}); + +export default styles;