diff --git a/app/App.js b/app/App.js new file mode 100644 index 00000000..e43ea3e7 --- /dev/null +++ b/app/App.js @@ -0,0 +1,245 @@ + +import 'react-native-gesture-handler'; +import * as React from 'react'; +import { Platform, AppState } from 'react-native'; +import { Provider } from "react-redux"; +import firebase from '@react-native-firebase/app'; +import '@react-native-firebase/messaging'; +import { NativeBaseProvider } from 'native-base'; +import Router from './screens/route.js'; +import store from './redux/store'; + +import DeviceInfo from 'react-native-device-info'; +import DB from './components/storage/'; + +var PushNotification = require("react-native-push-notification"); + +export default class App extends React.Component { + + constructor(props) { + super(props) + } + + _isMounted = false + state = { + appState: AppState.currentState, + backgroundCaptureTime: null + } + + async componentDidMount() { + this._isMounted = true + this.saveDeviceUUID() + this.notificationAuthorization() + this.createNotificationListener() + AppState.addEventListener('change', this._handleAppStateChange) + } + + componentWillUnmount() { + this._isMounted = false + try { + this.messageListener() + this.notificationOpenedListener() + this.notificationListener() + AppState.removeEventListener('change', this._handleAppStateChange) + } catch (error) {} + } + + _handleAppStateChange = (nextAppState) => { + if(this.state.appState.match(/inactive|background/) && nextAppState === 'active') { + + } else { + + } + } + + saveDeviceUUID = () => { + DB.set("deviceUUID", DeviceInfo.getUniqueId(), () => {}, (e) => console.log("DEVICE INFO SAVING FAILED!", e)) + } + + notificationAuthorization = async () => { + const authStatus = await firebase.messaging().requestPermission(); + const enabled = authStatus === firebase.messaging.AuthorizationStatus.AUTHORIZED || authStatus === firebase.messaging.AuthorizationStatus.PROVISIONAL; + if (enabled) { + try { + const token = await firebase.messaging().getToken() + if(token) { + let existingToken = await DB.get("fcmToken") || "" + if(token != existingToken){ + DB.set("fcmRegistration", "new", (r) => {}, (e) => {}) + DB.set("fcmToken", token, () => console.log("FCM TOKEN SAVED", token), () => console.log("FCM TOKEN SAVING FAILED")) + } + } + console.log('device_token:', token); + } catch (error) { + console.log(error); + } + } + } + + createNotificationListener = () => { + this.messageListener = firebase.messaging().onMessage(message => { + const { notification } = message + PushNotification.localNotification({ + title: notification.title || "Unioil Loyalty App", + message: notification.body, + playSound: false, + soundName: "default" + }) + }); + + /* + * Triggered when a particular notification has been received in foreground + * */ + this.notificationListener = firebase.messaging().onMessage(async remoteMessage => { + console.log(remoteMessage) + }); + + /* + * If your app is in background, you can listen for when a notification is clicked / tapped / opened as follows: + * */ + this.notificationOpenedListener = firebase.messaging().onNotificationOpenedApp(remoteMessage => { + console.log(remoteMessage) + }); + + + firebase.messaging() + .getInitialNotification() + .then(async remoteMessage => { + if (remoteMessage) { + if(Platform.OS == 'ios'){ + console.log( + 'Notification caused app to open from quit state:', + remoteMessage.data.notification, + ); + let result = await DB.AddNotification({ + messageId: remoteMessage.data.from, + title: remoteMessage.data.notification.title, + body: remoteMessage.data.notification.body, + visible: true, + delivery: false, + recieved: remoteMessage.data.from + }) + console.log("Notifications rendered on background", result) + }else{ + console.log( + 'Notification caused app to open from quit state:', + remoteMessage.notification, + ); + let result = await DB.AddNotification({ + messageId: remoteMessage.messageId, + title: remoteMessage.notification.title, + body: remoteMessage.notification.body, + visible: true, + delivery: false, + recieved: remoteMessage.sentTime + }) + console.log("Notifications rendered on background", result) + } + + } + }); + } + + render() { + return ( + + + + + + ) + } +} + +// export default function App(){ + +// useEffect(() => { +// const uniqueId = DeviceInfo.getUniqueId(); +// DB.set("deviceUUID", uniqueId, function(){ +// }, function(e){ +// console.log("DEVICE INFO SAVING FAILED!", e) +// }) +// }, []) + +// const GetFCMToken = () => { +// messaging().getToken().then(async fcmToken => { +// if(fcmToken){ +// let existingToken = await DB.get("fcmToken") || "" +// if(fcmToken != existingToken){ +// DB.set("fcmRegistration", "new", (r) => console.log(r), (e) => console.log(e)) +// DB.set("fcmToken", fcmToken, () => console.log("FCM TOKEN SAVED"), () => console.log("FCM TOKEN SAVING FAILED")) +// } +// } +// }) +// } + +// const NotificationAuthorization = async () => { +// await messaging().registerDeviceForRemoteMessages(); +// const enabled = await messaging().hasPermission() +// if (!enabled) { +// try { +// await messaging().requestPermission() +// } catch (error) { +// // permission denied +// return +// } +// } +// GetFCMToken() +// } + +// useEffect(() => { +// if(Platform.OS === 'ios'){ +// NotificationAuthorization() +// }else{ +// GetFCMToken() +// } +// }, []) + +// useEffect(() => { +// messaging() +// .getInitialNotification() +// .then(async remoteMessage => { +// if (remoteMessage) { + +// if(Platform.OS == 'ios'){ +// console.log( +// 'Notification caused app to open from quit state:', +// remoteMessage.data.notification, +// ); +// let result = await DB.AddNotification({ +// messageId: remoteMessage.data.from, +// title: remoteMessage.data.notification.title, +// body: remoteMessage.data.notification.body, +// visible: true, +// delivery: false, +// recieved: remoteMessage.data.from +// }) +// console.log("Notifications rendered on background", result) +// }else{ +// console.log( +// 'Notification caused app to open from quit state:', +// remoteMessage.notification, +// ); +// let result = await DB.AddNotification({ +// messageId: remoteMessage.messageId, +// title: remoteMessage.notification.title, +// body: remoteMessage.notification.body, +// visible: true, +// delivery: false, +// recieved: remoteMessage.sentTime +// }) +// console.log("Notifications rendered on background", result) +// } + +// } +// }); +// }, []) + +// return ( +// +// +// +// +// +// ) +// } \ No newline at end of file diff --git a/app/assets/100086780_691038501729006_175336722689687552_n.jpg b/app/assets/100086780_691038501729006_175336722689687552_n.jpg new file mode 100644 index 00000000..26337fa4 Binary files /dev/null and b/app/assets/100086780_691038501729006_175336722689687552_n.jpg differ diff --git a/app/assets/98331769_3168630556522364_3516965718727852032_n.jpg b/app/assets/98331769_3168630556522364_3516965718727852032_n.jpg new file mode 100644 index 00000000..984eb354 Binary files /dev/null and b/app/assets/98331769_3168630556522364_3516965718727852032_n.jpg differ diff --git a/app/assets/baseline_call_black_24.png b/app/assets/baseline_call_black_24.png new file mode 100755 index 00000000..04423ef7 Binary files /dev/null and b/app/assets/baseline_call_black_24.png differ diff --git a/app/assets/baseline_delete_white_24.png b/app/assets/baseline_delete_white_24.png new file mode 100755 index 00000000..fe91d65f Binary files /dev/null and b/app/assets/baseline_delete_white_24.png differ diff --git a/app/assets/baseline_edit_white_24.png b/app/assets/baseline_edit_white_24.png new file mode 100755 index 00000000..c2124f1b Binary files /dev/null and b/app/assets/baseline_edit_white_24.png differ diff --git a/app/assets/baseline_email_black_24.png b/app/assets/baseline_email_black_24.png new file mode 100755 index 00000000..e2d1d37e Binary files /dev/null and b/app/assets/baseline_email_black_24.png differ diff --git a/app/assets/baseline_info_black_18.png b/app/assets/baseline_info_black_18.png new file mode 100755 index 00000000..2ac9e1d5 Binary files /dev/null and b/app/assets/baseline_info_black_18.png differ diff --git a/app/assets/baseline_keyboard_arrow_left_black_48.png b/app/assets/baseline_keyboard_arrow_left_black_48.png new file mode 100755 index 00000000..45c24123 Binary files /dev/null and b/app/assets/baseline_keyboard_arrow_left_black_48.png differ diff --git a/app/assets/baseline_keyboard_arrow_right_black_48.png b/app/assets/baseline_keyboard_arrow_right_black_48.png new file mode 100755 index 00000000..16eed615 Binary files /dev/null and b/app/assets/baseline_keyboard_arrow_right_black_48.png differ diff --git a/app/assets/bg_classic.png b/app/assets/bg_classic.png new file mode 100755 index 00000000..e1e62efb Binary files /dev/null and b/app/assets/bg_classic.png differ diff --git a/app/assets/bg_fuel_efficiency.png b/app/assets/bg_fuel_efficiency.png new file mode 100755 index 00000000..871bae35 Binary files /dev/null and b/app/assets/bg_fuel_efficiency.png differ diff --git a/app/assets/bg_grab.png b/app/assets/bg_grab.png new file mode 100755 index 00000000..db7eac40 Binary files /dev/null and b/app/assets/bg_grab.png differ diff --git a/app/assets/bg_ica.png b/app/assets/bg_ica.png new file mode 100755 index 00000000..31cdff23 Binary files /dev/null and b/app/assets/bg_ica.png differ diff --git a/app/assets/bg_scheduler.png b/app/assets/bg_scheduler.png new file mode 100755 index 00000000..bc2ed8be Binary files /dev/null and b/app/assets/bg_scheduler.png differ diff --git a/app/assets/bg_xavier.png b/app/assets/bg_xavier.png new file mode 100755 index 00000000..25e2998b Binary files /dev/null and b/app/assets/bg_xavier.png differ diff --git a/app/assets/dexie.jpg b/app/assets/dexie.jpg new file mode 100644 index 00000000..77692140 Binary files /dev/null and b/app/assets/dexie.jpg differ diff --git a/app/assets/empty_state_history.png b/app/assets/empty_state_history.png new file mode 100755 index 00000000..aa47255b Binary files /dev/null and b/app/assets/empty_state_history.png differ diff --git a/app/assets/empty_state_home_promo.png b/app/assets/empty_state_home_promo.png new file mode 100755 index 00000000..9ac95caa Binary files /dev/null and b/app/assets/empty_state_home_promo.png differ diff --git a/app/assets/empty_state_image.png b/app/assets/empty_state_image.png new file mode 100755 index 00000000..2731a77a Binary files /dev/null and b/app/assets/empty_state_image.png differ diff --git a/app/assets/empty_state_no_internet.png b/app/assets/empty_state_no_internet.png new file mode 100755 index 00000000..25de66a0 Binary files /dev/null and b/app/assets/empty_state_no_internet.png differ diff --git a/app/assets/empty_state_promo.png b/app/assets/empty_state_promo.png new file mode 100755 index 00000000..d496bd9e Binary files /dev/null and b/app/assets/empty_state_promo.png differ diff --git a/app/assets/empty_state_top_up_error.png b/app/assets/empty_state_top_up_error.png new file mode 100755 index 00000000..a044e0f3 Binary files /dev/null and b/app/assets/empty_state_top_up_error.png differ diff --git a/app/assets/empty_state_top_up_success.png b/app/assets/empty_state_top_up_success.png new file mode 100755 index 00000000..5ae232b2 Binary files /dev/null and b/app/assets/empty_state_top_up_success.png differ diff --git a/app/assets/empty_state_transaction.png b/app/assets/empty_state_transaction.png new file mode 100755 index 00000000..1a353165 Binary files /dev/null and b/app/assets/empty_state_transaction.png differ diff --git a/app/assets/googleMapsIcon.png b/app/assets/googleMapsIcon.png new file mode 100644 index 00000000..924b442b Binary files /dev/null and b/app/assets/googleMapsIcon.png differ diff --git a/app/assets/guide_card.png b/app/assets/guide_card.png new file mode 100755 index 00000000..3061786d Binary files /dev/null and b/app/assets/guide_card.png differ diff --git a/app/assets/ic_about.png b/app/assets/ic_about.png new file mode 100755 index 00000000..cef86beb Binary files /dev/null and b/app/assets/ic_about.png differ diff --git a/app/assets/ic_account.png b/app/assets/ic_account.png new file mode 100755 index 00000000..1989090e Binary files /dev/null and b/app/assets/ic_account.png differ diff --git a/app/assets/ic_account_active.png b/app/assets/ic_account_active.png new file mode 100644 index 00000000..f7f7be0e Binary files /dev/null and b/app/assets/ic_account_active.png differ diff --git a/app/assets/ic_account_inactive.png b/app/assets/ic_account_inactive.png new file mode 100644 index 00000000..c573f41e Binary files /dev/null and b/app/assets/ic_account_inactive.png differ diff --git a/app/assets/ic_account_inactive2.png b/app/assets/ic_account_inactive2.png new file mode 100644 index 00000000..5d017b8b Binary files /dev/null and b/app/assets/ic_account_inactive2.png differ diff --git a/app/assets/ic_add_white_24dp.png b/app/assets/ic_add_white_24dp.png new file mode 100755 index 00000000..d64c22e9 Binary files /dev/null and b/app/assets/ic_add_white_24dp.png differ diff --git a/app/assets/ic_card.png b/app/assets/ic_card.png new file mode 100755 index 00000000..10c93ee2 Binary files /dev/null and b/app/assets/ic_card.png differ diff --git a/app/assets/ic_card_active.png b/app/assets/ic_card_active.png new file mode 100644 index 00000000..211f7efd Binary files /dev/null and b/app/assets/ic_card_active.png differ diff --git a/app/assets/ic_card_inactive.png b/app/assets/ic_card_inactive.png new file mode 100644 index 00000000..8b741a94 Binary files /dev/null and b/app/assets/ic_card_inactive.png differ diff --git a/app/assets/ic_card_inactive2.png b/app/assets/ic_card_inactive2.png new file mode 100644 index 00000000..f8718ca1 Binary files /dev/null and b/app/assets/ic_card_inactive2.png differ diff --git a/app/assets/ic_chevron_down.png b/app/assets/ic_chevron_down.png new file mode 100755 index 00000000..0756f042 Binary files /dev/null and b/app/assets/ic_chevron_down.png differ diff --git a/app/assets/ic_close_white_24dp.png b/app/assets/ic_close_white_24dp.png new file mode 100755 index 00000000..39641921 Binary files /dev/null and b/app/assets/ic_close_white_24dp.png differ diff --git a/app/assets/ic_contact.png b/app/assets/ic_contact.png new file mode 100755 index 00000000..43bcd1d3 Binary files /dev/null and b/app/assets/ic_contact.png differ diff --git a/app/assets/ic_dashboard.png b/app/assets/ic_dashboard.png new file mode 100755 index 00000000..285fb4e1 Binary files /dev/null and b/app/assets/ic_dashboard.png differ diff --git a/app/assets/ic_fav_empty.png b/app/assets/ic_fav_empty.png new file mode 100755 index 00000000..a30d4eb3 Binary files /dev/null and b/app/assets/ic_fav_empty.png differ diff --git a/app/assets/ic_fav_full.png b/app/assets/ic_fav_full.png new file mode 100755 index 00000000..1b00f75a Binary files /dev/null and b/app/assets/ic_fav_full.png differ diff --git a/app/assets/ic_fuel.png b/app/assets/ic_fuel.png new file mode 100755 index 00000000..1baa3ea6 Binary files /dev/null and b/app/assets/ic_fuel.png differ diff --git a/app/assets/ic_fuel_tab.png b/app/assets/ic_fuel_tab.png new file mode 100755 index 00000000..67058c16 Binary files /dev/null and b/app/assets/ic_fuel_tab.png differ diff --git a/app/assets/ic_history.png b/app/assets/ic_history.png new file mode 100755 index 00000000..3a224f31 Binary files /dev/null and b/app/assets/ic_history.png differ diff --git a/app/assets/ic_history_active.png b/app/assets/ic_history_active.png new file mode 100644 index 00000000..dfb0981c Binary files /dev/null and b/app/assets/ic_history_active.png differ diff --git a/app/assets/ic_home.png b/app/assets/ic_home.png new file mode 100755 index 00000000..ad9489cf Binary files /dev/null and b/app/assets/ic_home.png differ diff --git a/app/assets/ic_home_inactive.png b/app/assets/ic_home_inactive.png new file mode 100644 index 00000000..a25877af Binary files /dev/null and b/app/assets/ic_home_inactive.png differ diff --git a/app/assets/ic_info.png b/app/assets/ic_info.png new file mode 100755 index 00000000..8111603a Binary files /dev/null and b/app/assets/ic_info.png differ diff --git a/app/assets/ic_km_liter.png b/app/assets/ic_km_liter.png new file mode 100755 index 00000000..637df6dd Binary files /dev/null and b/app/assets/ic_km_liter.png differ diff --git a/app/assets/ic_launcher.png b/app/assets/ic_launcher.png new file mode 100755 index 00000000..def89273 Binary files /dev/null and b/app/assets/ic_launcher.png differ diff --git a/app/assets/ic_launcher_foreground.png b/app/assets/ic_launcher_foreground.png new file mode 100755 index 00000000..fd884a2e Binary files /dev/null and b/app/assets/ic_launcher_foreground.png differ diff --git a/app/assets/ic_launcher_round.png b/app/assets/ic_launcher_round.png new file mode 100755 index 00000000..2511a290 Binary files /dev/null and b/app/assets/ic_launcher_round.png differ diff --git a/app/assets/ic_logout.png b/app/assets/ic_logout.png new file mode 100755 index 00000000..497f24a8 Binary files /dev/null and b/app/assets/ic_logout.png differ diff --git a/app/assets/ic_my_location_black_24dp.png b/app/assets/ic_my_location_black_24dp.png new file mode 100755 index 00000000..0812b0e3 Binary files /dev/null and b/app/assets/ic_my_location_black_24dp.png differ diff --git a/app/assets/ic_my_profile_card.png b/app/assets/ic_my_profile_card.png new file mode 100755 index 00000000..c65e0dd7 Binary files /dev/null and b/app/assets/ic_my_profile_card.png differ diff --git a/app/assets/ic_my_profile_profile.png b/app/assets/ic_my_profile_profile.png new file mode 100755 index 00000000..a1092f26 Binary files /dev/null and b/app/assets/ic_my_profile_profile.png differ diff --git a/app/assets/ic_payatpump.png b/app/assets/ic_payatpump.png new file mode 100644 index 00000000..8f57d3fc Binary files /dev/null and b/app/assets/ic_payatpump.png differ diff --git a/app/assets/ic_peso.png b/app/assets/ic_peso.png new file mode 100755 index 00000000..747b921e Binary files /dev/null and b/app/assets/ic_peso.png differ diff --git a/app/assets/ic_products.png b/app/assets/ic_products.png new file mode 100755 index 00000000..d61ea0e1 Binary files /dev/null and b/app/assets/ic_products.png differ diff --git a/app/assets/ic_promos.png b/app/assets/ic_promos.png new file mode 100755 index 00000000..8b622ce0 Binary files /dev/null and b/app/assets/ic_promos.png differ diff --git a/app/assets/ic_promos_active.png b/app/assets/ic_promos_active.png new file mode 100644 index 00000000..b5bc6f39 Binary files /dev/null and b/app/assets/ic_promos_active.png differ diff --git a/app/assets/ic_reminder.png b/app/assets/ic_reminder.png new file mode 100755 index 00000000..0596fa1d Binary files /dev/null and b/app/assets/ic_reminder.png differ diff --git a/app/assets/ic_rewards.png b/app/assets/ic_rewards.png new file mode 100755 index 00000000..4ec55365 Binary files /dev/null and b/app/assets/ic_rewards.png differ diff --git a/app/assets/ic_rewards_active.png b/app/assets/ic_rewards_active.png new file mode 100644 index 00000000..df1cd54a Binary files /dev/null and b/app/assets/ic_rewards_active.png differ diff --git a/app/assets/ic_scheduler.png b/app/assets/ic_scheduler.png new file mode 100755 index 00000000..c7518313 Binary files /dev/null and b/app/assets/ic_scheduler.png differ diff --git a/app/assets/ic_search_white_24dp.png b/app/assets/ic_search_white_24dp.png new file mode 100755 index 00000000..dd5adfc7 Binary files /dev/null and b/app/assets/ic_search_white_24dp.png differ diff --git a/app/assets/ic_selected_station_pin.png b/app/assets/ic_selected_station_pin.png new file mode 100755 index 00000000..36bcbdc4 Binary files /dev/null and b/app/assets/ic_selected_station_pin.png differ diff --git a/app/assets/ic_station.png b/app/assets/ic_station.png new file mode 100755 index 00000000..0fd3ca6e Binary files /dev/null and b/app/assets/ic_station.png differ diff --git a/app/assets/ic_station_map_pin.png b/app/assets/ic_station_map_pin.png new file mode 100755 index 00000000..cf42a0b8 Binary files /dev/null and b/app/assets/ic_station_map_pin.png differ diff --git a/app/assets/ic_station_map_pin_lg.png b/app/assets/ic_station_map_pin_lg.png new file mode 100755 index 00000000..79a64270 Binary files /dev/null and b/app/assets/ic_station_map_pin_lg.png differ diff --git a/app/assets/ic_stations.png b/app/assets/ic_stations.png new file mode 100755 index 00000000..acdb6d70 Binary files /dev/null and b/app/assets/ic_stations.png differ diff --git a/app/assets/ic_stations_active.png b/app/assets/ic_stations_active.png new file mode 100644 index 00000000..368a9846 Binary files /dev/null and b/app/assets/ic_stations_active.png differ diff --git a/app/assets/ic_topup.png b/app/assets/ic_topup.png new file mode 100755 index 00000000..4191f251 Binary files /dev/null and b/app/assets/ic_topup.png differ diff --git a/app/assets/ic_tutorial.png b/app/assets/ic_tutorial.png new file mode 100755 index 00000000..76bf54aa Binary files /dev/null and b/app/assets/ic_tutorial.png differ diff --git a/app/assets/ic_type.png b/app/assets/ic_type.png new file mode 100755 index 00000000..c6d0ccae Binary files /dev/null and b/app/assets/ic_type.png differ diff --git a/app/assets/icon_googlemaps.png b/app/assets/icon_googlemaps.png new file mode 100755 index 00000000..f5ac4a58 Binary files /dev/null and b/app/assets/icon_googlemaps.png differ diff --git a/app/assets/icon_internet.png b/app/assets/icon_internet.png new file mode 100755 index 00000000..1706adbf Binary files /dev/null and b/app/assets/icon_internet.png differ diff --git a/app/assets/icon_unioil.png b/app/assets/icon_unioil.png new file mode 100755 index 00000000..538849bc Binary files /dev/null and b/app/assets/icon_unioil.png differ diff --git a/app/assets/icon_waze.png b/app/assets/icon_waze.png new file mode 100755 index 00000000..1e9ca6ee Binary files /dev/null and b/app/assets/icon_waze.png differ diff --git a/app/assets/img_bg_about.png b/app/assets/img_bg_about.png new file mode 100755 index 00000000..786a44d6 Binary files /dev/null and b/app/assets/img_bg_about.png differ diff --git a/app/assets/loading.gif b/app/assets/loading.gif new file mode 100644 index 00000000..81105847 Binary files /dev/null and b/app/assets/loading.gif differ diff --git a/app/assets/login_background.jpg b/app/assets/login_background.jpg new file mode 100644 index 00000000..fe6a5291 Binary files /dev/null and b/app/assets/login_background.jpg differ diff --git a/app/assets/login_bg.jpg b/app/assets/login_bg.jpg new file mode 100644 index 00000000..c925913b Binary files /dev/null and b/app/assets/login_bg.jpg differ diff --git a/app/assets/logo_paypal.png b/app/assets/logo_paypal.png new file mode 100755 index 00000000..f562f6af Binary files /dev/null and b/app/assets/logo_paypal.png differ diff --git a/app/assets/logo_unioil.png b/app/assets/logo_unioil.png new file mode 100755 index 00000000..3f695b43 Binary files /dev/null and b/app/assets/logo_unioil.png differ diff --git a/app/assets/logo_unioil_new.png b/app/assets/logo_unioil_new.png new file mode 100644 index 00000000..965794ba Binary files /dev/null and b/app/assets/logo_unioil_new.png differ diff --git a/app/assets/logo_unioil_reverse.png b/app/assets/logo_unioil_reverse.png new file mode 100755 index 00000000..39405dbd Binary files /dev/null and b/app/assets/logo_unioil_reverse.png differ diff --git a/app/assets/mapsIcon.png b/app/assets/mapsIcon.png new file mode 100644 index 00000000..8760537e Binary files /dev/null and b/app/assets/mapsIcon.png differ diff --git a/app/assets/onboarding_01.png b/app/assets/onboarding_01.png new file mode 100644 index 00000000..34b74e40 Binary files /dev/null and b/app/assets/onboarding_01.png differ diff --git a/app/assets/onboarding_01x.png b/app/assets/onboarding_01x.png new file mode 100755 index 00000000..0c6accc4 Binary files /dev/null and b/app/assets/onboarding_01x.png differ diff --git a/app/assets/onboarding_02.png b/app/assets/onboarding_02.png new file mode 100755 index 00000000..42f1657d Binary files /dev/null and b/app/assets/onboarding_02.png differ diff --git a/app/assets/onboarding_03.png b/app/assets/onboarding_03.png new file mode 100644 index 00000000..b902601d Binary files /dev/null and b/app/assets/onboarding_03.png differ diff --git a/app/assets/onboarding_03x.png b/app/assets/onboarding_03x.png new file mode 100755 index 00000000..372ce777 Binary files /dev/null and b/app/assets/onboarding_03x.png differ diff --git a/app/assets/onboarding_04.png b/app/assets/onboarding_04.png new file mode 100755 index 00000000..48e5b059 Binary files /dev/null and b/app/assets/onboarding_04.png differ diff --git a/app/assets/philippine_peso.png b/app/assets/philippine_peso.png new file mode 100644 index 00000000..dee2d82d Binary files /dev/null and b/app/assets/philippine_peso.png differ diff --git a/app/assets/place_holder_profile_pic.png b/app/assets/place_holder_profile_pic.png new file mode 100755 index 00000000..3ba19110 Binary files /dev/null and b/app/assets/place_holder_profile_pic.png differ diff --git a/app/assets/points_balance.png b/app/assets/points_balance.png new file mode 100644 index 00000000..6614c9bb Binary files /dev/null and b/app/assets/points_balance.png differ diff --git a/app/assets/pump_grey.png b/app/assets/pump_grey.png new file mode 100644 index 00000000..5f18f916 Binary files /dev/null and b/app/assets/pump_grey.png differ diff --git a/app/assets/pump_loader.png b/app/assets/pump_loader.png new file mode 100644 index 00000000..4bc7e512 Binary files /dev/null and b/app/assets/pump_loader.png differ diff --git a/app/assets/pump_logo.png b/app/assets/pump_logo.png new file mode 100644 index 00000000..0e0f2b57 Binary files /dev/null and b/app/assets/pump_logo.png differ diff --git a/app/assets/pump_plain.png b/app/assets/pump_plain.png new file mode 100644 index 00000000..eee23318 Binary files /dev/null and b/app/assets/pump_plain.png differ diff --git a/app/assets/stp_card_jcb.png b/app/assets/stp_card_jcb.png new file mode 100644 index 00000000..f9746682 Binary files /dev/null and b/app/assets/stp_card_jcb.png differ diff --git a/app/assets/stp_card_mastercard.png b/app/assets/stp_card_mastercard.png new file mode 100644 index 00000000..9022725e Binary files /dev/null and b/app/assets/stp_card_mastercard.png differ diff --git a/app/assets/stp_card_unknown.png b/app/assets/stp_card_unknown.png new file mode 100644 index 00000000..59def1fd Binary files /dev/null and b/app/assets/stp_card_unknown.png differ diff --git a/app/assets/stp_card_unlabeled.png b/app/assets/stp_card_unlabeled.png new file mode 100644 index 00000000..2a076e6a Binary files /dev/null and b/app/assets/stp_card_unlabeled.png differ diff --git a/app/assets/stp_card_visa.png b/app/assets/stp_card_visa.png new file mode 100644 index 00000000..07bf33af Binary files /dev/null and b/app/assets/stp_card_visa.png differ diff --git a/app/assets/stp_close_form.png b/app/assets/stp_close_form.png new file mode 100644 index 00000000..9da57921 Binary files /dev/null and b/app/assets/stp_close_form.png differ diff --git a/app/assets/success_icon.png b/app/assets/success_icon.png new file mode 100644 index 00000000..2af1b3a1 Binary files /dev/null and b/app/assets/success_icon.png differ diff --git a/app/assets/wazeIcon.png b/app/assets/wazeIcon.png new file mode 100644 index 00000000..e02c212d Binary files /dev/null and b/app/assets/wazeIcon.png differ diff --git a/app/components/api/endpoints.js b/app/components/api/endpoints.js new file mode 100644 index 00000000..db1924a5 --- /dev/null +++ b/app/components/api/endpoints.js @@ -0,0 +1,188 @@ +// const api_url = "https://mobileapid.unioil.com/api/mobile/"; +// export const api_url = "https://mobileapi.unioilapps.com/api/mobile/"; +// export const api_url = "https://unioilapi.lbteksystems.com/api/mobile/"; +export const api_url = "https://stag-mobapi.unioilapps.com/api/mobile/"; // staging +// export const api_url = "https://mobilerest.unioilapps.com/api/mobile/"; // prod +// const api_url = "https://unioilapi.taxikel.com/api/mobile/" + +const notifications = "https://stag-notifapi.unioilapps.com/api/notification/mobile/all"; //staging +const account_fcm = "https://stag-notifapi.unioilapps.com/api/fcmtoken/notifs"; //staging +const fcm_register = "https://stag-notifapi.unioilapps.com/api/fcmtoken"; //staging +// const notifications = "https://notification.unioilapps.com/api/notification/mobile/all"; //prod +// const account_fcm = "https://notification.unioilapps.com/api/fcmtoken/notifs"; //prod +// const fcm_register = "https://notification.unioilapps.com/api/fcmtoken"; //prod +// const notifications = "https://unioil-notif-api.herokuapp.com/api/notification/mobile/all"; +// const account_fcm = "https://unioil-notif-api.herokuapp.com/api/fcmtoken/notifs"; +// const fcm_register = "https://unioil-notif-api.herokuapp.com/api/fcmtoken"; +// const fcm_regoster = "https://mhcuuigdea.execute-api.us-east-1.amazonaws.com/dev/api/fcmtoken"; + +// const post_pay = "https://partnerapi-lab.petrozone.com/v5/"; +// const post_pay = "https://partnerapi-au-uat.petrozone.com/v5/"; +const post_pay = "https://partnerapi-int.petrozone.com/v5/"; + +const card_validation = api_url + "loginCardNumber"; +const card_validation_enroll = api_url + "loginCardNumberEnroll"; +const login = api_url + "loginBirthdate"; +const login_mobile = api_url + "loginMobileNumber"; +const security_question = api_url + "securityQuestion"; +const setup_mpin = api_url + "mpinUpdate"; +const requestOTP = api_url + "requestOtp"; +const sendOTP = api_url + "sendOTP"; +const mobileOTP = api_url + "mobileOtp"; +const registerAndValidate = api_url + "registerAndValidate"; +const validateMobileOTP = api_url + "validateMobileOTP"; +const submitRegistration = api_url + "submitRegistration"; +const validateOTP = api_url + "validateOtp"; +const terms_and_privacy = api_url + "termsAndPrivacy"; +const contact_us = api_url + "contactus"; + +const user_profile = api_url + "userProfile"; +const send_card_pin = api_url + "validatePin"; +const activateCard = api_url + "activateCard"; +const apply = api_url + "signUp"; +const signup_id_number = api_url + "signUpIDNumber"; + +const get_card_prompt_info = api_url + "systemPreference/information_guide_details"; + +const gas_stations = api_url + "stationNearby"; +const gas_stations_city = api_url + "stationViaCity"; +const gas_station_details = api_url + "stationDetails"; +const gas_station_fuel = api_url + "stationfuels"; + +const loyalty_cards = api_url + "getcardtype"; +const products = api_url + "products"; +const cities = api_url + "cityList"; +const station_search = api_url + "stationSearch"; + +const whats_hot = api_url + "whatshot"; +const new_promos = api_url + "getpromo"; +const promo_gps = api_url + "promotionsGPS"; +const promos = api_url + "promotions"; + +const update_profile_no_otp = api_url + "editProfile"; +const update_profile_with_otp = api_url + "editProfileOTP"; + +const shared_treats = api_url + "sharedtreats"; +const logout = api_url + "logout"; + +const station_favorites = api_url + "stationFavorites"; +const station_add_favorite = api_url + "stationSetFavorite"; +const station_delete_favorite = api_url + "stationDeleteFavorite"; + +const transactions = api_url + "transactions"; +const transaction_rate = api_url + "rateTransaction"; +const transaction_single = api_url + "singleTransaction"; + +const topup_paypal = api_url + "paypalURL"; +const topup = api_url + "paymaya_execute"; +const topup_paypal_execute = api_url + "transactionStatus"; +const topup_transaction_entry = api_url + "transactionEntry"; + +// const fuel_types = api_url + "fuelList"; +const fuel_types = api_url + "fuels"; + +const validate_mobile = api_url + "checkMobile"; +const paymaya_tokens = api_url + "paymayatokens"; + +const getAppVersion = api_url + "getVersion/ios"; + + +// P97 API +const post_pay_api_key = "BZ3SGKGaGPNb9PtIoALywvizQZC26qgSFx3Ac9zc3yNQKw79CI5mabma3eXcBzo5IfUKy84JgIj"; +const getStores = post_pay + "stores"; +const getStoreDetails = post_pay + "stores/details"; +const payOutsideStore = post_pay + "pay/outside/store"; +const getTransactionDetails = post_pay + "transactions"; +const postClaim = post_pay + "postpay/claim"; +const postpay = post_pay + "postpay"; +const getWalletPublicKey = post_pay + "wallet/register/publickey"; +const addCreditCard = post_pay + "wallet/register"; +const deleteCreditCard = post_pay + "wallet/unregister"; +const getFunding = post_pay + "wallet/funding"; + +export default { + + server: api_url, + + card_validation, + card_validation_enroll, + login, + login_mobile, + security_question, + setup_mpin, + requestOTP, + sendOTP, + terms_and_privacy, + contact_us, + + user_profile, + send_card_pin, + activateCard, + apply, + signup_id_number, + + get_card_prompt_info, + + gas_stations, + gas_stations_city, + gas_station_details, + gas_station_fuel, + + loyalty_cards, + products, + cities, + station_search, + + whats_hot, + new_promos, + promo_gps, + promos, + + update_profile_no_otp, + update_profile_with_otp, + + shared_treats, + logout, + + station_favorites, + station_add_favorite, + station_delete_favorite, + + transactions, + transaction_rate, + transaction_single, + + topup_paypal, + topup_paypal_execute, + topup_transaction_entry, + fuel_types, + topup, + + validate_mobile, + + notifications, + account_fcm, + fcm_register, + + paymaya_tokens, + + mobileOTP, + registerAndValidate, + validateMobileOTP, + submitRegistration, + validateOTP, + + getAppVersion, + + post_pay_api_key, + getStores, + getStoreDetails, + payOutsideStore, + getTransactionDetails, + postClaim, + postpay, + getFunding, + getWalletPublicKey, + addCreditCard, + deleteCreditCard +} \ No newline at end of file diff --git a/app/components/api/file.manager.js b/app/components/api/file.manager.js new file mode 100644 index 00000000..5f8cd53d --- /dev/null +++ b/app/components/api/file.manager.js @@ -0,0 +1,30 @@ +import React from 'react' +import EP from './endpoints'; +import RNFetchBlob from 'rn-fetch-blob' + +export default async function API(endpoint, method, headers, params, body, onSuccess, onError) { + RNFetchBlob.fetch(method, endpoint.includes(":") ? endpoint : EP[endpoint], { + Authorization : headers.Authorization, + 'Content-Type' : 'multipart/form-data', + }, [ + // { name : 'file', filename : 'photo.png', type: body.image.type, data: RNFetchBlob.wrap(decodeURIComponent(body.image.uri.replace("file:///", "")))}, + { name : 'photo', filename : 'photo.png', type: body.image.type, data: body.image.data}, + { name : 'firstname', data : body.firstname}, + { name : 'middlename', data : body.middlename}, + { name : 'lastname', data : body.lastname}, + { name : 'birthdate', data : body.birthdate}, + { name : 'mobile', data : body.mobile}, + { name : 'email', data : body.email}, + { name : 'address', data : body.address}, + // { name : 'city', data : body.city}, + { name : 'vo_code', data : body.vo_code}, + { name : 'fueltype_code', data : body.fueltype_code}, + { name : 'civilstatus_code', data : body.civilstatus_code}, + { name : 'gender_code', data : body.gender_code}, + { name : 'is_deleted', data : body.is_deleted} + ]).then((resp) => { + onSuccess(resp.json()) + }).catch((err) => { + onError(err) + }) +} \ No newline at end of file diff --git a/app/components/api/index.js b/app/components/api/index.js new file mode 100644 index 00000000..f570710a --- /dev/null +++ b/app/components/api/index.js @@ -0,0 +1,99 @@ +import * as React from 'react'; +import EP from './endpoints.js'; + +const defaultHeaders = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', +} + +const Options = (method, headers, params, body) => { + let obj = {} + if(method == "post"){ + obj = {method: method, body: JSON.stringify(body), headers: headers || defaultHeaders, params: params} + }else if(method == "get"){ + obj = {method: method, headers: headers || defaultHeaders, params: params} + } + return obj +} + +export default async function API(endpoint, method, headers, params, body, onSuccess, onError) { + try { + if(method == "post"){ + let Head = { + 'Accept': headers.Accept ? headers.Accept : 'application/json', + 'Content-Type': headers['Content-Type'] ? headers['Content-Type'] : 'application/json', + 'Authorization': headers.Authorization || '', + 'card_number': headers.card_number || '' + } + let response = await fetch(endpoint.includes(":") ? endpoint : EP[endpoint], { + method: method, + body: body?._parts ? body : JSON.stringify(body), + headers: Head, + params: params + }); + let json = await response.json(); + onSuccess(json) + return json + }else if(method == "get"){ + let url = !params.noID ? (EP[endpoint] + "?" + params) : EP[endpoint] + "/" + params.value + console.log("URL", url) + if(headers.Authorization){ + let response = await fetch(endpoint.includes(":") ? endpoint : url, { + method: method, + headers: new Headers({ + 'Accept': 'application/json', + 'Authorization': `${headers.Authorization || ''}`, + 'card_number': `${headers.card_number}` || '' + }) + }); + let json = await response.json(); + onSuccess(json) + return json + }else{ + let response = await fetch(endpoint.includes(":") ? endpoint : url); + let json = await response.json(); + onSuccess(json) + return json + } + }else if(method == "delete"){ + let url = !params.noID ? (EP[endpoint] + "?" + params) : EP[endpoint] + "/" + params.value + console.warn('URL delete', url); + if(headers.Authorization){ + let response = await fetch(endpoint.includes(":") ? endpoint : url, { + method: method, + headers: new Headers({ + 'Accept': 'application/json', + 'Authorization': `${headers.Authorization || ''}`, + 'card_number': `${headers.card_number}` || '' + }) + }); + let json = await response.json(); + onSuccess(json) + return json + }else{ + let response = await fetch(endpoint.includes(":") ? endpoint : url); + console.warn('Response', JSON.stringify(res)) + let json = await response.json(); + onSuccess(json) + return json + } + }else if(method == "put") { + let Head = { + 'Content-Type': 'application/json' + } + let response = await fetch(endpoint.includes(":") ? endpoint : EP[endpoint], { + method: method, + body: body?._parts ? body : JSON.stringify(body), + headers: Head, + params: params + }); + let json = await response.json(); + onSuccess(json) + return json + } + + } catch (error) { + onError(error) + return error + } +} \ No newline at end of file diff --git a/app/components/api/mobile.js b/app/components/api/mobile.js new file mode 100644 index 00000000..e2242742 --- /dev/null +++ b/app/components/api/mobile.js @@ -0,0 +1,13 @@ +import * as React from 'react'; +import EP from './endpoints'; + +export default function ValidateMobile(mobile) { + return fetch(EP.validate_mobile + "/" + mobile) + .then((response) => response.json()) + .then((json) => { + return json + }) + .catch((error) => { + return error + }); +} \ No newline at end of file diff --git a/app/components/api/postpayapi.js b/app/components/api/postpayapi.js new file mode 100644 index 00000000..148c7779 --- /dev/null +++ b/app/components/api/postpayapi.js @@ -0,0 +1,73 @@ +import EP from './endpoints.js'; + +export default async function API(endpoint, method, headers, params, body, onSuccess, onError) { + try { + if(method == "post") { + var Head = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'x-p97-tenantid': '8ba3cd89-74be-4a7a-91a5-5eb59a2040b8', + 'x-p97-apikey': EP.post_pay_api_key + } + let url = endpoint.includes(":") ? endpoint : EP[endpoint] + if(headers.token != undefined) { + Head = {...Head, 'Authorization': `Bearer ${headers.token}`} + } + if(headers.language != undefined) { + Head = {...Head, 'Accept-Language': headers.language} + } + console.log(url, Head) + let response = await fetch(url, { + method: method, + body: body?._parts ? body : JSON.stringify(body), + headers: Head, + params: params + }); + let json = await response.json(); + onSuccess(json) + } else if(method == "get") { + var Head = { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'x-p97-tenantid': '8ba3cd89-74be-4a7a-91a5-5eb59a2040b8', + 'x-p97-apikey': EP.post_pay_api_key, + } + let paramsItem = Object.entries(params).map(([key, value]) => `${key}=${value}`) + let url = paramsItem.length > 1 ? `${EP[endpoint]}?${paramsItem.join('&').toString()}` : `${EP[endpoint]}/${Object.entries(params).map(([key, value]) => value).join('').toString()}` + if(headers.token != undefined) { + Head = {...Head, 'Authorization': `Bearer ${headers.token}`} + } + if(headers.language != undefined) { + Head = {...Head, 'Accept-Language': headers.language} + } + console.log(url, Head) + let response = await fetch(endpoint.includes(":") ? endpoint : url, { + method: method, + headers: Head + }); + let json = await response.json(); + onSuccess(json) + } else if(method == "delete") { + let url = !params.noID ? (EP[endpoint] + "?" + params) : EP[endpoint] + "/" + params.value + console.warn('URL delete', url); + if(headers.Authorization){ + let response = await fetch(endpoint.includes(":") ? endpoint : url, { + method: method, + headers: new Headers({ + 'Accept': 'application/json', + 'Authorization': `${headers.Authorization || ''}` + }) + }); + let json = await response.json(); + onSuccess(json) + } else { + let response = await fetch(endpoint.includes(":") ? endpoint : url); + console.warn('Response', JSON.stringify(res)) + let json = await response.json(); + onSuccess(json) + } + } + } catch (error) { + onError(error) + } +} \ No newline at end of file diff --git a/app/components/assets.manager.js b/app/components/assets.manager.js new file mode 100644 index 00000000..c4add19f --- /dev/null +++ b/app/components/assets.manager.js @@ -0,0 +1,245 @@ +import {Platform} from 'react-native'; + +const icons = { + about: require("../assets/ic_about.png"), + account: require("../assets/ic_account.png"), + add: require("../assets/ic_add_white_24dp.png"), + card: require("../assets/ic_card.png"), + down: require("../assets/ic_chevron_down.png"), + close: require("../assets/ic_close_white_24dp.png"), + contact: require("../assets/ic_contact.png"), + dashboard: require("../assets/ic_dashboard.png"), + favEmpty: require("../assets/ic_fav_empty.png"), + favFull: require("../assets/ic_fav_full.png"), + fuelTab: require("../assets/ic_fuel_tab.png"), + fuel: require("../assets/ic_fuel.png"), + history: require("../assets/ic_history.png"), + info: require("../assets/ic_info.png"), + kmLiter: require("../assets/ic_km_liter.png"), + logout: require("../assets/ic_logout.png"), + mylocation: require("../assets/ic_my_location_black_24dp.png"), + peso: require("../assets/ic_peso.png"), + products: require("../assets/ic_products.png"), + promos: require("../assets/ic_promos.png"), + reminders: require("../assets/ic_reminder.png"), + rewards: require("../assets/ic_rewards.png"), + sheduler: require("../assets/ic_scheduler.png"), + search: require("../assets/ic_search_white_24dp.png"), + selectedStationPin: require("../assets/ic_selected_station_pin.png"), + // stationMapPin: Platform.OS == 'ios' ? require("../assets/ic_station_map_pin_lg.png") : require("../assets/ic_station_map_pin.png"), + stationMapPin: require("../assets/ic_station_map_pin.png"), + station: require("../assets/ic_station.png"), + stations: require("../assets/ic_stations.png"), + topup: require("../assets/ic_topup.png"), + tutorial: require("../assets/ic_tutorial.png"), + type: require("../assets/ic_type.png"), + + googleMaps: require("../assets/icon_googlemaps.png"), + internet: require("../assets/icon_internet.png"), + unioil: require("../assets/icon_unioil.png"), + waze: require("../assets/icon_waze.png"), + + activehome: require("../assets/ic_home.png"), + activepromos: require("../assets/ic_promos_active.png"), + activestation: require("../assets/ic_stations_active.png"), + activerewards: require("../assets/ic_rewards_active.png"), + + inactivehome: require("../assets/ic_home_inactive.png"), + inactivepromos: require("../assets/ic_promos.png"), + inactivestation: require("../assets/ic_stations.png"), + inactiverewards: require("../assets/ic_rewards.png"), + + activeaccount: require("../assets/ic_account_active.png"), + activecard: require("../assets/ic_card_active.png"), + activehistory: require("../assets/ic_history_active.png"), + + inactiveaccount: require("../assets/ic_account_inactive2.png"), + inactivecard: require("../assets/ic_card_inactive2.png"), + + activepayatpump: require("../assets/ic_payatpump.png"), + + stpcloseform: require("../assets/stp_close_form.png"), + stpunknown: require("../assets/stp_card_unknown.png"), + stpjcb: require("../assets/stp_card_jcb.png"), + stpvisa: require("../assets/stp_card_visa.png"), + stpmastercard: require("../assets/stp_card_mastercard.png"), + stpunlabeled: require("../assets/stp_card_unlabeled.png"), + + philippine_pesos: require("../assets/philippine_peso.png"), + points_balance: require("../assets/points_balance.png"), + + successmpinupdate: require("../assets/success_icon.png"), + no_connection: require('../assets/empty_state_no_internet.png'), + animated_loading: require("../assets/loading.gif") +} + +const cards = { + classic: require("../assets/bg_classic.png"), + grab: require("../assets/bg_grab.png"), + ica: require("../assets/bg_ica.png"), + xavier: require("../assets/bg_xavier.png") +} + +const pumps = { + plain_pump: require("../assets/pump_plain.png"), + plain_pump_grey: require("../assets/pump_grey.png"), + pump_logo: require("../assets/pump_logo.png"), + pump_loader: require("../assets/pump_loader.png") +} + +const bg = { + fuelEfficiency: require("../assets/bg_fuel_efficiency.png"), + scheduler: require("../assets/bg_scheduler.png"), + about: require("../assets/img_bg_about.png"), + loginBg: require('../assets/login_background.jpg'), + login_bg: require('../assets/login_bg.jpg') +} + +const boarding = [ + require("../assets/onboarding_01.png"), + require("../assets/onboarding_02.png"), + require("../assets/onboarding_03.png"), + require("../assets/onboarding_04.png") +] + +const logo = { + unioil: require("../assets/logo_unioil_new.png"), + reverse: require("../assets/logo_unioil_reverse.png"), + profileHolder: require("../assets/place_holder_profile_pic.png"), + icon: require('../assets/ic_launcher.png'), + iconWhite: require('../assets/ic_launcher_foreground.png'), + round: require('../assets/ic_launcher_round.png'), + profileHolderString: '../assets/place_holder_profile_pic.png' +} + +const baseline = { + callBlack: require("../assets/baseline_call_black_24.png"), + deleteWhite: require("../assets/baseline_delete_white_24.png"), + editWhite: require("../assets/baseline_edit_white_24.png"), + mailBlack: require("../assets/baseline_email_black_24.png"), + infoBlack: require("../assets/baseline_info_black_18.png"), + keyboardLeft: require("../assets/baseline_keyboard_arrow_left_black_48.png"), + keyboardRight: require("../assets/baseline_keyboard_arrow_right_black_48.png"), +} + +const directionIcon = { + wazeIcon: require("../assets/wazeIcon.png"), + googleMapsIcon: require("../assets/googleMapsIcon.png"), + mapsIcon: require("../assets/mapsIcon.png") +} + +const guidecard = require("../assets/guide_card.png") + +const test = { + dixie: [ + { + uri: "https://3.bp.blogspot.com/-G1qbDEhxq1s/XMvr8wvTevI/AAAAAAAAOk0/RXfFIBKhXpQS-LtXxa9fU58MSn501IY8gCEwYBhgL/s1600/52563319_308667079841654_99526654379950080_n.jpg" + },{ + uri: "https://i.pinimg.com/236x/7a/19/27/7a1927f35e9c8a97d129b68ff9177c3c.jpg" + },{ + uri: "https://www.famousbirthdays.com/headshots/dexie-diaz-3.jpg" + },{ + uri: "https://i.pinimg.com/originals/8b/6d/93/8b6d933fed4733cc7f461dbde449c60a.jpg" + },{ + uri: "https://images.networthlist.org/imgs/7/78/dexie-diaz.jpg" + },{ + uri: "https://i.pinimg.com/originals/9b/2e/1f/9b2e1f385ddc4996a1d08cbebe36ee31.jpg" + },{ + uri: "https://i.pinimg.com/originals/9b/ba/ce/9bbace72ca15124c4a88df8d7509c07b.jpg" + }], + zuvapit: [{ + uri: "https://i.pinimg.com/originals/d8/a7/b9/d8a7b9a2e78879782f7e8791c5036f41.jpg" + },{ + uri: "https://i.pinimg.com/originals/84/a9/9e/84a99e02d1cbb758335240a0ff0014d3.jpg" + },{ + uri: "https://pic.kissgoddess.com/girl/24177/24177.jpg" + } + ], + mina: [{ + uri: 'https://i.redd.it/4abcufq84ep01.jpg' + },{ + uri: 'https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTDtLc7hLmie9EP5KENw-PVPcwFPyjoNTGr7ZsrEQVt1WyfUCcN&usqp=CAU' + },{ + uri: 'https://lh3.googleusercontent.com/CoCRup6zxPpqhP_wjwSe4-nbAyLwl4NQF2Cd3eEGcVkzuHDMW3vBYqHh0Cs2tPc72OCOUPwlC69xGP9rZKui4ZPCuPCvypUYOfo=w1200-h630-rj-pp' + },{ + uri: 'https://c1.staticflickr.com/1/515/18524221670_2bc8c35ff7_o.jpg' + }, + ] +} + +const dixie = require("../assets/dexie.jpg") + +const fueltypes = [{ + value: "1", + string: "Euro 5 GASOLINE 97" + },{ + value: "2", + string: "Euro 5 GASOLINE 95" + },{ + value: "3", + string: "Euro 5 GASOLINE 91" + },{ + value: "4", + string: "Euro 5 Diesel" + },{ + value: "0", + string: "" +}] + +const vehicletypes = [{ + value: "1", + string: "4 Wheeler" + },{ + value: "2", + string: "2 Wheeler" + },{ + value: "0", + string: "" +}] + +const civilstatus = [{ + value: "S", + string: "Single" + },{ + value: "M", + string: "Married" + },{ + value: "0", + string: "No Data Entered" + },{ + value: "W", + string: "Widow" + },{ + value: "SE", + string: "Separated" +}] + +const gender = [{ + value: "M", + string: "Male" + },{ + value: "F", + string: "Female" + },{ + value: "0", + string: "No Data Entered" +}] + + +export default { + icons, + cards, + bg, + boarding, + logo, + baseline, + dixie, + test, + fueltypes, + vehicletypes, + civilstatus, + gender, + guidecard, + directionIcon, + pumps +} \ No newline at end of file diff --git a/app/components/cardencrypt/encryptcard.js b/app/components/cardencrypt/encryptcard.js new file mode 100644 index 00000000..dae11dfc --- /dev/null +++ b/app/components/cardencrypt/encryptcard.js @@ -0,0 +1,14 @@ +import forge from 'node-forge'; +import { Buffer } from 'buffer'; + +const encryptCard = async (card, pubkey) => { + var formatpubkey = `-----BEGIN PUBLIC KEY-----\n${pubkey}\n-----END PUBLIC KEY-----` + const publicKey = forge.pki.publicKeyFromPem(formatpubkey) + const cardBuffer = Buffer.from(card) + let encryptedCard = publicKey.encrypt(card, 'RSA-OAEP') + return forge.util.encode64(encryptedCard) +} + +export default { + encryptCard +} \ No newline at end of file diff --git a/app/components/carousel/board.js b/app/components/carousel/board.js new file mode 100644 index 00000000..6df2b3b2 --- /dev/null +++ b/app/components/carousel/board.js @@ -0,0 +1,85 @@ +import React from 'react' +import { View, ScrollView, Text, StyleSheet, Image, ImageBackground, TouchableOpacity, Platform } from 'react-native' +import Theme from '../theme.style.js'; + +export default function BoardCarousel(props) { + return ( + + {props.items.map((item, index) => { + return ( + + + {item.title} + {item.subtitle} + + + ) + })} + + + {props.bullets} + + + + GET STARTED + + ) +} + +const styles = StyleSheet.create({ + statsHead: { + paddingTop: 10, + paddingHorizontal: 12, + }, + container: { + height: '100%', + width: '100%', + backgroundColor: '#fbfbfb', + shadowColor: '#fcfcfc', + shadowOpacity: 1, + shadowOffset: { + width: 0, + height: 5 + }, + }, + scrollView: { + display: 'flex', + flexDirection: 'row', + overflow: 'hidden', + width: '100%', + height: '100%' + }, + bullets: { + position: 'absolute', + bottom: Platform.OS == 'ios' ? 35 : 0, + display: 'flex', + justifyContent: 'flex-start', + flexDirection: 'row', + paddingHorizontal: 10, + paddingTop: 5, + }, + button: { + position: 'absolute', + bottom: Platform.OS == 'ios' ? 50 : 20, + right: 35, + display: 'flex', + justifyContent: 'flex-end', + flexDirection: 'row', + paddingHorizontal: 10, + paddingTop: 5, + }, + bullet: { + paddingHorizontal: 3, + alignSelf: 'center', + left: 20 + } +}); \ No newline at end of file diff --git a/app/components/carousel/index.js b/app/components/carousel/index.js new file mode 100644 index 00000000..4dfb658a --- /dev/null +++ b/app/components/carousel/index.js @@ -0,0 +1,171 @@ +import React from 'react' +import { View, ScrollView, Text, StyleSheet, Image, ImageBackground, TouchableOpacity } from 'react-native' +import Theme from '../theme.style.js'; +import Board from './board.js'; +import Promo from './promo.js'; + +export const Carousel = (props) => { + + const { items, style } = props; + const itemsPerInterval = props.itemsPerInterval === undefined ? 1 : props.itemsPerInterval; + const [interval, setInterval] = React.useState(1); + const [intervals, setIntervals] = React.useState(1); + const [width, setWidth] = React.useState(0); + + const init = (width) => { + // initialise width + setWidth(width); + // initialise total intervals + const totalItems = items.length; + setIntervals(Math.ceil(totalItems / itemsPerInterval)); + + } + + const getInterval = (offset) => { + for (let i = 1; i <= intervals; i++) { + if (offset < (width / intervals) * i) { + return i; + } + if (i == intervals) { + return i; + } + } + } + + let bullets = []; + for (let i = 1; i <= intervals; i++) { + + if(props.type == "board"){ + bullets.push( + + • + + ); + }else if(props.type == "promo"){ + bullets.push( + + • + + ); + } + + } + + return ( + + {props.type == "board" ? + init(w)} + onScroll={data => { + setWidth(data.nativeEvent.contentSize.width); + setInterval(getInterval(data.nativeEvent.contentOffset.x)); + }} + items={items} + onPress={props.onPress} + bullets={bullets} + /> : + init(w)} + onScroll={data => { + setWidth(data.nativeEvent.contentSize.width); + setInterval(getInterval(data.nativeEvent.contentOffset.x)); + }} + items={items} + onPress={props.onPress} + bullets={bullets} + /> } + + ) +} + +const styles = StyleSheet.create({ + statsHead: { + paddingTop: 10, + paddingHorizontal: 12, + }, + container: { + height: '100%', + width: '100%', + backgroundColor: '#fbfbfb', + shadowColor: '#fcfcfc', + shadowOpacity: 1, + shadowOffset: { + width: 0, + height: 5 + }, + }, + scrollView: { + display: 'flex', + flexDirection: 'row', + overflow: 'hidden', + width: '100%', + height: '100%' + }, + bullets: { + position: 'absolute', + bottom: 0, + display: 'flex', + justifyContent: 'flex-start', + flexDirection: 'row', + paddingHorizontal: 10, + paddingTop: 5, + }, + button: { + position: 'absolute', + bottom: 20, + right: 35, + display: 'flex', + justifyContent: 'flex-end', + flexDirection: 'row', + paddingHorizontal: 10, + paddingTop: 5, + }, + bullet: { + paddingHorizontal: 3, + alignSelf: 'center', + left: 20 + }, + bullet2: { + paddingHorizontal: 5, + alignSelf: 'center', + } +}); + +export default Carousel; + + // switch (style) { + // case 'stats': + // return ( + // + // ); + // default: + // return ( + // + // ); + // } \ No newline at end of file diff --git a/app/components/carousel/promo.js b/app/components/carousel/promo.js new file mode 100644 index 00000000..e2b88166 --- /dev/null +++ b/app/components/carousel/promo.js @@ -0,0 +1,79 @@ +import React from 'react' +import { View, ScrollView, Text, StyleSheet, Image, ImageBackground, TouchableOpacity } from 'react-native' +import Theme from '../theme.style.js'; + +export default function BoardCarousel(props) { + + const [scrollView, setscrollView] = React.useState(null); + let interval; + + React.useEffect(() => { + // setInterval(function(){ + // // if(scrollView) scrollView.scrollTo({x: 0, y: `${100 * props.intervals}%`, animated: true}) + // if(scrollView) scrollView.scrollTo({x: 0, y: 100, animated: true}) + // // console.log(scrollView.isTouching) + // }, 3000) + // return () => { + // clearInterval(interval) + // } + }, []) + + return ( + + setscrollView(el)} + horizontal={true} + contentContainerStyle={{ ...styles.scrollView, width: `${100 * props.intervals}%` }} + showsHorizontalScrollIndicator={false} + onContentSizeChange={props.onContentSizeChange} + onScroll={props.onScroll} + scrollEventThrottle={200} + pagingEnabled + decelerationRate="fast" + > + {props.items.map((item, index) => { + return ( + + + + ) + })} + + + {props.bullets} + + ) +} + +const styles = StyleSheet.create({ + statsHead: { + paddingTop: 10, + paddingHorizontal: 12, + }, + container: { + height: '100%', + width: '100%', + backgroundColor: '#fbfbfb', + shadowColor: '#fcfcfc', + shadowOpacity: 1, + shadowOffset: { + width: 0, + height: 5 + }, + }, + scrollView: { + display: 'flex', + flexDirection: 'row', + overflow: 'hidden', + width: '100%', + height: '100%' + }, + bullets: { + flex: 1, + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + padding: 10, + paddingTop: 20, + } +}); \ No newline at end of file diff --git a/app/components/contact.action.js b/app/components/contact.action.js new file mode 100644 index 00000000..ab16f284 --- /dev/null +++ b/app/components/contact.action.js @@ -0,0 +1,133 @@ +import * as React from 'react'; +import {Linking, Platform} from 'react-native'; +import {ActionSheet} from 'native-base'; +import REQUEST from './api/'; +import { openComposer } from 'react-native-email-link'; + +export const ContactOptions = () => { + let options = [ + {text: 'Call Customer Service', icon: 'call-outline', execute: async () => { + + await REQUEST("contact_us", "get", {}, {}, {}, async (res) => { + if(res.status == 1 && res.data){ + let url = Platform.OS == 'ios' ? 'telprompt:' : 'tel:' + Linking.canOpenURL(url).then(supported => { + if (!supported) { + console.log('Cant handle url') + alert("Call Not Supported") + } else { + return Linking.openURL(`${url}${res.data.contact_number_mobile}`) + } + }).catch(err => { + console.error('An error occurred', err) + }) + }else{ + console.log(res.message, res.data) + } + }, function(error){ + console.log(error) + }) + + + }}, + {text: 'Email Customer Service', icon: 'mail-outline', execute: async () => { + + await REQUEST("contact_us", "get", {}, {}, {}, async (res) => { + if(res.status == 1 && res.data){ + + openComposer({ + to: res.data.contact_email_address_mobile, + subject: `CN`, + body: '' + }) + + }else{ + console.log(res.message, res.data) + } + }, function(error){ + console.log(error) + }) + }}, + {text: 'Cancel', execute: () => ActionSheet.hide()} + ] + + ActionSheet.show( + { + options: options, + title: "Select an option", + destructiveButtonIndex: 2, + cancelButtonIndex: 2 + }, + buttonIndex => { + options[buttonIndex].execute() + } + ) + +} + +export const ContactOptionsWithParams = (params) => { + let options = [ + {text: 'Call Customer Service', icon: 'call-outline', execute: async () => { + + await REQUEST("contact_us", "get", {}, {}, {}, async (res) => { + if(res.status == 1 && res.data){ + let url = Platform.OS == 'ios' ? 'telprompt:' : 'tel:' + Linking.canOpenURL(url).then(supported => { + if (!supported) { + console.log('Cant handle url') + alert("Call Not Supported") + } else { + return Linking.openURL(`${url}${res.data.contact_number_mobile}`) + } + }).catch(err => { + console.error('An error occurred', err) + }) + }else{ + console.log(res.message, res.data) + } + }, function(error){ + console.log(error) + }) + + + }}, + {text: 'Email Customer Service', icon: 'mail-outline', execute: async () => { + + await REQUEST("contact_us", "get", {}, {}, {}, async (res) => { + if(res.status == 1 && res.data){ + + openComposer({ + to: res.data.contact_email_address_mobile, + subject: `Mobile App Feedback: ${(params == undefined && params.cardnumber == undefined) ? "" : "CN"+params.cardnumber}`, + body: '' + }) + + }else{ + console.log(res.message, res.data) + } + }, function(error){ + console.log(error) + }) + + }}, + + {text: 'Cancel', execute: () => ActionSheet.hide()} + ] + + ActionSheet.show( + { + options: options, + title: "Select an option", + destructiveButtonIndex: 2, + cancelButtonIndex: 2 + }, + buttonIndex => { + options[buttonIndex].execute() + } + ) + +} + +export default { + +} \ No newline at end of file diff --git a/app/components/crypto.js b/app/components/crypto.js new file mode 100644 index 00000000..22158e22 --- /dev/null +++ b/app/components/crypto.js @@ -0,0 +1,28 @@ +import CryptoJS from "react-native-crypto-js"; + +const encrypt = (data, hash) => { + var key = CryptoJS.enc.Latin1.parse(`${hash}-loyalty`) + var iv = CryptoJS.enc.Latin1.parse('unioilloyaltyapp') + var encrypted = CryptoJS.AES.encrypt( + data, + key, + {iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7 + }).toString() + return encrypted +} + +const decrypt = (data, hash) => { + var key = CryptoJS.enc.Latin1.parse(`${hash}-loyalty`) + var iv = CryptoJS.enc.Latin1.parse('unioilloyaltyapp') + var decrypted = CryptoJS.AES.decrypt( + data, + key, + {iv:iv,padding:CryptoJS.pad.Pkcs7} + ).toString(CryptoJS.enc.Utf8) + return decrypted +} + +export default { + encrypt, + decrypt +} \ No newline at end of file diff --git a/app/components/custominput.js b/app/components/custominput.js new file mode 100644 index 00000000..5ff4be6d --- /dev/null +++ b/app/components/custominput.js @@ -0,0 +1,167 @@ +import * as React from 'react' +import {useState, useEffect, useRef} from 'react' +import {View, Text, TouchableOpacity, TextInput, StyleSheet, Keyboard} from 'react-native' +import Theme from './theme.style.js' + +export default function CustomInput(props){ + + const [focus, setfocus] = useState(false) + const [activefield, setactivefield] = useState(null) + const [activeInput, setactiveinput] = useState(null) + const [value, setvalue] = useState(null) + const inputs = {} + + let [cn1, setcn1] = useState("") + let [cn2, setcn2] = useState("") + let [cn3, setcn3] = useState("") + let [cn4, setcn4] = useState("") + + let [mobilenumber, setmobilenumber] = useState("+63") + + const send = () => { + props.onChangeText(value) + } + + const onFocus = (key) => { + setactiveinput(key) + setfocus(true) + } + + const onSubmitEditing = (key) => { + inputs[key].focus() + } + + if(props.isMobileNumber) { + return ( + + { + nativeEvent.key === 'Backspace' && mobilenumber.length === 3 + }} + onChangeText={(value) => { + if(value.substring(0, 3) == "+63") { + setmobilenumber(value) + props.onChangeText(value) + } + }} + onSubmitEditing={Keyboard.dismiss} + style={[{...styles.input, borderBottomColor: focus && activeInput == 1 ? Theme.colors.accent : "gray", borderBottomWidth: focus && activeInput == 1 ? 1.5 : 1, textAlign: 'left' }, { color: props.textColor || Theme.colors.black}]} + /> + + ) + } + + return ( + + inputs['field1'] = input} + returnKeyType={ 'next' } + onSubmitEditing={() => { onSubmitEditing('field2'); }} + blurOnSubmit={ false } + keyboardType="numeric" + maxLength={4} + onFocus={() => onFocus(1)} + onKeyPress={({ nativeEvent }) => { + if(cn1.length == 4 && nativeEvent.key != "Backspace") onSubmitEditing("field2") + }} + value={cn1} + onChangeText={(value) => { + setcn1(value) + // setvalue(value + cn2 + cn3 + cn4) + // send() + props.onChangeText(value + cn2 + cn3 + cn4) + if(value.length == 4) onSubmitEditing("field2") + }} + style={[{...styles.input, borderBottomColor: focus && activeInput == 1 ? Theme.colors.accent : "gray", borderBottomWidth: focus && activeInput == 1 ? 1.5 : 1 }, { color: props.textColor || Theme.colors.black}]} + /> + inputs['field2'] = input} + returnKeyType={ 'next' } + onSubmitEditing={() => { onSubmitEditing('field3'); }} + keyboardType="numeric" + maxLength={4} + blurOnSubmit={ false } + onFocus={() => onFocus(2)} + value={cn2} + onKeyPress={({ nativeEvent }) => { + nativeEvent.key === 'Backspace' && cn2 === "" ? onSubmitEditing("field1") : null + if(cn2.length == 4 && nativeEvent.key != "Backspace") onSubmitEditing("field3") + }} + onChangeText={(value) => { + setcn2(value) + // setvalue(cn1 + value + cn3 + cn4) + // send() + props.onChangeText(cn1 + value + cn3 + cn4) + if(value.length == 4) onSubmitEditing("field3") + else if(value == "" || value == "" && cn2 == "") onSubmitEditing("field1") + }} + style={[{...styles.input, borderBottomColor: focus && activeInput == 2 ? Theme.colors.accent : "gray", borderBottomWidth: focus && activeInput == 2 ? 1.5 : 1 }, { color: props.textColor || Theme.colors.black}]} + /> + inputs['field3'] = input} + returnKeyType={ 'next' } + onSubmitEditing={() => { onSubmitEditing('field4'); }} + keyboardType="numeric" + maxLength={4} + blurOnSubmit={ false } + onFocus={() => onFocus(3)} + value={cn3} + onKeyPress={({ nativeEvent }) => { + nativeEvent.key === 'Backspace' && cn3 === "" ? onSubmitEditing("field2") : null + if(cn3.length == 4 && nativeEvent.key != "Backspace") onSubmitEditing("field4") + }} + onChangeText={(value) => { + setcn3(value) + // setvalue(cn1 + cn2 + value + cn4) + // send() + props.onChangeText(cn1 + cn2 + value + cn4) + if(value.length == 4) onSubmitEditing("field4") + else if(value == "") onSubmitEditing("field2") + }} + style={[{...styles.input, borderBottomColor: focus && activeInput == 3 ? Theme.colors.accent : "gray", borderBottomWidth: focus && activeInput == 3 ? 1.5 : 1 }, { color: props.textColor || Theme.colors.black}]} + /> + inputs['field4'] = input} + keyboardType="numeric" + maxLength={4} + blurOnSubmit={ false } + onFocus={() => onFocus(4)} + value={cn4} + onKeyPress={({ nativeEvent }) => { + nativeEvent.key === 'Backspace' && cn4 === "" ? onSubmitEditing("field3") : null + }} + onChangeText={(val) => { + setcn4(val) + // setvalue(cn1 + cn2 + cn3 + cn4) + // send() + props.onChangeText(cn1 + cn2 + cn3 + val) + if(val == "") onSubmitEditing("field3") + if(val.length == 4){ + // setvalue(value + val) + // alert(value + " / " + val) + // alert(value) + // console.log(value) + // props.onDone(value + val) + } + // else{ + // setvalue(cn1 + cn2 + cn3 + cn4) + // } + // if(value.length == 4) props.onDone() + }} + style={[{...styles.input, borderBottomColor: focus && activeInput == 4 ? Theme.colors.accent : "gray", borderBottomWidth: focus && activeInput == 4 ? 1.5 : 1 }, { color: props.textColor || Theme.colors.black}]} + /> + + ) +} + +const styles = StyleSheet.create({ + input: { + flex: 1, padding: 0, margin: 5, fontSize: 18, textAlign: 'center', borderBottomColor: Theme.colors.accent, borderBottomWidth: 1.5, color: Theme.colors.white + } + }); \ No newline at end of file diff --git a/app/components/drawer.js b/app/components/drawer.js new file mode 100644 index 00000000..e1268b5a --- /dev/null +++ b/app/components/drawer.js @@ -0,0 +1,305 @@ +import * as React from 'react'; +import { + SafeAreaView, + Text, + View, + FlatList, + Alert, + Platform, +} from 'react-native'; +import { connect } from "react-redux"; +import { createDrawerNavigator } from '@react-navigation/drawer'; +import { ListItem } from 'react-native-elements'; +import Assets from './assets.manager.js'; +import Elements from './elements.js'; +import DB from './storage/'; +import {TouchableOpacity} from 'react-native-gesture-handler'; +import Theme from '../components/theme.style.js'; +import NetInfo from "../components/netstatus"; + +const User = [ + { + name: 'Guest', + avatar_url: Assets.logo.profileHolder, + subtitle: '', + icon: 'chevron-right', + }, +]; + +const Options = [ + { + title: 'My Profile', + icon: 'account', + screen: 'MyProfile', + props: {tab: 0, onBackPress: () => console.log('backed')} + }, + { + title: 'Top-up Points', + icon: 'topup', + screen: 'TopUp', + }, + { + title: 'Fuel Efficiency Tracker', + icon: 'fuelTab', + screen: 'Tracker', + }, +]; + +const Options2 = [ + { + title: 'About Us', + icon: 'about', + screen: 'About', + }, + { + title: 'Loyalty Program', + icon: 'card', + screen: 'Loyalty', + }, + { + title: 'Contact Us', + icon: 'contact', + screen: 'Contact', + }, + { + title: 'Getting Started', + icon: 'tutorial', + screen: 'OnBoarding', + }, +]; + +const Drawer = createDrawerNavigator(); + +const styles = { + container: {padding: 13, paddingTop: 14}, + title: {fontSize: 13, marginLeft: 12}, + titleDisabled: {fontSize: 13, marginLeft: 12, color: '#7e7e7e'}, + icon: {size: 28}, + enrollBtn: { + backgroundColor: '#e74610', + height: Theme.screen.h / 17, + marginHorizontal: 15, + borderRadius: 10, + alignItems: 'center', + justifyContent: 'center', + }, + enrollBtnTxt: { + textAlign: 'center', + alignItems: 'center', + color: '#fff', + fontWeight: '600', + fontSize: 16, + }, + marginEnroll: { + ...Platform.select({ + ios: { + marginTop: Theme.screen.h / 2 - 270, + }, + android: { + marginTop: Theme.screen.h / 4 + 10, + }, + }), + }, +}; + +class CustomDrawer extends React.PureComponent { + + constructor(props) { + super(props) + this.logoutdialog = false + } + + state = { + loading: false, + guest: false + } + + componentDidMount() { + this.init() + } + + componentWillUnmount() { + + } + + init = async () => { + let isGuest = await DB.get('is_guest'); + this.setState({ guest: isGuest }) + }; + + onLogout = async () => { + this.setState({ loading: true }) + NetInfo.netstatus(isConnected => { + if (isConnected) { + this.setState({ loading: false }) + this.logoutAccount(success => { + let props = this.props.props + props.navigation.reset({ + index: 0, + routes: [{name: 'Mpin'}], + }); + }, error =>{}) + } else { + this.setState({ loading: false }) + Elements.nointernet2(); + } + }) + }; + + logoutAccount = (successCallback, errorCallback) => { + DB.logoutAccount(success => { + successCallback() + }, error => { + errorCallback() + }) + } + + navigateTo = (screen = null, param = null, drawerParam = null) => { + let props = this.props.props + let params = screen == "OnBoarding" ? { onBackToMain: () => this.backToMainView() } : param + props.navigation.closeDrawer(drawerParam) + props.navigation.navigate(screen, params) + } + + renderItem(item) { + let range = 4; + let indexes = [0, 4, 8, 12]; + let cn = ''; + for (let i = 0; i < indexes.length; i++) { + cn += item.subtitle.substr(indexes[i], range) + (i < indexes.length - 1 ? ' ' : '') + } + if (this.state.guest) { + return ( + + ); + } else { + return ( + + this.navigateTo('Account', { test: {} }), + }} + bottomDivider + containerStyle={{ backgroundColor: this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.background : Theme.colors.white }} + subtitleStyle={{ color: this.props.app_theme?.theme.colors.text }} + titleStyle={{fontWeight: 'bold', fontSize: 15, color: this.props.app_theme?.theme.colors.text}} + /> + + ); + } + } + + backToMainView = () => { + this.props.props.navigation.navigate('Main') + } + + render() { + return ( + + + + index.toString()} + data={this.props.User} + navigation={this.props.navigation} + renderItem={({item, index}) => { + return this.renderItem(item, index) + }} + style={{fontSize: 16}} + /> + + {Options.map((item, i) => ( + } + titleStyle={[this.state.guest ? styles.titleDisabled : styles.title, { color: !this.state.guest ? this.props.app_theme?.theme.colors.text : '#7e7e7e' }]} + bottomDivider={i == Options.length - 1 ? true : false} + containerStyle={[styles.container, { backgroundColor: this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.background : Theme.colors.white }]} + disabled={this.state.guest ? true : false} + onPress={() => { + this.navigateTo(item.screen, item.props || {}) + }} + /> + ))} + + + {Options2.map((item, i) => ( + } + titleStyle={[styles.title, { color: this.props.app_theme?.theme.colors.text }]} + containerStyle={[styles.container, { backgroundColor: this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.background : Theme.colors.white }]} + bottomDivider={i == Options2.length - 1 ? true : false} + onPress={() => { + this.navigateTo(item.screen, item.props || {}, item.screen) + }} + /> + ))} + + {this.state.guest ? ( + + { + let props = this.props.props + props.navigation.reset({ + index: 0, + routes: [{name: 'Login'}], + }); + }}> + Enroll Your Card Now + + + ) : ( + } + titleStyle={[styles.title, { color: this.props.app_theme?.theme.colors.text }]} + containerStyle={[styles.container, { backgroundColor: this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.background : Theme.colors.white }]} + onPress={() => { + this.props.props.navigation.closeDrawer(); + Alert.alert( + 'Logout', + '\nAre you sure you want to logout?\n', + [ + { + text: 'CANCEL', + style: 'cancel', + }, + { + text: 'OK', + onPress: () => this.onLogout(), + }, + ], + {cancelable: true}, + ); + }} + /> + )} + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(CustomDrawer) diff --git a/app/components/elements.js b/app/components/elements.js new file mode 100644 index 00000000..868bf4a6 --- /dev/null +++ b/app/components/elements.js @@ -0,0 +1,956 @@ +import * as React from 'react'; +import {useEffect, useState, useRef} from 'react'; +import { Platform, View, Text, Image, TouchableOpacity, ScrollView, StyleSheet, TextInput, Modal, TouchableWithoutFeedback, ActivityIndicator, Alert } from 'react-native' +import { Card, ListItem, Button, Icon, Divider, Input, Overlay, CheckBox } from 'react-native-elements' +import {ActionSheet} from 'native-base'; +import DateTimePicker from '@react-native-community/datetimepicker'; +import DateTimePickerModal from "react-native-modal-datetime-picker"; +import DatePicker from "react-native-date-picker"; +import MonthPicker from 'react-native-month-year-picker'; +import moment from 'moment'; +import Theme from './theme.style.js'; +import Assets from './assets.manager.js'; +import CustomIcon from './icons.js'; + +var styles = StyleSheet.create({ + column: { + flexDirection: 'column', + alignItems: 'flex-start', + width: '100%', + padding: 15 + }, + row: { + flexDirection: 'row', + alignItems: 'flex-start', + flexWrap: 'wrap', + flex: 1, + padding: 5, + fontSize: 16, + }, + bullet: { + width: 10 + }, + bulletText: { + flex: 1, + color: 'rgba(0, 0, 0, 0.7)' + }, + boldText: { + fontWeight: 'bold' + }, + normalText: { + color: 'rgba(0, 0, 0, 0.7)' + }, + container: { + flex: 1, + backgroundColor: 'blue', + flexDirection: 'row', + flexWrap: 'wrap' + }, + + item: { + backgroundColor: 'orange', + width: Theme.screen.w / 2, + height: Theme.screen.h / 2, + margin: 5 + }, + image: { + width: Theme.screen.w / 2, + height: Theme.screen.h / 2 + }, + centeredView: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: '#00000090', + padding: 30 + }, + bottomView: { + flex: 1, + justifyContent: "flex-end", + alignItems: "center", + backgroundColor: '#00000090', + padding: 0 + }, + modalView: { + backgroundColor: '#fff', + padding: 20 + }, + modalTitle: { + padding: 5, + fontSize: 18, + fontWeight: 'bold' + }, + modalText: { + padding: 5, + fontSize: 16, + color: 'gray', + marginTop: 10 + }, +}); + +const ModalDialogContainer = (props) => { + const Content = props.content; + return ( + + + + + + + + + + + ) +} + +const ul = function(props){ + + const [drop, setdrop] = useState(false); + + const renderlist = (dataset) => { + return dataset.map((data, index) => { + return ( + + + {'\u2022' + " "} + + + + {data.label} + + + ) + }) + } + + return ( + + + {setdrop(!drop)}}> + {props.title} + + + + + + {drop ? {renderlist(props.list || [])} : null} + ) +} + +const product = function(props){ + return ( + + + + + {props.title} + + ) +} + +const loyaltycard = function(props){ + return ( + + + + + {props.title} + + ) +} + +const card = function(props){ + let h = props.height ? props.height : 200 + return ( + + + + + {props.title} + ) +} + +const button = function(props){ + return ( + + {this.state.sliderIndex == 0 ? + : + } + + + + + ); + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(MyCard); diff --git a/app/screens/myprofile/index.js b/app/screens/myprofile/index.js new file mode 100644 index 00000000..90363306 --- /dev/null +++ b/app/screens/myprofile/index.js @@ -0,0 +1,83 @@ +import React from 'react'; +import { SafeAreaView } from 'react-native'; +import { Container, Tab, Tabs, TabHeading, Text } from 'native-base'; +import { connect } from "react-redux"; +import CustomHeader from '../../components/header.js'; +import Elements from '../../components/elements.js'; +import Theme from '../../components/theme.style'; +import Tab1 from './profile/profile'; +import Tab2 from './card'; +import Tab3 from './transaction/transaction'; +import CustomSafeArea from '../../components/safeArea.component.js'; + +class MyProfile extends React.PureComponent { + + constructor(props) { + super(props) + } + + state = { + px: 0, + pg: null + } + + componentDidMount() { + this.init() + } + + componentWillUnmount() { + + } + + init = async () => { + let t = this.props.route.params && this.props.route.params.tab ? this.props.route.params.tab : '' + let x = 0 + if(t == 'ProfileCardTab') x = 1 + else if(t == 'ProfileTransactionsTab') x = 2 + this.setState({ pg: x }) + } + + TabHead = (title, icon) => { + return ( + + + {title} + + ) + } + + render() { + return ( + + this.props.navigation.goBack()} menu={false} navigation={this.props} /> + + { + this.setState({ pg: tab.i }) + }}> + + + + + + + + + + + + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(MyProfile); \ No newline at end of file diff --git a/app/screens/myprofile/profile/edit.js b/app/screens/myprofile/profile/edit.js new file mode 100644 index 00000000..5bdd3143 --- /dev/null +++ b/app/screens/myprofile/profile/edit.js @@ -0,0 +1,1150 @@ +import * as React from 'react'; +import { SafeAreaView, ScrollView, TouchableOpacity, View, Text, Alert } from 'react-native'; +import { Avatar } from 'react-native-elements'; +import { connect } from "react-redux"; +import { saveUserInfo, savePlainUserInfo } from "../../../redux/actions/AppUserInfoActions"; +import CustomHeader from '../../../components/header.js'; +import Assets from '../../../components/assets.manager.js'; +import Theme from '../../../components/theme.style.js'; +import Elements from '../../../components/elements.js'; +import ImagePicker from 'react-native-image-picker'; +import moment from 'moment'; +import DB from '../../../components/storage/'; +import REQUEST from '../../../components/api/'; +import RNFETCHBLOB from '../../../components/api/file.manager'; +import { ContactOptions, ContactOptionsWithParams } from '../../../components/contact.action'; +import Utils from './utils'; +import CustomSafeArea from '../../../components/safeArea.component'; + + +class EditProfile extends React.PureComponent { + + constructor(props) { + super(props) + } + + state = { + loading: false, + userProfile: null, + focused: false, + editprofileselect: false, + customerserviceselect: false, + currentFocus: null, + opendialog: false, + currentDialog: null, + currentphoto: null, + newphoto: null, + newmobile: "", + newemail: null, + newaddress: null, + newcity: null, + vehicleType: null, + fuelType: null, + maritalStatus: null, + gender: null, + is_deleted: false, + errors: {}, + deleted: false + } + + componentDidMount() { + this.setState({ loading: false, userProfile: this.props.route.params.data }) + } + + componentWillUnmount() { + + } + + onInputFocus = (index) => { + this.setState({ focused: true, currentFocus: index }) + } + + onSelectPress = (index) => { + this.setState({ opendialog: true, currentDialog: index }) + } + + getFT = (type) => { + let types = Assets.fueltypes + for(var x=0;x { + let types = Assets.vehicletypes + for(var x=0;x { + let types = Assets.civilstatus + for(var x=0;x { + let types = Assets.gender + for(var x=0;x { + + } + + onDeletePhoto = () => { + this.setState({ is_deleted: true, currentphoto: Assets.logo.profileHolder }) + } + + formatBytes = (bytes, decimals = 2) => { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return { stringFormat: parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i], decimalFormat: parseFloat((bytes / Math.pow(k, i)).toFixed(dm)), sizeFormat: sizes[i] }; +} + + onEditPhoto = () => { + let options = { + mediaType: 'photo', + maxHeight: 1500, + maxWidth: 1500, + quality: 0.75 + } + ImagePicker.launchImageLibrary(options, (response) => { + let filesize = this.formatBytes(response.fileSize) + console.log(filesize.stringFormat.includes('KB')) + if(!filesize.stringFormat.includes('KB') && !filesize.stringFormat.includes('Bytes')) { + Alert.alert("Error", "Image size too large.") + return + } + if(!response.didCancel){ + if(this.state.is_deleted){ + this.setState({ is_deleted: false, currentphoto: null }) + } + this.setState({ newphoto: response }) + } + }) + } + + validateEmail = () => { + let regEx = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/ + return regEx.test(this.state.email) + } + + FormValidate = async () => { + let res = {} + this.setState({ loading: true }) + if(this.state.newemail == ""){ + res = Object.assign(this.state.errors, {email: ["This field is required"]}) + } + if(this.state.newaddress == "" || this.state.newaddress == null && this.state.userProfile.address == ""){ + res = Object.assign(this.state.errors, {address: ["This field is required"]}) + } + if(!this.state.vehicleType && this.state.userProfile.vo_code == "0"){ + res = Object.assign(this.state.errors, {vehicleType: ["This field is required"]}) + } + if(!this.state.fuelType && this.state.userProfile.fueltype_code == "0"){ + res = Object.assign(this.state.errors, {fuelType : ["This field is required"]}) + } + this.setState({ errors: res, loading: false }) + return Object.keys(this.state.errors).length > 0 ? false : true + } + + Body = function() { + return { + image: this.state.newphoto ? this.state.newphoto.uri : null, + mobile: this.state.newmobile, + email: this.state.newemail, + address: this.state.newaddress, + city: this.state.newcity, + vo_code: this.state.vehicleType ? this.state.vehicleType.value : null, + fueltype_code: this.state.fuelType ? this.state.fuelType.value : null, + civilstatus_code: this.state.maritalStatus ? this.state.maritalStatus.value : null, + gender_code: this.state.gender ? this.state.gender.value : null, + is_deleted: this.state.is_deleted + } + } + + fetchProfileBlob = async (successCallback, errorCallback) => { + let SESSION = await DB.session() + let params = { + newphoto: this.state.newphoto, + userProfile: this.state.userProfile, + newmobile: this.state.newmobile, + newemail: this.state.newemail, + newaddress: this.state.newaddress, + newcity: this.state.newcity, + vehicleType: this.state.vehicleType, + fuelType: this.state.fuelType, + maritalStatus: this.state.maritalStatus, + gender: this.state.gender, + is_deleted: this.state.is_deleted + } + RNFETCHBLOB("update_profile_no_otp", "POST", + {Authorization: SESSION.token}, {}, Utils.blobBody(params), + (res) => { + if(res.status == 1) { + this.updateProfileInBackground(onSuccess => successCallback(onSuccess), onError => errorCallback(onError)) + } else { + Utils.responseHandler(res, () => { + successCallback(res) + }, (errors) => { + console.log(errors) + errorCallback(errors) + }) + } + }, (err) => { + errorCallback(err) + }) + } + + updateProfileBlob = async (successCallback, errorCallback) => { + let SESSION = await DB.session() + let params = { + userProfile: this.state.userProfile, + newmobile: this.state.newmobile, + newemail: this.state.newemail, + newaddress: this.state.newaddress, + newcity: this.state.newcity, + vehicleType: this.state.vehicleType, + fuelType: this.state.fuelType, + maritalStatus: this.state.maritalStatus, + gender: this.state.gender, + is_deleted: this.state.is_deleted + } + await REQUEST("update_profile_no_otp", "post", { + Authorization: SESSION.token, + "Content-Type": "multipart/form-data", + }, {}, Utils.formData(params), + (res) => { + if(res.status == 1) { + this.updateProfileInBackground(onSuccess => successCallback(onSuccess), onError => errorCallback(onError)) + } else { + Utils.responseHandler(res, () => { + successCallback(res) + }, (errors) => { + console.log(errors) + errorCallback(errors) + }) + } + }, (error) => { + errorCallback(error) + }) + } + + responseHandler = (res, responseCallback) => { + if(res.status == 1){ + setTimeout(() => { + Alert.alert( + "Success", + '\nYour profile is successfully updated\n', + [{ + text: 'OK', onPress: () => { + responseCallback() + } + }] + ); + }, 700) + } else { + setTimeout(() => { + Alert.alert("Error", res?.email[0]); + }, 700) + } + } + + updateProfileInBackground = async (successCallback, errorCallback) => { + const SESSION = await DB.session() + try{ + this.props.saveUserInfo({ token: SESSION.token, card_number: SESSION.user.card_number }).then(res => { + DB.updateProfile(res, () => { + successCallback(res) + }, (error) => { + errorCallback(error) + }) + }) + .catch(error => this.setState({ loading: false })) + } catch(error) { + console.log(error) + } + } + + SaveNoOtp = async () => { + this.setState({ loading: true }) + if(this.state.newphoto) { + this.fetchProfileBlob(success => { + this.setState({ loading: false }) + if(success.status == 0) { + this.setState({ errors: success }) + } else { + this.responseHandler(success, handler => { + this.props.route.params.onGoBack(success.data) + this.props.navigation.goBack() + }) + } + }, error => { + this.setState({ loading: false }) + setTimeout(() => { + if(this.state.newphoto) Alert.alert("Error", "Failed to upload image") + }, 300) + }) + } else { + this.setState({ loading: true }) + this.updateProfileBlob(success => { + this.setState({ loading: false }) + if(success.status == 0) { + this.setState({ errors: success }) + } else { + this.responseHandler(success, handler => { + this.props.route.params.onGoBack(success.data) + this.props.navigation.goBack() + }) + } + }, error => { + this.setState({ loading: false }) + setTimeout(() => { + if(this.state.newphoto) Alert.alert("Error", "Failed to upload image") + }, 300) + }) + } + } + + SaveWithOtp = () => { + this.props.navigation.navigate("AccountSendOtp", { + data: { + lcard_uuid: this.state.userProfile.lcard_uuid, + mobile_number: this.state.newmobile.replace("+", "") + }, + type: 'edit', + callback: (res) => { + if(res == "valid") this.SaveNoOtp() + }, + }) + } + + onSubmit = () => { + this.setState({ focused: false }) + if(this.hasChanges()){ + Alert.alert( + "Update Profile", + '\nAre you sure you want to save changes to your profile?\n', + [{ + text: 'Cancel', + style: 'cancel', + },{ + text: 'OK', onPress: async () => { + let validation = await this.FormValidate() + if(!validation) return + if(this.state.newmobile != "" && this.state.newmobile.replace("+", "") != this.state.userProfile.mobile){ + this.SaveWithOtp() + }else{ + this.SaveNoOtp() + } + } + }], + {cancelable: true} + ); + }else{ + this.props.navigation.goBack() + } + } + + hasChanges = () => { + let values = Object.values(this.Body()) + for(var x=0;x { + Elements.ActionOption({ + options: ['Edit Photo', 'Delete Photo', 'Cancel'], + functions: [{ + execute: () => this.onEditPhoto() + }, { + execute: () => this.onDeletePhoto() + }] + }) + } + + renderProfilePhoto = () => { + if(this.state.currentphoto) return Assets.logo.profileHolder + else if(!this.state.currentphoto && this.state.newphoto || this.state.newphoto) return {uri: this.state.newphoto.uri} + else if(!this.state.currentphoto && !this.state.newphoto && this.state.userProfile && this.state.userProfile.photo) return {uri: this.state.userProfile.photo} + else return Assets.logo.profileHolder + } + + render() { + return ( + + + this.setState({ opendialog: !this.state.opendialog })} + onSelect={(value, string) => { + this.setState({ vehicleType: { value: value, string: string }, opendialog: !this.state.opendialog }) + }} + items={Assets.vehicletypes} + /> + this.setState({ opendialog: !this.state.opendialog })} + onSelect={(value, string) => { + this.setState({ fuelType: { value: value, string: string }, opendialog: !this.state.opendialog }) + }} + items={Assets.fueltypes} + /> + this.setState({ opendialog: !this.state.opendialog })} + onSelect={(value, string) => { + this.setState({ maritalStatus: { value: value, string: string }, opendialog: !this.state.opendialog }) + }} + items={Assets.civilstatus} + /> + this.setState({ opendialog: !this.state.opendialog })} + onSelect={(value, string) => { + this.setState({ gender: { value: value, string: string }, opendialog: !this.state.opendialog }) + }} + items={Assets.gender} + /> + + { + if(this.hasChanges()){ + Alert.alert( + null, + 'You have an unsaved profile information, are you sure you want to navigate back?\n', + [{ + text: 'Cancel', + style: 'cancel', + }, + { + text: 'OK', onPress: () => this.props.navigation.navigate("MyProfile") + }, + ], + {cancelable: true} + ); + }else{ + this.props.navigation.navigate("MyProfile") + } + }} + navigation={this.props.navigation} + /> + + + + + + + + {this.state.userProfile && this.state.userProfile.photo && !this.state.is_deleted || this.state.userProfile && !this.state.userProfile.photo && this.state.newphoto && !this.state.is_deleted ? + this.handleUpdatePhotoOptions()}> + Edit Photo + : + this.onEditPhoto()}> + Add Photo + + } + + + + + ContactOptionsWithParams({ cardnumber: this.state?.userProfile?.data?.card_number })} + error={"This field is required"} /> + + ContactOptionsWithParams({ cardnumber: this.state?.userProfile?.data?.card_number })} + error={"This field is required"} /> + + ContactOptionsWithParams({ cardnumber: this.state?.userProfile?.data?.card_number })} + error={"This field is required"} /> + + { + this.setState({ newmobile: value }) + }} + focused={this.state.focused && this.state.currentFocus == 3 ? true : false} + onFocus={() => this.onInputFocus(3)} + error={"This field is required"} /> + + this.onInputFocus(4)} + onChangeText={(value) => { + this.setState({ newemail: value }) + delete this.state.errors["email"] + }} + hasError={this.state.errors && this.state.errors.email ? true : false} + errorMessage={this.state.errors && this.state.errors.email ? this.state.errors?.email[0] : "Invalid email"} + /> + + this.onInputFocus(5)} + onChangeText={(value) => { + this.setState({ newaddress: value }) + delete this.state.errors["address"] + }} + hasError={this.state.errors && this.state.errors.address ? true : false} + errorMessage={this.state.errors && this.state.errors.address ? this.state.errors?.address[0] : "Invalid address"} + /> + + { + this.onInputFocus(7) + this.onSelectPress(1) + delete this.state.errors["vehicleType"] + }} + hasError={this.state.errors && this.state.errors.vehicleType ? true : false} + errorMessage={this.state.errors && this.state.errors.vehicleType ? this.state.errors?.vehicleType[0] : "Invalid vehicle type"} + /> + + { + this.onInputFocus(8) + this.onSelectPress(2) + delete this.state.errors["fuelType"] + }} + hasError={this.state.errors && this.state.errors.fuelType ? true : false} + errorMessage={this.state.errors && this.state.errors.fuelType ? this.state.errors?.fuelType[0] : "Invalid fuel type"} + /> + + { + this.onInputFocus(9) + this.onSelectPress(3) + }} + error={"This field is required"} /> + + { + this.onInputFocus(10) + this.onSelectPress(4) + }} + error={"This field is required"} /> + + + + + this.onSubmit()} style={{width: '92%', padding: 15, borderRadius: 10, backgroundColor: Theme.colors.primary}}> + Save Changes + + + + + ) + } +} + +const mapStateToProps = (state) => { + return { + userinfo: state.appUserInfoReducer.userinfo, + app_theme: state.appThemeReducer.theme + } +} + +const mapDispatchToProps = { + saveUserInfo +} + +export default connect(mapStateToProps, mapDispatchToProps)(EditProfile) + + +// export default function EditProfile(navigation) { + +// const [loading, setloading] = useState(false) +// const [userProfile, setUserProfile] = useState(null) +// const [focused, setfocused] = useState(false); +// const [editprofileselect, seteditprofileselect] = useState(false) +// const [customerserviceselect, setcustomerserviceselect] = useState(false) +// const [currentFocus, setCurrentFocus] = useState(null); + +// const [opendialog, setopendialog] = useState(false); +// const [currentDialog, setCurrentDialog] = useState(null); + +// const [currentphoto, setcurrentphoto] = useState(null) +// const [newphoto, setnewphoto]= useState(null) +// const [newmobile, setnewmobile]= useState("") +// const [newemail, setnewemail]= useState(null) +// const [newaddress, setnewaddress]= useState(null) +// const [newcity, setnewcity]= useState(null) + +// const [vehicleType, setVehicleTyle] = useState(null); +// const [fuelType, setFuelType] = useState(null); +// const [maritalStatus, setMaritalStatus] = useState(null); +// const [gender, setGender] = useState(null); +// const [is_deleted, setdeleted] = useState(false); + +// const [errors, seterrors] = useState({}); + + +// useEffect(() => { +// // navigation.route.params.data.photo = '' +// setloading(true) +// setUserProfile(navigation.route?.params.data) +// setloading(false) +// console.log("PROFILE", navigation.route?.params.data) +// }, []) + +// const onInputFocus = (index) => { +// setfocused(true) +// setCurrentFocus(index) +// } +// const onSelectPress = (index) => { +// setopendialog(true) +// setCurrentDialog(index) +// } + +// const getFT = (type) => { +// let types = Assets.fueltypes +// for(var x=0;x { +// let types = Assets.vehicletypes +// for(var x=0;x { +// let types = Assets.civilstatus +// for(var x=0;x { +// let types = Assets.gender +// for(var x=0;x { + +// } + +// const onDeletePhoto = () => { +// setdeleted(true) +// setcurrentphoto(Assets.logo.profileHolder) +// } + +// const onEditPhoto = () => { +// ImagePicker.launchImageLibrary({}, (response) => { +// // let b64 = response.data +// // response.data = '' +// if(!response.didCancel){ +// if(is_deleted){ +// setdeleted(false) +// setcurrentphoto(null) +// } +// setnewphoto(response) +// } +// // console.log(response) +// }); +// } + +// const FormValidate = async () => { +// let res = {} +// setloading(true) +// if(newemail == ""){ +// res = Object.assign(errors, {email: ["This field is required"]}) +// } +// if(newaddress == "" || newaddress == null && userProfile.address == ""){ +// // seterrors(Object.assign(errors, {address: ["This field is required"]})) +// res = Object.assign(errors, {address: ["This field is required"]}) +// } +// if(!vehicleType && userProfile.vo_code == "0"){ +// // seterrors(Object.assign(errors, {vehicleType: ["This field is required"]})) +// res = Object.assign(errors, {vehicleType: ["This field is required"]}) +// } +// if(!fuelType && userProfile.fueltype_code == "0"){ +// // seterrors(Object.assign(errors, {fuelType : ["This field is required"]})) +// res = Object.assign(errors, {fuelType : ["This field is required"]}) +// } +// seterrors(res) +// setloading(false) +// return Object.keys(errors).length > 0 ? false : true +// } + +// const Body = function(){ +// return { +// image: newphoto ? newphoto.uri : null, +// mobile: newmobile, +// email: newemail, +// address: newaddress, +// city: newcity, +// vo_code: vehicleType ? vehicleType.value : null, +// fueltype_code: fuelType ? fuelType.value : null, +// civilstatus_code: maritalStatus ? maritalStatus.value : null, +// gender_code: gender ? gender.value : null, +// is_deleted: is_deleted +// } +// } + +// const SaveNoOtp = async () => { + +// let SESSION = await DB.session() +// if(newphoto){ +// // console.log("BLOB BODY", blobBody.image.uri) +// setloading(true) +// RNFETCHBLOB("update_profile_no_otp", "POST", +// {Authorization: SESSION.token}, {}, Utils.blobBody({newphoto, userProfile, newmobile, newemail, newaddress, newcity, vehicleType, fuelType, maritalStatus, gender, is_deleted}), +// function(res){ +// console.log(res) +// setloading(false) +// Utils.responseHandler(res, () => { +// navigation.route.params.onGoBack() +// navigation.navigation.goBack() +// }, (errors) => { +// seterrors(errors) +// }) +// }, function(err){ +// setloading(false) +// setTimeout(() => { +// if(newphoto) Alert.alert("Error", "Failed to upload image") +// }, 300) +// }) + +// }else{ +// setloading(true) +// await REQUEST("update_profile_no_otp", "post", { +// Authorization: SESSION.token, +// "Content-Type": "multipart/form-data", +// }, {}, Utils.formData({userProfile, newmobile, newemail, newaddress, newcity, vehicleType, fuelType, maritalStatus, gender, is_deleted}), +// function(res){ +// setloading(false) +// console.log("Update Res", res) +// Utils.responseHandler(res, () => { +// navigation.route.params.onGoBack() +// navigation.navigation.goBack() +// }, (errors) => { +// seterrors(errors) +// }) +// }, function(error){ +// console.log("Failed", error) +// setloading(false) +// setTimeout(() => { +// if(newphoto) Alert.alert("Error", "Failed to upload image") +// }, 300) +// }) +// } + +// } + +// const SaveWithOtp = () => { + +// navigation.navigation.navigate("AccountSendOtp", { +// data: { +// lcard_uuid: userProfile.lcard_uuid, +// mobile_number: newmobile.replace("+", "") +// }, +// type: 'edit', +// callback: (res) => { +// if(res == "valid") SaveNoOtp() +// }, +// }) +// } + +// const onSubmit = () => { +// setfocused(false) +// if(hasChanges()){ +// Alert.alert( +// "Update Profile", +// '\nAre you sure you want to save changes to your profile?\n', +// [{ +// text: 'Cancel', +// style: 'cancel', +// },{ +// text: 'OK', onPress: async () => { +// let validation = await FormValidate() +// if(!validation) return +// if(newmobile != "" && newmobile.replace("+", "") != userProfile.mobile){ +// SaveWithOtp() +// }else{ +// SaveNoOtp() +// } +// } +// }], +// {cancelable: true} +// ); +// }else{ +// navigation.navigation.goBack() +// } +// } + +// const hasChanges = () => { +// let values = Object.values(Body()) +// for(var x=0;x { +// Elements.ActionOption({ +// options: ['Edit Photo', 'Delete Photo', 'Cancel'], +// functions: [{ +// execute: () => onEditPhoto() +// }, { +// execute: () => onDeletePhoto() +// }] +// }) +// } + +// const renderProfilePhoto = () => { +// if(currentphoto) return Assets.logo.profileHolder +// else if(!currentphoto && newphoto || newphoto) return {uri: newphoto.uri} +// else if(!currentphoto && !newphoto && userProfile && userProfile.photo) return {uri: userProfile.photo} +// else return Assets.logo.profileHolder +// } + +// return ( +// +// +// setopendialog(!opendialog)} +// onSelect={(value, string) => { +// setVehicleTyle({ +// value: value, +// string: string +// }) +// setopendialog(!opendialog) +// }} +// items={Assets.vehicletypes} +// /> +// setopendialog(!opendialog)} +// onSelect={(value, string) => { +// setFuelType({ +// value: value, +// string: string +// }) +// setopendialog(!opendialog) +// }} +// items={Assets.fueltypes} +// /> +// setopendialog(!opendialog)} +// onSelect={(value, string) => { +// setMaritalStatus({ +// value: value, +// string: string +// }) +// setopendialog(!opendialog) +// }} +// items={Assets.civilstatus} +// /> +// setopendialog(!opendialog)} +// onSelect={(value, string) => { +// setGender({ +// value: value, +// string: string +// }) +// setopendialog(!opendialog) +// }} +// items={Assets.gender} +// /> + +// { +// if(hasChanges()){ +// Alert.alert( +// null, +// 'You have an unsaved profile information, are you sure you want to navigate back?\n', +// [{ +// text: 'Cancel', +// style: 'cancel', +// }, +// { +// text: 'OK', onPress: () => navigation.navigation.navigate("MyProfile") +// }, +// ], +// {cancelable: true} +// ); +// }else{ +// navigation.navigation.navigate("MyProfile") +// } +// }} +// navigation={navigation} +// /> + +// + +// +// +// {/* */} +// +// +// +// {userProfile && userProfile.photo && !is_deleted || userProfile && !userProfile.photo && newphoto && !is_deleted ? +// +// Edit Photo +// : +// onEditPhoto()}> +// Add Photo +// +// } +// +// +// +// +// + +// + +// + +// { +// setnewmobile(value) +// console.log(value) +// }} +// focused={focused && currentFocus == 3 ? true : false} +// onFocus={() => onInputFocus(3)} +// error={"This field is required"} /> + +// onInputFocus(4)} +// onChangeText={(value) => { +// setnewemail(value) +// delete errors["email"] +// }} +// hasError={errors && errors.email ? true : false} +// errorMessage={errors && errors.email ? errors.email[0] : "Invalid email"} +// /> + +// onInputFocus(5)} +// onChangeText={(value) => { +// setnewaddress(value) +// delete errors["address"] +// }} +// hasError={errors && errors.address ? true : false} +// errorMessage={errors && errors.address ? errors.address[0] : "Invalid address"} +// /> + +// {/* onInputFocus(6)} +// onChangeText={(value) => setnewcity(value)} +// error={"This field is required"} /> */} + +// { +// onInputFocus(7) +// onSelectPress(1) +// delete errors["vehicleType"] +// }} +// hasError={errors && errors.vehicleType ? true : false} +// errorMessage={errors && errors.vehicleType ? errors.vehicleType[0] : "Invalid vehicle type"} +// /> + +// { +// onInputFocus(8) +// onSelectPress(2) +// delete errors["fuelType"] +// }} +// hasError={errors && errors.fuelType ? true : false} +// errorMessage={errors && errors.fuelType ? errors.fuelType[0] : "Invalid fuel type"} +// /> + +// { +// onInputFocus(9) +// onSelectPress(3) +// }} +// error={"This field is required"} /> + +// { +// onInputFocus(10) +// onSelectPress(4) +// }} +// error={"This field is required"} /> + +// +// +// +// +// Save Changes +// +// +// +// +// ); +// } \ No newline at end of file diff --git a/app/screens/myprofile/profile/profile.js b/app/screens/myprofile/profile/profile.js new file mode 100644 index 00000000..671f4ddf --- /dev/null +++ b/app/screens/myprofile/profile/profile.js @@ -0,0 +1,227 @@ +import * as React from 'react'; +import { SafeAreaView, View, Text, ScrollView, Image, TouchableOpacity, Platform } from 'react-native'; +import { connect } from "react-redux"; +import { saveUserInfo } from "../../../redux/actions/AppUserInfoActions"; +import NetInfo from "../../../components/netstatus" +import Assets from '../../../components/assets.manager.js'; +import Theme from '../../../components/theme.style.js'; +import Elements from '../../../components/elements.js'; +import CustomIcon from '../../../components/icons.js'; +import { Avatar, Divider } from 'react-native-elements'; +import DB from '../../../components/storage/'; + + +class MyProfile extends React.PureComponent { + + constructor(props) { + super(props) + } + + state = { + connected: false, + loading: false, + userProfile: (this.props.userinfo != undefined && this.props.userinfo.data != undefined) ? this.props.userinfo.data : null, + session: null + } + + componentDidMount() { + this.init() + } + + componentWillUnmount() { + + } + + init = () => { + NetInfo.netstatus(isConnected => { + if(isConnected){ + this.loadUserProfile() + }else{ + Elements.nointernet2() + } + }) + } + + loadUserProfile = async () => { + if(this.props.userinfo == undefined) { + const SESSION = await DB.session() + this.setState({ connected: true, loading: true, session: SESSION }) + try{ + this.props.saveUserInfo({ token: SESSION.token, card_number: SESSION.user.card_number }).then(res => { + this.setState({ userProfile: res.data, loading: false }) + DB.updateProfile(res, function(){}, function(error){}) + }) + .catch(error => this.setState({ loading: false })) + }catch(error){ + this.setState({ loading: false }) + } + } else { + this.setState({ + connected: true, + userProfile: this.props.userinfo.data, + loading: false + }) + } + } + + getData = (key) => { + return this.state.userProfile != null ? this.state.userProfile[key] : "" + } + + getFT = (type) => { + let types = Assets.fueltypes + for(var x=0;x { + let types = Assets.vehicletypes + for(var x=0;x { + let navigation = this.props.navigation.navigation + navigation.navigate("EditProfile", {data: this.state.userProfile, onGoBack: (data) => { + console.log(data) + this.setState({ userProfile: data, loading: false }) + this.init() + }}) + } + + onSelectedPaymentCard = (payment_card) => { + console.log(payment_card) + } + + renderNoInternetView = () => { + return ( + + { + NetInfo.netstatus(isConnected => { + if(isConnected){ + this.init() + }else{ + Elements.nointernet2() + } + }) + }} + /> + + ) + } + + render() { + if(!this.state.connected) return this.renderNoInternetView() + return ( + + + + + + + {this.state.userProfile ? Theme.formatter.NAME(this.state.userProfile) : ''} + + this.onEdit()} style={{backgroundColor: Theme.colors.primary, marginTop: 5, padding: 15, borderRadius: 10}}> + Edit Profile + + + + + + + Card Number + + {this.state.userProfile ? Theme.formatter.CN(this.getData("card_number")) : ''} + + + + + + + Mobile Number + + +{this.state.userProfile ? this.getData("mobile") : ''} + + + + + + + Birthday + + {Theme.formatter.DTUI(this.getData("birthdate"))} + + + + + + + Email + + {this.state.userProfile ? this.getData("email") : ''} + + + + + + + Address + + {this.state.userProfile ? this.getData("address") : ''} + + + + + + + Owned Vehicle Type + + + {this.state.userProfile ? this.getVT(this.state.userProfile.vo_code) : ''} + + + + + + + + Fuel Type + + {this.state.userProfile ? this.getFT(this.state.userProfile.fueltype_code) : ''} + + + + {/* + + this.props.navigation.navigation.navigate('PayatpumpPaymentList', { displaySelectionCard: true })} + style={{ flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginLeft: 16 }}> + + + Pay at Pump Payment Methods + + + + */} + + + + ); + } +} + +const mapStateToProps = (state) => { + return { + userinfo: state.appUserInfoReducer.userinfo, + app_theme: state.appThemeReducer.theme + } +} + +const mapDispatchToProps = { + saveUserInfo +} + +export default connect(mapStateToProps, mapDispatchToProps)(MyProfile) \ No newline at end of file diff --git a/app/screens/myprofile/profile/utils.js b/app/screens/myprofile/profile/utils.js new file mode 100644 index 00000000..708e2b1b --- /dev/null +++ b/app/screens/myprofile/profile/utils.js @@ -0,0 +1,100 @@ +import React from 'react'; +import {Alert, Platform} from 'react-native'; +import Assets from '../../../components/assets.manager.js'; +import Theme from '../../../components/theme.style.js'; + +const formData = (data) => { + let formData = new FormData() + formData.append('firstname', data.userProfile.firstname) + formData.append('middlename', data.userProfile.middlename) + formData.append('lastname', data.userProfile.lastname) + formData.append('birthdate', Theme.formatter.DTUI(data.userProfile.birthdate)) + formData.append('mobile', data.newmobile && data.newmobile != "+" + data.userProfile.mobile ? Theme.formatter.PMBL(data.newmobile.replace("+", "")) : Theme.formatter.PMBL(data.userProfile.mobile)) + formData.append('email', data.newemail ? data.newemail : data.userProfile.email) + formData.append('address', data.newaddress ? data.newaddress : data.userProfile.address) + // formData.append('city', data.newcity ? data.newcity : data.userProfile.city_name) + formData.append('vo_code', data.vehicleType ? data.vehicleType.value : data.userProfile.vo_code) + formData.append('fueltype_code', data.fuelType ? data.fuelType.value : data.userProfile.fueltype_code) + formData.append('civilstatus_code', data.maritalStatus ? data.maritalStatus.value : data.userProfile.civilstatus_code) + formData.append('gender_code', data.gender ? data.gender.value : data.userProfile.gender_code) + formData.append('is_deleted', data.is_deleted) + return formData +} + +const responseHandler = (res, callback, onError) => { + if(res.status == 1){ + if(Platform.OS == 'ios'){ + setTimeout(() => { + Alert.alert( + "Success", + '\nYour profile is successfully updated\n', + [{ + text: 'OK', onPress: () => { + callback() + } + }] + ); + }, 700) + }else{ + Alert.alert( + "Success", + '\nYour profile is successfully updated\n', + [{ + text: 'OK', onPress: () => { + navigation.route.params.onGoBack() + navigation.navigation.goBack() + + } + }] + ); + } + }else{ + onError(res.data) + if(Platform.OS == 'ios'){ + let message = res?.data.email[0] != undefined ? res?.data.email : res.message + setTimeout(() => { + Alert.alert( + "Error", + '\n' + message, + [{ + text: 'OK', onPress: () => { + // navigation.route.params.onGoBack() + // navigation.navigation.goBack() + } + }] + ); + }, 700) + } + } +} + +const blobBody = (data) => { + return { + image: { + name: 'photo.jpg', + filename: 'photo.jpg', + type: data.newphoto.type, + uri: data.newphoto.uri, + data: data.newphoto.data + }, + firstname: data.userProfile.firstname, + middlename: data.userProfile.middlename, + lastname: data.userProfile.lastname, + birthdate: Theme.formatter.DTUI(data.userProfile.birthdate), + mobile: data.newmobile && data.newmobile != "+" + data.userProfile.mobile ? Theme.formatter.PMBL(data.newmobile.replace("+", "")) : Theme.formatter.PMBL(data.userProfile.mobile), + email: data.newemail ? data.newemail : data.userProfile.email, + address: data.newaddress ? data.newaddress : data.userProfile.address, + // city: data.newcity ? data.newcity : data.userProfile.city_name, + vo_code: data.vehicleType ? data.vehicleType.value : data.userProfile.vo_code, + fueltype_code: data.fuelType ? data.fuelType.value : data.userProfile.fueltype_code, + civilstatus_code: data.maritalStatus ? data.maritalStatus.value : data.userProfile.civilstatus_code, + gender_code: data.gender ? data.gender.value : data.userProfile.gender_code, + is_deleted: data.is_deleted + } +} + +export default { + blobBody, + formData, + responseHandler +} diff --git a/app/screens/myprofile/transaction/details.js b/app/screens/myprofile/transaction/details.js new file mode 100644 index 00000000..783a9bfb --- /dev/null +++ b/app/screens/myprofile/transaction/details.js @@ -0,0 +1,311 @@ +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { SafeAreaView, ScrollView, TouchableOpacity, Button, View, Text, Image, Alert, Linking, Platform } from 'react-native'; +import { connect } from "react-redux"; +import CustomHeader from '../../../components/header.js'; +import Assets from '../../../components/assets.manager.js'; +import Elements from '../../../components/elements.js'; +import Theme from '../../../components/theme.style.js'; +import Icon from '../../../components/icons.js'; +import REQUEST from '../../../components/api'; +import DB from '../../../components/storage'; +import { openComposer } from 'react-native-email-link' +import CustomSafeArea from '../../../components/safeArea.component.js'; + + +class TransactionDetails extends React.Component { + + constructor(props) { + super(props) + } + + state = { + loading: false, + ratesubmitted: false, + rating: 0, + session: '', + transaction: [], + umail: "", + cn: "" + } + + componentDidMount() { + this.init() + } + + componentWillUnmount() { + + } + + init = async () => { + this.props.route.params.data.is_feedback_disabled = false + let session = await DB.session() + let user_profile = await DB.profile() + this.setState({ cn: user_profile.data.card_number, transaction: this.props.route.params.data, session: session }) + } + + renderRatings = (rating, disabled) => { + return [1, 2, 3, 4, 5].map((rate, i) => { + return ( + 0 || !this.state.ratesubmitted ? 0.5 : 1 } onPress={this.state.transaction.rating == 0 && !this.state.ratesubmitted ? () => this.setState({ rating: rate }) : null} > + = rate ? "star" : "staro"} color={rating > 0 || !this.state.ratesubmitted ? Theme.colors.yellow : Theme.colors.darkerGray} size={40} /> + + ) + }) + } + + onSubmit = async () => { + if (this.state.transaction.is_feedback_disabled) return false + this.setState({ loading: true }) + REQUEST('transaction_rate', 'post', { + Authorization: this.state.session.token, + Accept: 'application/json', + card_number: this.state.session.user.card_number + }, {}, { + trans_num: this.state.transaction.trans_num, + rate: this.state.rating + }, (result) => { + this.setState({ loading: false, rating: 0, ratesubmitted: true }) + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert(result.message, 'Rating has been submitted') + }, 300) : + Alert.alert(result.message, 'Rating has been submitted') + }, (error) => { + this.setState({ loading: false }) + }) + } + + checkPendingTransaction = () => { + if(this.state.rating > 0) { + Alert.alert( + '', + 'You have an unsaved rating, are you sure you want to navigate back?', + [ + { + text: 'No', + style: 'cancel', + }, + { + text: 'Yes', + onPress: async () => { + this.props.route.params?.onBackPress() + this.props.navigation.goBack() + }, + }, + ], + {cancelable: true}, + ); + } else { + this.props.route.params?.onBackPress() + this.props.navigation.goBack() + } + } + + render() { + return ( + + + this.checkPendingTransaction()} + menu={false} + backscreen="MyProfile" + navigation={this.props.navigation} + /> + + + + Sales Invoice Number + + + {this.state.transaction.trans_num || 455826} + + + + {this.state.transaction.station || 'APP'} + + + + Rate Your Experience + + {this.renderRatings(this.state.rating ? this.state.rating : this.state.transaction.rating, this.state.transaction.is_feedback_disabled || this.state.transaction.is_disabled)} + + this.state.transaction.is_feedback_disabled || this.state.transaction.is_disabled || this.state.ratesubmitted || this.state.rating == 0 ? null : this.onSubmit()} style={{width: '90%', padding: 12, alignItems: 'center', backgroundColor: this.state.rating == 0 || this.state.ratesubmitted ? "" : Theme.colors.primary, borderColor: Theme.colors.primary, borderWidth: 2, borderRadius: 10, marginTop: 15}}> + Submit Rating + + {this.state.transaction.is_feedback_disabled ? null : + { + await REQUEST("contact_us", "get", {}, {}, {}, async (res) => { + if(res.status == 1 && res.data){ + openComposer({ + to: res.data.contact_email_address_mobile, + subject: `Mobile App Feedback:\nCN${cn}`, + body: '' + }) + } else { + console.log(res.message, res.data) + } + } + ) + }} style={{width: '90%', padding: 12, alignItems: 'center', marginTop: 15}}> + Send Feedback + } + + + + ); + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(TransactionDetails) + +// export default function TransactionDetails(navigation) { + +// const [loading, setloading] = useState(false) +// const [ratesubmitted, setratesubmitted] = useState(false) +// const [rating, setrating] = useState(0); +// const [session, setsession] = useState(''); +// const [transaction, settransaction] = useState([]); +// const [umail, setumail] = useState("") +// const [cn, setcn] = useState("") + +// const init = async () => { +// navigation.route.params.data.is_feedback_disabled = false +// let session = await DB.session() +// let user_profile = await DB.profile() +// await setcn(user_profile.data.card_number) +// await settransaction(navigation.route.params.data) +// await setsession(session) +// console.log("transaction", transaction, session) +// } + +// useEffect(() => { +// init() +// }, []) + +// const renderRatings = (rating, disabled) => { +// return [1, 2, 3, 4, 5].map((rate, i) => { +// return ( +// 0 || !ratesubmitted ? 0.5 : 1 } onPress={transaction.rating == 0 && !ratesubmitted ? () => setrating(rate) : null} > +// = rate ? "star" : "staro"} color={rating > 0 || !ratesubmitted ? Theme.colors.yellow : Theme.colors.darkerGray} size={40} /> +// +// ) +// }) +// } + +// const onSubmit = async () => { +// if (transaction.is_feedback_disabled) return false +// setloading(true) +// REQUEST('transaction_rate', 'post', { +// Authorization: session.token, +// Accept: 'application/json', +// card_number: session.user.card_number +// }, {}, { +// trans_num: transaction.trans_num, +// rate: rating +// }, function(result){ +// console.log(result) +// setloading(false) +// setrating(0) +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert(result.message, 'Rating has been submitted') +// }, 300) : +// Alert.alert(result.message, 'Rating has been submitted') +// setratesubmitted(true) +// }, function(error){ +// setloading(false) +// console.log(error) +// }) +// } + +// const checkPendingTransaction = () => { +// if(rating > 0) { +// Alert.alert( +// '', +// 'You have an unsaved rating, are you sure you want to navigate back?', +// [ +// { +// text: 'No', +// style: 'cancel', +// }, +// { +// text: 'Yes', +// onPress: async () => { +// navigation.route.params?.onBackPress() +// navigation.navigation.goBack() +// }, +// }, +// ], +// {cancelable: true}, +// ); +// } else { +// navigation.route.params?.onBackPress() +// navigation.navigation.goBack() +// } +// } + +// return ( +// +// +// checkPendingTransaction()} +// menu={false} +// backscreen="MyProfile" +// navigation={navigation} +// /> +// +// +// +// Sales Invoice Number +// +// +// {transaction.trans_num || 455826} +// +// +// +// {transaction.station || 'APP'} +// +// +// +// Rate Your Experience +// +// {renderRatings(rating ? rating : transaction.rating, transaction.is_feedback_disabled || transaction.is_disabled)} +// +// transaction.is_feedback_disabled || transaction.is_disabled || ratesubmitted || rating == 0 ? null : onSubmit()} style={{width: '90%', padding: 12, alignItems: 'center', backgroundColor: rating == 0 || ratesubmitted ? "" : Theme.colors.primary, borderColor: Theme.colors.primary, borderWidth: 2, borderRadius: 10, marginTop: 15}}> +// Submit Rating +// +// {transaction.is_feedback_disabled ? null : +// { +// await REQUEST("contact_us", "get", {}, {}, {}, async (res) => { + +// if(res.status == 1 && res.data){ + +// openComposer({ +// to: res.data.contact_email_address_mobile, +// subject: `Mobile App Feedback:\nCN${cn}`, +// body: '' +// }) + +// }else{ +// console.log(res.message, res.data) +// } +// } +// ) +// }} style={{width: '90%', padding: 12, alignItems: 'center', marginTop: 15}}> +// Send Feedback +// } +// +// +// +// ); +// } \ No newline at end of file diff --git a/app/screens/myprofile/transaction/transaction.js b/app/screens/myprofile/transaction/transaction.js new file mode 100644 index 00000000..dd989f5c --- /dev/null +++ b/app/screens/myprofile/transaction/transaction.js @@ -0,0 +1,350 @@ +import * as React from 'react'; +import { useState, useEffect, useCallback, useContext } from 'react'; +import { connect } from "react-redux"; +import { SafeAreaView, ScrollView, Linking, Button, View, Text, TouchableOpacity, RefreshControl, Platform } from 'react-native'; +import {useNetInfo} from "@react-native-community/netinfo"; +// import NetInfo from "@react-native-community/netinfo"; +import NetInfo from "../../../components/netstatus"; +import CustomHeader from '../../../components/header.js'; +import Assets from '../../../components/assets.manager.js'; +import Theme from '../../../components/theme.style.js'; +import Icon from '../../../components/icons.js'; +import Elements from '../../../components/elements.js'; +import REQUEST from '../../../components/api/'; +import DB from '../../../components/storage/'; +import CustomSafeArea from '../../../components/safeArea.component'; + +class MyTransactions extends React.Component { + + constructor(props) { + super(props) + this.navigate = props.navigation.navigation.navigate + } + + state = { + connected: false, + loading: false, + transactions: [] + } + + componentDidMount() { + this.initTransactions() + console.log("WOOOOOOOW", this.props) + } + + componentWillUnmount() { + + } + + initTransactions = async () => { + NetInfo.netstatus(isConnected => { + if(isConnected){ + this.init() + } else { + Elements.nointernet2() + } + }) + } + + init = async () => { + const SESSION = await DB.session() + this.setState({ connected: true, loading: true }) + await REQUEST("transactions", "get", { + Authorization: SESSION.token, + card_number: SESSION.user.card_number + }, {}, {}, (data) => { + this.setState({ transactions: data.data, loading: false }) + }, (error) => { + this.setState({ loading: false }) + } + ) + } + + renderTransactions = () => { + return this.state.transactions.map((data, index) => { + return ( this.navigate("TransactionDetails", {data: data, onBackPress: () => this.init()})} + key={index} />) + }); + } + + render() { + if(!this.state.connected){ + return ( + + this.init()} + /> + + ) + } + + return ( + + this.init()} /> + }> + {this.state.transactions.length == 0 ? + + There's nothing here. + You have made no transactions yet. + + : + + + + Only your last 5 transactions can be viewed. For your complete transaction history please go to + { + let url = 'https://unioil.com/loyalty' + Linking.canOpenURL(url).then(supported => { + if (!supported) { + console.log('Cant handle url') + alert("URL Not Supported") + } else { + return Linking.openURL(url) + } + }).catch(err => { + console.error('An error occurred', err) + }) + }}> +  unioil.com/loyalty + + + + {this.renderTransactions()} + + } + + + + ); + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(MyTransactions) + +// export default function MyTransactions({navigation}) { + +// const [connected, setconnected] = useState(false) +// const navigate = navigation.navigation.navigate; +// const [loading, setloading] = useState(false); +// const [transactions, settransactions] = useState([]); + +// const init = async () => { + +// const SESSION = await DB.session() +// const USERPROFILE = await DB.profile() +// setconnected(true) +// setloading(true) +// await REQUEST("transactions", "get", { +// Authorization: SESSION.token, +// card_number: SESSION.user.card_number +// }, {}, {}, function(data){ +// settransactions(data.data) +// setloading(false) +// }, function(error){ +// console.log(error) +// }) +// console.log("transactions", transactions) +// } + +// useEffect(() => { +// NetInfo.fetch().then(state => { +// console.log("Connection type", state.type); +// console.log("Is connected?", state.isConnected); +// if(state.isConnected){ +// init() +// }else{ +// Elements.nointernet2() +// } +// }) +// }, []) + +// const renderTransactions = () => { +// return transactions.map((data, index) => { +// console.log(data) +// return navigate("TransactionDetails", {data: data, onBackPress: () => init()})} key={index} /> +// }); +// } + +// if(!connected){ +// return ( +// +// { +// NetInfo.fetch().then(state => { +// console.log("Connection type", state.type); +// console.log("Is connected?", state.isConnected); +// if(state.isConnected){ +// init() +// }else{ +// Elements.nointernet2() +// } +// }) +// }} +// /> +// +// ) +// } + +// return ( +// +// +// } +// > +// {/* */} +// {transactions.length == 0 ? +// +// There's nothing here. +// You have made no transactions yet. +// +// : +// +// +// +// Only your last 5 transactions can be viewed. For your complete transaction history please go to + +// { +// let url = 'https://unioil.com/loyalty' +// Linking.canOpenURL(url).then(supported => { +// if (!supported) { +// console.log('Cant handle url') +// alert("URL Not Supported") +// } else { +// return Linking.openURL(url) +// } +// }).catch(err => { +// console.error('An error occurred', err) +// }) +// }}> +//  unioil.com/loyalty +// +// + +// +// {renderTransactions()} +// +// } +// +// +// +// ); +// } + +/* + + + // const transactions = [ + // { + // transactionID: "12345", + // date: "13 Feb 2020, 04:27 PM", + // station: "APP", + // total: "5,000.00", + // earned: "5,000.00", + // redeemed: "0.00", + // rate: 1, + // items: [{ + // item: 'PREPAID POINTS', + // quantity: 1, + // price: '5,000.00' + // }] + // }, + // { + // transactionID: "1367", + // date: "13 Feb 2020, 01:57 PM", + // station: "UNIOIL-TESTER", + // total: "2,388.01", + // earned: "1.78", + // redeemed: "2,000.00", + // rate: 5, + // items: [{ + // item: "EUROS MOGAS91", + // quantity: 6.65, + // price: "45.10" + // },{ + // item: "UN MOTORSPORT 4T SJ 20W-50 (12X800)", + // quantity: 1, + // price: "134.00" + // },{ + // item: "UN Gmax SG/CD 20W-50 (6x4)", + // quantity: 1, + // price: "588.00" + // },{ + // item: "UN DELUX CH-4/CF/CF-2/SJ 15W-40 (6x4)", + // quantity: 1, + // price: "646.00" + // },{ + // item: "UN Gmax SG/CD 20W-50 ()", + // quantity: 6.65, + // price: "45.10" + // }] + // }, + // { + // transactionID: "23435", + // date: "13 Feb 2020, 04:27 PM", + // station: "APP", + // total: "5,000.00", + // earned: "5,000.00", + // redeemed: "0.00", + // rate: 2, + // items: [] + // }, + // { + // transactionID: "46457", + // date: "13 Feb 2020, 04:27 PM", + // station: "APP", + // total: "5,000.00", + // earned: "5,000.00", + // redeemed: "0.00", + // rate: 0, + // items: [] + // }, + // { + // transactionID: "78978", + // date: "13 Feb 2020, 04:27 PM", + // station: "APP", + // total: "5,000.00", + // earned: "5,000.00", + // redeemed: "0.00", + // rate: 0, + // items: [] + // }, + // { + // transactionID: "24234", + // date: "13 Feb 2020, 04:27 PM", + // station: "APP", + // total: "5,000.00", + // earned: "5,000.00", + // redeemed: "0.00", + // rate: 0, + // items: [] + // } + // ] + + +*/ \ No newline at end of file diff --git a/app/screens/onboard/index.js b/app/screens/onboard/index.js new file mode 100644 index 00000000..b6731cee --- /dev/null +++ b/app/screens/onboard/index.js @@ -0,0 +1,142 @@ +import * as React from 'react'; +import { View } from 'react-native'; +import { connect } from "react-redux"; +import { saveUserInfo } from "../../redux/actions/AppUserInfoActions"; +import NetInfo from "../../components/netstatus"; +import Carousel from '../../components/carousel'; +import Assets from '../../components/assets.manager.js'; +import DB from '../../components/storage'; +import Theme from '../../components/theme.style.js'; + + +class OnBoarding extends React.PureComponent { + + constructor(props) { + super(props) + } + + slides = [{ + image: Assets.boarding[0], + title: "Earn Rewards", + subtitle: "Earn points as fuel rebates or use points to redeem special offers from our partner merchants." + }, { + image: Assets.boarding[1], + title: "Locate Stations", + subtitle: "Find a Unioil station near you or check fuel prices and products offered through our station locator." + }, { + image: Assets.boarding[2], + title: "Top Up Your Card", + subtitle: "Load up your card with points to use on your next fuel purchases." + }, { + image: Assets.boarding[3], + title: "Track Vehicle Performance", + subtitle: "Monitor your fuel efficiency." + }] + + state = { + loading: true, + isGuest: false + } + + componentDidMount() { + this.init() + } + + componentWillUnmount() { + + } + + init = async () => { + let isGuest = await DB.get("is_guest") + this.setState({ isGuest: isGuest }) + if(!isGuest){ + NetInfo.netstatus(isConnected => { + if(isConnected) { + this.getUserInfo() + } + }) + } + } + + getUserInfo = async () => { + const SESSION = await DB.session() + this.props.saveUserInfo({ token: SESSION.token, card_number: SESSION.user.card_number }).then(data => { + if(data.status == 1) { + DB.updateProfile(data, res => { + this.setState({ loading: false }) + console.log("USER PROFILE SAVED ON ON BOARDING! ") + }, error => { + this.setState({ loading: false }) + console.log("Error saving profile", error) + }) + } + }) + .catch(error => {}) + } + + validateParamType = () => { + let object = this.props.route.params?.onBackToMain + return ({}.toString.call(object) === '[object Function]') + } + + navigateToMpin = async () => { + if(this.state.isGuest == 'true') { + this.props.navigation.navigate("Main") + } else { + const USER_PROFILE = await DB.profile() + const SESSION = await DB.session() + + let sessiondata = { + card_number: USER_PROFILE.data.cardnumber, + lcard_uuid: USER_PROFILE.data.lcard_uuid, + mobile_number: Theme.formatter.PMBL(USER_PROFILE.data.mobile) + } + + let data = { + sessiondata: sessiondata, + token: SESSION.token + } + let userDetails = { + mobile_number: sessiondata.mobile_number, + mpin: USER_PROFILE.data.mpin + } + if( + userDetails.mpin == undefined || + userDetails.mpin == null || + userDetails.mpin == "" + ) { + this.props.navigation.navigate("Setmpin", data) + DB.set('set_mpin', 'true', success => {}, error => {}) + } else { + DB.set('enter_mpin', 'true', success => {}, error => {}) + this.props.navigation.navigate("Mpin", userDetails) + } + } + } + + render() { + return ( + + this.validateParamType() ? this.props.route.params?.onBackToMain() : this.navigateToMpin()} + /> + + ) + } +} + +const mapStateToProps = (state) => { + return { + userinfo: state.appUserInfoReducer.userinfo + } +} + +const mapDispatchToProps = { + saveUserInfo +} + +export default connect(mapStateToProps, mapDispatchToProps)(OnBoarding) \ No newline at end of file diff --git a/app/screens/payatpump/fragments/map.js b/app/screens/payatpump/fragments/map.js new file mode 100644 index 00000000..bbf9a448 --- /dev/null +++ b/app/screens/payatpump/fragments/map.js @@ -0,0 +1,58 @@ +import React from 'react'; +import { StyleSheet } from 'react-native'; +import MapView, { Marker } from 'react-native-maps'; +import Theme from '../../../components/theme.style.js'; +import Assets from '../../../components/assets.manager.js'; + +const styles = { + mapContainer: { + ...StyleSheet.absoluteFillObject, + flex: 1, + marginTop: 60, + height: Theme.screen.h - 15, + width: Theme.screen.w, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + height: Theme.screen.h - 150, + width: Theme.screen.w, + } +} + +export default function RenderMap(props){ + + const defaultRegion = { + latitude: 14.599512, + longitude: 120.984222, + latitudeDelta: 12, + longitudeDelta: 6, + } + + return ( + + {props.markers.map((marker, i) => { + return ( + { + props.onMarkerClick ? props.onMarkerClick(marker) : console.log('marker is pressed', marker) + }} + /> + ) + })} + + ) +} diff --git a/app/screens/payatpump/fragments/searchbar.js b/app/screens/payatpump/fragments/searchbar.js new file mode 100644 index 00000000..929c5fe7 --- /dev/null +++ b/app/screens/payatpump/fragments/searchbar.js @@ -0,0 +1,22 @@ +import React from 'react'; +import {View, TextInput, TouchableOpacity} from 'react-native'; +import Theme from '../../../components/theme.style.js'; +import Icon from '../../../components/icons'; + +export default function SearchBar(props){ + return ( + + + + + + + + + {props.clear ? + + : null} + + + ) +} \ No newline at end of file diff --git a/app/screens/payatpump/fragments/stationdetailspanel.js b/app/screens/payatpump/fragments/stationdetailspanel.js new file mode 100644 index 00000000..9bd90029 --- /dev/null +++ b/app/screens/payatpump/fragments/stationdetailspanel.js @@ -0,0 +1,455 @@ +import React from 'react'; +import { useState, useEffect, useRef } from 'react'; +import { connect } from 'react-redux'; +import { + Text, + View, + TouchableOpacity, + Linking, + Platform, + Alert, + Modal, + FlatList, + Image, + ActivityIndicator +} from 'react-native'; +import { + List, + ListItem, + Left, + Right +} from 'native-base'; +import REQUEST_POST_PAY from '../../../components/api/postpayapi'; +import Assets from '../../../components/assets.manager.js'; +import BottomSheet from 'reanimated-bottom-sheet'; +import Theme from '../../../components/theme.style.js'; +import Icon from '../../../components/icons'; +import DB from '../../../components/storage/'; +import { NavigationApps,actions,googleMapsTravelModes, mapsTravelModes } from "../../../components/navigationapps"; +import Elements from '../../../components/elements.js'; + +const styles = { + container: { + flex: 1, + backgroundColor: '#f8f9fa', + alignItems: 'center', + justifyContent: 'center' + }, + panel: { + flex: 1, + backgroundColor: 'white', + position: 'relative' + }, + panelHeader: { + height: 60, + backgroundColor: Theme.colors.lightGray, + alignItems: 'center', + justifyContent: 'center' + }, + centeredView: { + flex: 1, + justifyContent: "center", + backgroundColor: '#00000090', + }, + modalView: { + margin: 25, + borderRadius: 15, + padding: 20, + alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2 + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5 + }, + selectedPumpBorder: { + backgroundColor: '#ffbaa3', + borderRadius: 10, + borderWidth: 2, + borderColor: Theme.colors.primary, + padding: 5 + }, + unselectedPumpBorder: { + borderRadius: 10, + borderWidth: 2, + padding: 5 + } +} + +const PayatpumpStationDetails = (props) => { + + const [panel, setpanel] = useState(null) + const [updateFavorite, setUpdateFavorite] = useState(0) + const [updateFavoriteVal, setUpdateFavoriteVal] = useState(false) + const [showModal, setShowModal] = useState(false) + const [error, setError] = useState(null) + const [selectedPump, setSelectedPump] = useState(null) + const [initiateCancel, setInitiateCancel] = useState(false) + const [initiateProceed, setInitiateProceed] = useState(false) + const [confirmProceed, setConfirmProceed] = useState(false) + var timerRef = null + + useEffect(() => { + if(props.visible){ + panel.show() + } + return setpanel(null) + }, [props.visible]) + + const renderStationDetails = (station, onPress) => { + let stars = [1,2,3,4,5].map((star, i) => { + let name = station.stars >= star ? "star" : "staro" + let mgn = i > 0 ? 5 : 0 + return ( + + + + ) + }) + + // filter the value of key pair if it is empty + var filteredAddress = Object.entries(station.address).filter(([key, value]) => { + if(key === "stateCode") return `${value.replace(/\s/g, '')}` + return `${value}` + }) + .map(([key, value]) => `${value}`) + + return ( + + + + {filteredAddress.join(', ').toString()} + + Contact: + + { + let url = Platform.OS == 'ios' ? 'telprompt:' : 'tel:' + Linking.canOpenURL(url).then(supported => { + if (!supported) { + console.log('Cant handle url') + alert("Call Not Supported") + } else { + Alert.alert("Call Customer Service", "You will be redirected to the dialer to call Unioil Customer Service", [ + { + text: 'Cancel', + style: 'cancel', + },{ + text: 'OK', onPress: () => Linking.openURL(`${url}${num}`) + } + ], + {cancelable: true}) + return true + } + }).catch(err => { + console.error('An error occurred', err) + }) + }}> + {station.phone} + + + + + + {stars} + + + { + if(props.data.fuelService?.fuelingPoints == undefined) { + Alert.alert("Error", "No pump available.") + } else { + setShowModal(true) + } + }} + style={{ backgroundColor: Theme.colors.primary, padding: 12, alignSelf: 'center', alignItems: 'center', justifyContent: 'center', borderRadius: 5 }}> + Pay at Pump + + + + + + + Fuel Prices + {/* Last Update: {station.latest_update} */} + + + {station.fuelService.fuelProducts != undefined && station.fuelService.fuelProducts.length > 0 ? ( + index.toString()} + data={station.fuelService.fuelProducts} + scrollEnabled={true} + showsVerticalScrollIndicator={false} + renderItem={({item, index}) => { + return ( + + + {item.name} + + + ₱ {item.creditPrice} + + + ) + }} /> + ) : ( + + No Current Data + + )} + + {PumpSelectorModalView()} + + ) + } + + const resetFlags = () => { + setShowModal(false) + setSelectedPump(null) + setInitiateCancel(false) + setInitiateProceed(false) + setConfirmProceed(false) + } + + const initiateTransaction = async () => { + const USER_PROFILE = await DB.profile(); + let store = props.data + let pump = selectedPump + + let params = { + storeId: store.storeId, + pump: pump.pumpNumber + } + + REQUEST_POST_PAY('postClaim', 'post', { + token: USER_PROFILE.data.auth_p97 + }, {}, params, async (res) => { + console.log(res) + if(res.success == undefined) { + resetFlags() + setTimeout(() => { + Alert.alert('Error', res.Message) + }, 300) + } else { + if(res.success) { + resetFlags() + props.onTransaction(res.response, store.storeId, selectedPump) + } else { + resetFlags() + setTimeout(() => { + Alert.alert('Error', res?.error.key == 'buy_gas_pump_unavailable' ? `Pump ${pump.pumpNumber} is not yet available. Try again.` : res?.error.key == 'pay_outside_petrozone_error' ? res.error.partnerApiMessage : 'Failed transaction. Try again.') + }, 300) + } + } + }, (error) => { + resetFlags() + setTimeout(() => { + Alert.alert('Error', res?.error.key == 'buy_gas_pump_unavailable' ? `Pump ${pump.pumpNumber} is not yet available. Try again.` : res?.error.key == 'pay_outside_petrozone_error' ? res.error.partnerApiMessage : 'Failed transaction. Try again.') + }, 300) + }) + } + + const PumpSelectorModalView = () => { + return ( + + + + {!initiateCancel && !confirmProceed && ( { + setInitiateCancel(true) + if(initiateProceed) { + setInitiateProceed(false) + } + }}> + + )} + {(!initiateCancel && !initiateProceed && !confirmProceed) && PumpListView(selectedPump)} + {(initiateCancel && !initiateProceed && !confirmProceed) && CancelPumpSelectorView()} + {(initiateProceed && !initiateCancel && !confirmProceed) && ProceedPumpSelectorView()} + {confirmProceed && ConfirmPumpSelectorView()} + + + + ) + } + + const CancelPumpSelectorView = () => { + return ( + <> + + Are you sure you want to cancel the transaction? + + + { + setShowModal(false) + setInitiateCancel(false) + setInitiateProceed(false) + setSelectedPump(null) + }} style={{ width: 100, height: 44, margin: 12, backgroundColor: Theme.colors.white, borderColor: Theme.colors.primary, borderWidth: 0.5, alignItems: 'center', justifyContent: 'center', borderRadius: 5 }}> + Cancel Transaction + + { + setInitiateCancel(false) + selectedPump != null ? setInitiateProceed(true) : setInitiateProceed(false) + }} style={{ width: 100, height: 44, margin: 12, backgroundColor: Theme.colors.primary, justifyContent: 'center', alignItems: 'center', borderRadius: 5 }}> + Continue Transaction + + + + ) + } + + const ProceedPumpSelectorView = () => { + let message = selectedPump != null ? `This will process your transaction at Pump ${selectedPump.pumpNumber}.` : "No selected pump." + return ( + <> + + + + + {message} + + + Please ensure that this is the correct pump and click Ok below to proceed. + + + { + setInitiateCancel(false) + setInitiateProceed(false) + }} + style={{ padding: 10, width: 100, backgroundColor: Theme.colors.primary, justifyContent: 'center', alignItems: 'center', borderRadius: 5 }}> + Ok + + + + ) + } + + const ConfirmPumpSelectorView = () => { + let message = selectedPump != null ? "Processing..." : "No selected pump." + return ( + <> + + + {/* + */} + + {message} + + + + ) + } + + + const PumpListView = (currentSelectedPump) => { + return ( + <> + Please select the pump + index.toString()} + data={props.data.fuelService?.fuelingPoints} + scrollEnabled={true} + showsVerticalScrollIndicator={false} + renderItem={({item, index}) => { + let available = item.pumpStatus == "Available" + return ( + { + setInitiateProceed(true) + setSelectedPump(item) + }} key={index} style={{ alignItems: 'center', justifyContent: 'center', margin: 20 }}> + {(currentSelectedPump != null && currentSelectedPump.pumpNumber == item.pumpNumber) ? ( + + + + ) : ( + + + + )} + {item.pumpNumber} + + ) + }} + numColumns={2} + /> + { + if(selectedPump != null) { + setConfirmProceed(true) + initiateTransaction() + } + }}> + Ok + + + ) + } + + const snapPoint = () => { + return props.data != null && props.data.fuelService?.fuelProducts != undefined && props.data.fuelService?.fuelProducts.length > 4 ? ['53', '53', '53'] : props.data != null && props.data.fuelService?.fuelProducts == undefined ? ['40', '40', '40'] : ['43', '43', '43'] + } + + return ( + + { + return props.data == null ? null : renderStationDetails(props.data, props.onClick) + }} + enabledBottomInitialAnimation={true} + enabledInnerScrolling={true} + initialSnap={0} + renderHeader={() => { + if(props.data == null) return null + return ( + + + + {props.data.storeName} + + + {}} activeOpacity={1} style={{top: -25}}> + + {(props.data && props.data.address != undefined && props.data.address != "") ? + } + actionSheetTitle="Choose an application to view the route" + address={props.data.address} // address to navigate by for all apps + waze={{ address: props.data.address, lat:props.data.latitude, lon:props.data.longitude, action: actions.navigateByLatAndLon}} // specific settings for waze + googleMaps={{ address: props.data.address, lat:'',lon:'',action: actions.navigateByAddress, travelMode:googleMapsTravelModes.driving}} // specific settings for google maps + maps={{ address: props.data.address, lat:'', lon:'',action: actions.navigateByAddress, travelMode:mapsTravelModes.driving}} /> // specific settings for maps + : null} + + Directions + + + + + ) + }}/> + + ); +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(PayatpumpStationDetails) diff --git a/app/screens/payatpump/fragments/stationpanel.js b/app/screens/payatpump/fragments/stationpanel.js new file mode 100644 index 00000000..f92c37d1 --- /dev/null +++ b/app/screens/payatpump/fragments/stationpanel.js @@ -0,0 +1,223 @@ +import React from 'react'; +import {useState, useEffect} from 'react'; +import {Divider} from 'react-native-elements'; +import { + Text, + View, + TouchableOpacity, + ActivityIndicator, + SafeAreaView, +} from 'react-native'; +import { connect } from 'react-redux'; +import BottomSheet from 'reanimated-bottom-sheet'; +import Theme from '../../../components/theme.style.js'; +import Icon from '../../../components/icons'; +import DB from '../../../components/storage'; +import REQUEST from '../../../components/api'; + +const styles = { + container: { + flex: 1, + backgroundColor: '#f8f9fa', + alignItems: 'center', + justifyContent: 'center', + }, + panel: { + flex: 1, + backgroundColor: '#fff', + }, + panelHeader: { + height: 60, + backgroundColor: Theme.colors.lightGray, + alignItems: 'center', + justifyContent: 'center', + }, +}; + +const renderStations = (data, onPress, onUpdateFavorite, props) => { + return data.map((station, index) => { + let stars = [1, 2, 3, 4, 5].map((star, i) => { + let name = station.stars >= star ? 'star' : 'staro'; + let mgn = index > 0 ? 5 : 0; + return ( + + + + ); + }); + + // filter the value of key pair if it is empty + var filteredAddress = Object.entries(station.address).filter(([key, value]) => { + if(key === "stateCode") return `${value.replace(/\s/g, '')}` + return `${value}` + }) + .map(([key, value]) => `${value}`) + + return ( + onPress(station) || null} + key={index} + style={{backgroundColor: props.app_theme?.theme.colors.background}}> + + + + {station.storeName} + + + {filteredAddress.join(', ').toString()} + + {stars} + + {/* updateFavorite(station, index, onUpdateFavorite)} + style={{flex: 0, justifyContent: 'center'}}> + + */} + + + + ); + }); +}; + +const updateFavorite = async (city, index, callback) => { + let session = await DB.session(); + let urlTask = city.favorite + ? 'station_delete_favorite' + : 'station_add_favorite'; + let method = city.favorite ? 'delete' : 'get'; + console.log(urlTask, method); + REQUEST( + urlTask, + method, { Authorization: session.token }, + { + noID: true, + value: city.station_uuid, + }, {}, + function (res) { + callback(index, city.favorite ? false : true); + }, + function (error) { + console.log(error); + }, + ); +}; + +const renderStationPanel = (props) => { + const [panel, setpanel] = useState(null); + const [updatedFavorite, setupdatedfavorite] = useState(false); + const [updatedFavoriteIndex, setupdatedfavoriteindex] = useState(null); + const [loading, setLoading] = useState(false); + const [snap, setSnap] = useState(['10', '10', '3']); + const [initialSnap, setInitialSnap] = useState(0) + + useEffect(() => { + init(); + }, [props.visible]); + + const init = () => { + if (props.visible == true) { + setSnap(['38', '8', '8']); + setInitialSnap(0) + } else if (props.data.length != 0) { + setSnap(['38', '8', '8']); + setInitialSnap(0) + } else { + setSnap(props.snapPoints); + setInitialSnap(2) + } + }; + + const getPanelTitle = () => { + if(props.error) return 'There was an error' + else if(props.loading || !props.isSearched && props.data.length == 0) return 'Getting Nearby Stations' + else if(props.isSearched) return 'Unioil Stations found: ' + (props.data.length > 0 ? props.data.length : 0) + else return 'Unioil Stations found: ' + (props.data.length > 0 ? props.data.length : 0) + } + + const renderHeaders = () => { + return ( + + + + + {getPanelTitle()} + + + {props.error ? ( + + Try Again + + ) : props.loading ? ( + + ) : null} + + + + + ); + }; + + const renderContents = () => { + return ( + + {props.data.length > 0 ? renderStations(props.data, props.onClick, props.onUpdateFavorite, props) : } + + ); + }; + + return ( + + = 3 ? snap : props.data.length < 2 ? ['23', '23', '23'] : ['13','13','6']} + renderContent={renderContents} + enabledBottomInitialAnimation={true} + enabledInnerScrolling={true} + initialSnap={0} + renderHeader={renderHeaders}/> + + ); +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(renderStationPanel) \ No newline at end of file diff --git a/app/screens/payatpump/index.js b/app/screens/payatpump/index.js new file mode 100644 index 00000000..1caf955f --- /dev/null +++ b/app/screens/payatpump/index.js @@ -0,0 +1,328 @@ +import React from 'react'; +import RNLocation from 'react-native-location'; +import { connect } from 'react-redux'; +import { + View, + Alert, + Image, + Linking, + Text, + TouchableOpacity, + SafeAreaView, + AppState +} from 'react-native'; +import { saveUserInfo } from "../../redux/actions/AppUserInfoActions"; +import Theme from '../../components/theme.style.js'; +import NetInfo from "../../components/netstatus"; +import CustomHeader from '../../components/header.js'; +import Elements from '../../components/elements.js'; +import DB from '../../components/storage'; +import REQUEST from '../../components/api'; +import REQUEST_POST_PAY from '../../components/api/postpayapi'; +import Assets from '../../components/assets.manager.js'; + +import MapFragment from '../payatpump/fragments/map.js'; +import PanelFragment from '../payatpump/fragments/stationpanel.js'; +import SearchFragment from '../payatpump/fragments/searchbar.js'; + +class Payatpump extends React.Component { + + constructor(props) { + super(props) + this.dataLoader = null + } + + state = { + connected: false, + loading: false, + error: false, + session: null, + map: null, + stationsViaCity: [], + stations: [], + markers: [], + searchValue: "", + showpanel: null, + mylocation: {}, + isSearched: false, + initialRegion: { + latitude: 14.599512, + longitude: 120.984222, + latitudeDelta: 15, + longitudeDelta: 11, + }, + appState: AppState.currentState, + permissionLocation: false + } + watchID = null + + componentDidMount() { + this._isMounted = true; + AppState.addEventListener('change', this._handleAppStateChange); + this.startTrackStation() + } + + componentWillUnmount() { + AppState.removeEventListener('change', this._handleAppStateChange) + } + + refreshProfileToken = async () => { + const SESSION = await DB.session() + this.props.saveUserInfo({ token: SESSION.token, card_number: SESSION.user.card_number }).then(res => { + DB.updateProfile(res, function(){}, function(error){}) + }) + } + + _handleAppStateChange = (nextAppState) => { + if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') { + RNLocation.requestPermission({ + ios: "whenInUse", // or 'always' + }).then(granted => { + if(granted) { + this.init() + } else { + this.setState({ permissionLocation: false }) + Alert.alert("Error", "Location GPS is disabled.") + } + }) + } + this.setState({ appState: nextAppState }); + } + + fetch = async (location, successCallback, errorCallback) => { + if(!location) return false + this.setState({ loading: true }) + + REQUEST_POST_PAY('getStores', 'get', {}, { + latitude: 14.580, //location.latitude, //uncomment this and replace the current value with this object + longitude: 121.026, //location.longitude, //uncomment this and replace the current value with this object + allowOutsidePayment: true + }, {}, async (res) => { + successCallback(res) + }, (error) => { + console.log(error) + errorCallback(error) + }) + } + + renderMarkers = (data) => { + if(data.length > 0) { + let datamap = data.map((station, index) => { + return { + _id: index, + latlng: {latitude: station.geoLocation.latitude, longitude: station.geoLocation.longitude}, + station_uuid: station.storeId, + station_number: station.storeNumber, + name: station.storeName, + address: Object.entries(station.address).map(([key, value]) => `${value}`).join(',').toString(), + longitude: station.geoLocation.longitude, + latitude: station.geoLocation.latitude, + store: station + } + }) + this.setState({ markers: datamap }) + } + } + + goToRegion = (data) => { + if(!data) return + let initialRegion = { + latitude: parseFloat(data.latitude), + longitude: parseFloat(data.longitude), + latitudeDelta: 0.0922, + longitudeDelta: 0.0421 + } + this.setState({ initialRegion: initialRegion }) + } + + navigate = (data) => { + this.props.navigation.navigate('PayatpumpDetails', {markers: this.state.markers, data: data, theme: this.props.app_theme, onBackPress: () => this.initLocationConfiguration(), location: this.state.mylocation}) + } + + startTrackStation = () => { + NetInfo.netstatus(isConnected => { + if(isConnected){ + this.initLocationConfiguration() + this.refreshProfileToken() + } else { + Elements.nointernet2() + } + }) + } + + initLocationConfiguration = () => { + this.setState({ connected: true }) + RNLocation.configure({ + // iOS Only + activityType: "other", + allowsBackgroundLocationUpdates: false, + headingFilter: 1, // Degrees + headingOrientation: "portrait", + pausesLocationUpdatesAutomatically: false, + showsBackgroundLocationIndicator: false, + }) + + RNLocation.requestPermission({ + ios: "whenInUse", // or 'always' + }).then(granted => { + if(granted) { + this.init() + } else { + Alert.alert("Error", "Location GPS is disabled.") + } + }) + } + + init = async () => { + this.setState({ permissionLocation: true }) + RNLocation.getLatestLocation({ timeout: 20000 }).then(latestLocation => { + if(latestLocation != undefined) { + const coords = {longitude: latestLocation.longitude, latitude: latestLocation.latitude} + this.setState({ mylocation: coords, error: false }) + this.goToRegion(coords) + this.fetch(coords, response => { + if(response.success){ + this.setState({ showpanel: true, stations: response.response.data, loading: false }) + this.renderMarkers(response.response.data) + this.goToRegion({longitude: response.response.data[0].geoLocation.longitude, latitude: response.response.data[0].geoLocation.latitude}) + } else alert('Internal server error') + }, error => { + this.setState({ error: false }) + }) + } + }) + } + + stationViaCity = async (city) => { + this.setState({ searchValue: city.name, loading: true }) + let SESSION = await DB.session() + REQUEST("gas_stations_city", "get", { + Authorization: SESSION.token + }, { + noID: true, + value: city.city_uuid + }, {}, (res) => { + if(res.data.length > 0){ + this.setState({ error: false, showpanel: true, isSearched: true, loading: false, stationsViaCity: res.data }) + this.goToRegion(res.data[0]) + } else if(res.message == 'Empty') { + this.setState({ loading: false, isSearched: true, stationsViaCity: [] }) + } else alert('Internal server error') + }, (error) => { + this.setState({ error: false, loading: false }) + }) + } + + getFavorites = async () => { + this.setState({ searchValue: "My Favorites", loading: true }) + let SESSION = await DB.session() + REQUEST("station_favorites", "get", { + Authorization: SESSION.token + }, {}, {}, (res) => { + if(res.data){ + this.setState({ error: false, showpanel: true, stationsViaCity: res.data, loading: false }) + this.goToRegion(res.data[0]) + } else alert('Internal server error') + }, (error) => { + this.setState({ loading: false }) + }) + } + + render() { + if(!this.state.connected){ + return ( + + this.props.navigation.goBack()} navigation={this.props.navigation} /> + this.startTrackStation()} + /> + + ) + } + + if(!this.state.permissionLocation){ + return ( + + this.props.navigation.goBack()} navigation={this.props.navigation} /> + + + + This function needs your permission, please allow access. + Linking.openSettings()} + style={{ padding: 15, backgroundColor: Theme.colors.primary, borderRadius: 5 }}> + Allow on settings + + + + + ) + } + + return ( + + this.props.navigation.goBack()} navigation={this.props.navigation} /> + + this.goToRegion(this.state.stations.length > 0 ? this.state.stations[0] : null)} + onMarkerClick={(data) => this.navigate(data)} + /> + {/* this.setState({ searchValue: e.target.value })} + onFocus={() => { + this.props.navigation.navigate("PayatpumpStationSearch", { + value: this.state.searchValue, + onBackPress: (value, city, isFavorite) => { + if(isFavorite){ + this.getFavorites() + } else { + this.stationViaCity(city) + } + } + }) + }} + onClear={() => { + this.setState({ searchValue: "", stationsViaCity: [] }) + this.goToRegion(this.state.stations[0]) + }} + /> */} + + 0 ? this.state.stationsViaCity : this.state.stations} + loading={this.state.loading} + isSearched={this.state.isSearched} + onClick={(data) => this.navigate(data)} + snapPoints={['13', '10', '10']} + onUpdateFavorite={(index, update) => { + let updatedStations = this.state.stations + updatedStations[index].favorite = update + this.setState({ loading: true, stations: updatedStations }) + }} + error={this.state.error && this.state.stations.length == 0 || this.state.error && this.state.stationsViaCity.length == 0} + onError={() => this.initLocationConfiguration()}/> + + ) + } +} + +const mapStateToProps = (state) => { + return { + userinfo: state.appUserInfoReducer.userinfo, + app_theme: state.appThemeReducer.theme + } +} + +const mapDispatchToProps = { + saveUserInfo +} + +export default connect(mapStateToProps, mapDispatchToProps)(Payatpump) \ No newline at end of file diff --git a/app/screens/payatpump/pumppaymentlist.js b/app/screens/payatpump/pumppaymentlist.js new file mode 100644 index 00000000..fda4e7c8 --- /dev/null +++ b/app/screens/payatpump/pumppaymentlist.js @@ -0,0 +1,299 @@ +import * as React from 'react'; +import { connect } from "react-redux"; +import { + SafeAreaView, + TouchableOpacity, + View, + Text, + FlatList, + Image, + StyleSheet, + Modal +} from 'react-native'; +import REQUEST_POST_PAY from '../../components/api/postpayapi'; +import Icon from '../../components/icons.js'; +import CustomHeader from '../../components/header.js'; +import Theme from '../../components/theme.style.js'; +import Assets from '../../components/assets.manager.js'; +import Elements from '../../components/elements.js'; +import DB from '../../components/storage/'; + + +class PayatpumpPaymentList extends React.Component { + + constructor(props) { + super(props) + } + + state = { + cards: [], + defaultCardIndex: -1, + defaultSelectedCard: null, + showModal: false, + deleteCard: null, + loading: false, + isShowSelectionCard: this.props.route.params?.displaySelectionCard + } + + componentDidMount() { + this.getFunding() + } + + componentWillUnmount() { + + } + + getFunding = async (defaultCard = null, enableDefaultCard = false) => { + this.setState({ loading: true }) + const USER_PROFILE = await DB.profile() + REQUEST_POST_PAY('getFunding', 'post', { + token: USER_PROFILE.data.auth_p97 + }, {}, {}, async (res) => { + this.setState({ loading: false }) + if(res.success == undefined) { + setTimeout(() => { + Alert.alert('Error', res.Message) + }, 300) + } else { + if(res.success) { + let wallets = res.response.wallets.map(item => { + let wallet = item.wallets.map(walletItem => { + let fundingProviderName = item.fundingProviderName + return {...walletItem, fundingProviderName} + }) + return wallet + }) + var mergedWallets = [].concat.apply([], wallets) + let defaultIndex = -1 + let selectedDefaultCard = null + if(enableDefaultCard) { + defaultIndex = mergedWallets.findIndex(item => item.userPaymentSourceId === defaultCard.userPaymentSourceId) + selectedDefaultCard = mergedWallets[defaultIndex] + } + this.setState({ cards: mergedWallets, defaultCardIndex: defaultIndex, defaultSelectedCard: selectedDefaultCard, deleteCard: null }) + } else { + setTimeout(() => { + Alert.alert('Error', 'Failed to get fund. Try again.') + }, 300) + } + } + }, (error) => { + this.setState({ loading: false }) + setTimeout(() => { + Alert.alert('Error', 'Failed to get fund. Try again.') + }, 300) + }) + } + + deleteCard = async () => { + this.setState({ loading: true, showModal: false }) + const USER_PROFILE = await DB.profile() + REQUEST_POST_PAY('deleteCreditCard', 'post', { + token: USER_PROFILE.data.auth_p97 + }, {}, { + userPaymentSourceId: this.state.deleteCard.userPaymentSourceId + }, async (res) => { + this.setState({ loading: false }) + if(res.success == undefined) { + setTimeout(() => { + Alert.alert('Error', res.Message) + }, 300) + } else { + if(res.success) { + let wallets = this.state.cards.filter(item => { + return item.userPaymentSourceId != this.state.deleteCard.userPaymentSourceId + }) + .map(item => { + return item + }) + this.setState({ cards: wallets, defaultCardIndex: -1, defaultSelectedCard: null, deleteCard: null }) + } else { + setTimeout(() => { + Alert.alert('Error', 'Failed to get fund. Try again.') + }, 300) + } + } + }, (error) => { + this.setState({ loading: false }) + setTimeout(() => { + Alert.alert('Error', error) + }, 300) + }) + } + + submitPaymentCard = async () => { + if(this.state.defaultSelectedCard == null) { + this.setState({ showModal: true }) + return + } + this.props.route?.params.onSelectedPaymentCard(this.state.defaultSelectedCard) + this.props.navigation.goBack() + } + + onAddNewCard = (response, enableDefaultCard) => { + this.getFunding(response, enableDefaultCard) + } + + renderDeleteErrorModal = () => { + return ( + + {}} style={styles.centeredView}> + + {this.state.deleteCard != null ? ( + <> + {'Are you sure you want to delete this card?'} + + this.setState({ showModal: false })} style={{ width: 100, height: 30, backgroundColor: Theme.colors.primary, alignItems: 'center', justifyContent: 'center', borderRadius: 5, marginRight: 10 }}> + No + + this.deleteCard()} style={{ width: 100, height: 30, backgroundColor: Theme.colors.white, alignItems: 'center', justifyContent: 'center', borderRadius: 5, borderColor: Theme.colors.primary, borderWidth: 0.5, marginLeft: 10 }}> + Yes + + + + ) : ( + <> + + {'Please Select Card...'} + this.setState({ showModal: false })} style={{ width: 80, height: 30, backgroundColor: Theme.colors.primary, alignItems: 'center', justifyContent: 'center', borderRadius: 5 }}> + Ok + + + )} + + + + ) + } + + renderCardList = () => { + return ( + index.toString()} + data={this.state.cards} + scrollEnabled={true} + showsVerticalScrollIndicator={false} + renderItem={({item, index}) => { + return ( + + this.setState({ defaultCardIndex: this.state.isShowSelectionCard ? -1 : this.state.defaultCardIndex == index ? -1 : index, defaultSelectedCard: this.state.defaultCardIndex == index ? null : item })} key={index} style={{ flex: 1, alignItems: 'center', justifyContent: 'center', flexDirection: 'row', backgroundColor: this.props.app_theme?.theme.colors.background, padding: 12 }}> + + + {`**** **** **** ${item.lastFour}`} + + + {this.state.defaultCardIndex == index && } + this.setState({ deleteCard: item, showModal: true })} + style={{ paddingVertical: 10 }}> + + + + + + ) + }} + numColumns={1} + /> + ) + } + + render() { + return ( + + this.props.navigation.goBack()} + back={true} + menu={false} + navigation={this.props.navigation} + /> + + + + + Enroll Card + + + + + + { + this.setState({ }) + this.props.navigation.navigate('PayatpumpPaymentMethod', { onAddNewCard: (newcard, enableDefaultCard) => this.onAddNewCard(newcard, enableDefaultCard)}) + }} + style={{flexDirection: 'row', padding: 13}}> + + + + + Add New Card + + + + + Select preferred Card + + + + + + + + {this.renderDeleteErrorModal()} + {this.renderCardList()} + + {!this.state.isShowSelectionCard && ( + + this.submitPaymentCard()} + style={{ backgroundColor: Theme.colors.primary, padding: 15, borderRadius: 5, marginHorizontal: 25, marginBottom: 10 }}> + Confirm + + + )} + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(PayatpumpPaymentList) + + +const styles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: "center", + backgroundColor: '#00000090', + }, + modalView: { + margin: 25, + backgroundColor: "white", + borderRadius: 15, + padding: 20, + alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2 + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5 + }, +}) \ No newline at end of file diff --git a/app/screens/payatpump/pumppaymentmethod.js b/app/screens/payatpump/pumppaymentmethod.js new file mode 100644 index 00000000..f8059e57 --- /dev/null +++ b/app/screens/payatpump/pumppaymentmethod.js @@ -0,0 +1,484 @@ +import * as React from 'react'; +import { connect } from "react-redux"; +import { + SafeAreaView, + TouchableOpacity, + View, + Text, + Image, + Switch, + StyleSheet, + Modal, + Alert, + TextInput +} from 'react-native'; +import { WebView } from 'react-native-webview'; +import moment from 'moment'; +import cardValidator from 'card-validator'; +import REQUEST_POST_PAY from '../../components/api/postpayapi'; +import CustomHeader from '../../components/header.js'; +import Theme from '../../components/theme.style.js'; +import Elements from '../../components/elements.js'; +import Assets from '../../components/assets.manager.js'; +import Cipher from '../../components/cardencrypt/encryptcard'; +import DB from '../../components/storage/'; + +class PayatpumpPaymentMethodList extends React.Component { + + constructor(props) { + super(props) + } + + state = { + cards: [], + cardNumber: null, + cardHolder: null, + cardExpiry: null, + cardCVV: null, + focus_holder: null, + focus_expiry: false, + focus_cvv: false, + enableDefaultCard: false, + showModal: false, + pubkey: null, + verificationUrl: null + } + + componentDidMount() { + this.getWalletKey() + } + + componentWillUnmount() { + + } + + getWalletKey = () => { + REQUEST_POST_PAY('getWalletPublicKey', 'get', {}, {}, {}, async (res) => { + this.setState({ loading: false }) + if(res.success == undefined) { + setTimeout(() => { + Alert.alert('Error', res.Message) + }, 300) + } else { + if(res.success) { + if(res.response.result) { + this.setState({ pubkey: res.response.publicKey }) + } + } else { + setTimeout(() => { + Alert.alert('Error', 'Failed to get fund. Try again.') + }, 300) + } + } + }, (error) => { + this.setState({ loading: false }) + setTimeout(() => { + Alert.alert('Error', 'Failed to get fund. Try again.') + }, 300) + }) + } + + addCard = async (cardDetails, publicKey, cardHolder, onSuccess, onError) => { + Cipher.encryptCard(cardDetails, publicKey) + .then(async (encrypted) => { + if(encrypted) { + let params = { + encBlock: { + encCardData: encrypted + }, + paymentType: "CREDIT" + } + const USER_PROFILE = await DB.profile() + REQUEST_POST_PAY('addCreditCard', 'post', { + token: USER_PROFILE.data.auth_p97 + }, {}, params, (res) => { + console.log(res) + if(res.success == undefined) { + onError(res.Message) + } else { + if(res.success) { + if(res.response.result) { + this.addCardProviderData(res.response.referenceNumber, cardHolder, onSuccessResponse => onSuccess(onSuccessResponse), onErrorResponse => onError(onErrorResponse)) + } + } else { + onError('Failed to add new card. Try again.') + } + } + // if(res.success == undefined) { + // onError(res.Message) + // } else { + // if(res.success) { + // if(res.response.result) { + // onSuccess({ userPaymentSourceId: res.response.userPaymentSourceId, referenceNumber: res.response.referenceNumber }) + // } + // } else { + // onError('Error', 'Failed to get fund. Try again.') + // } + // } + }, (error) => { + onError('Error', error) + }) + } + }) + } + + addCardProviderData = async (referenceNumber, cardHolder, onSuccess, onError) => { + let params = { + referenceNumber: referenceNumber, + providerData: { + nickName: "Paymaya Visa", + firstName: cardHolder + } + } + + const USER_PROFILE = await DB.profile() + REQUEST_POST_PAY('addCreditCard', 'post', { + token: USER_PROFILE.data.auth_p97 + }, {}, params, (res) => { + console.log(res) + if(res.success == undefined) { + onError(res.Message) + } else { + if(res.success) { + if(res.response.result) { + onSuccess({ referenceNumber: res.response.referenceNumber, verificationUrl: res.response.stepWiseResponsePayload.verificationUrl }) + } + } else { + onError('Failed to add new card. Try again.') + } + } + }, (error) => { + onError(error) + }) + } + + _handlingCardNumber = (number) => { + var value = number.replace(/\s+/g, '').replace(/[^0-9]/gi, '') + var matches = value.match(/\d{4,16}/g); + var match = matches && matches[0] || '' + var parts = [] + for (let i = 0, len=match.length; i { + this.setState({ cardHolder: name }) + } + + _handlingCardExpiry = (number) => { + if (number.indexOf('.') >= 0 || number.length > 5) return + if (number.length === 2 && this.state.cardExpiry.length === 1) number += '/' + this.setState({ cardExpiry: number }); + } + + _handlingCVV = (number) => { + if (number.indexOf('.') >= 0 || number.length > 3) return + this.setState({ cardCVV: number }); + } + + _onAuthenticationSuccess = async (referenceNumber) => { + let params = { + referenceNumber: referenceNumber, + } + + this.setState({ loading: true }) + const USER_PROFILE = await DB.profile() + REQUEST_POST_PAY('addCreditCard', 'post', { + token: USER_PROFILE.data.auth_p97 + }, {}, params, (res) => { + console.log(res) + if(res.success == undefined) { + this.setState({ loading: false }) + setTimeout(() => { + Alert.alert('Error', res.Message) + }, 300) + } else { + this.setState({ loading: false }) + if(res.success) { + setTimeout(() => { + this.props.route?.params.onAddNewCard({ userPaymentSourceId: res.response.userPaymentSourceId, referenceNumber: res.response.referenceNumber }, this.state.enableDefaultCard) + this.props.navigation.goBack() + }, 500); + } else { + setTimeout(() => { + Alert.alert('Error', res.Message) + }, 300) + } + } + }, (error) => { + this.setState({ loading: false }) + setTimeout(() => { + Alert.alert('Error', error) + }, 300) + }) + } + + submitNewAddedCard = () => { + if(this.state.cardNumber == null || this.state.cardExpiry == null || this.state.cardCVV == null || this.state.cardHolder == null) { + this.setState({ showModal: true }) + return + } + + let numberYear = moment(new Date()).format('YYYY').slice(0, 2) //get initial 2 digits of year + let cardnumber = this.state.cardNumber.replace(/\s/g, "") + let cardHolder = this.state.cardHolder + let cardExpiry = `${this.state.cardExpiry.split("/")[0]}${numberYear}${this.state.cardExpiry.split("/")[1]}` + let cardCvv = this.state.cardCVV + + if(cardValidator.number(cardnumber)?.card?.type != undefined && cardValidator.number(cardnumber)?.card?.type !== "visa") { + Alert.alert('Error', `${cardValidator.number(cardnumber)?.card?.type.slice(0,1).toUpperCase() + cardValidator.number(cardnumber).card.type.slice(1, cardValidator.number(cardnumber).card.type.length)} in not available. Please use different card type.`) + return + } else { + if(cardValidator.number(cardnumber)?.card?.type == undefined) { + Alert.alert('Error', "Invalid card number. Please check and try again.") + return + } + } + + if(!cardValidator.expirationDate(cardExpiry).isValid) { + Alert.alert('Error', "Invalid expiration date. Please check and try again.") + return + } + + if(!cardValidator.cvv(cardCvv).isValid) { + Alert.alert('Error', "Invalid cvv number. Please check and try again") + return + } + + + let cardDetails = { pan: cardnumber, expDate: cardExpiry, cvv: cardCvv } + this.setState({ loading: true }) + this.addCard(JSON.stringify(cardDetails), this.state.pubkey, cardHolder, onSuccess => { + this.setState({ loading: false }) + setTimeout(() => { + this.props.navigation.navigate('VerificationWebview', {...onSuccess, onSuccessAuthentication:(referenceNumber) => this._onAuthenticationSuccess(referenceNumber)}) + }, 500); + }, + error => { + this.setState({ loading: false }) + setTimeout(() => { + Alert.alert('Error', error) + }, 300) + }) + } + + onBackConfirmation = () => { + Alert.alert( + '', + 'Are you sure you want to cancel adding card?', + [ + { + text: 'Cancel', + style: 'cancel', + }, + { + text: 'OK', + onPress: () => this.props.navigation.goBack(), + }, + ], + {cancelable: true}, + ); + } + + onStateChange = (webviewState) => { + console.log(webviewState) + } + + renderWebview = () => { + return ( + this.onStateChange(webViewState)}/> + ) + } + + renderErrorModal = () => { + return ( + + {}} style={styles.centeredView}> + + <> + + {'Please Fill in the required fields'} + this.setState({ showModal: false })} style={{ width: 80, height: 30, backgroundColor: Theme.colors.primary, alignItems: 'center', justifyContent: 'center', borderRadius: 5 }}> + Ok + + + + + + ) + } + + renderCardForm = () => { + return ( + <> + + Enroll Your Payment Card + + + + + Enroll Your Payment Card + + + this.setState({ focus_expiry: true, focus_cvv: false })} + onChangeText={(value) => this._handlingCardNumber(value)} + style={{ flex: 1, color: this.props.app_theme?.theme.colors.text, fontStyle: 'italic' }} + /> + + + + + + Name on Card + + + this.setState({ focus_expiry: true, focus_cvv: false })} + onChangeText={(value) => this._handlingCardHolder(value)} + style={{ flex: 1, color: this.props.app_theme?.theme.colors.text }} + /> + + + + + Expiration (MM/YY) + CVV + + + + this.setState({ focus_expiry: true, focus_cvv: false })} + onChangeText={(value) => this._handlingCardExpiry(value)} + style={{ flex: 1, color: this.props.app_theme?.theme.colors.text, marginRight: 3 }} + /> + + + this.setState({ focus_expiry: true, focus_cvv: false })} + onChangeText={(value) => this._handlingCVV(value)} + style={{ flex: 1, color: this.props.app_theme?.theme.colors.text, marginLeft: 3 }} + /> + + + + + ) + } + + render() { + return ( + + this.onBackConfirmation()} + back={true} + menu={false} + navigation={this.props.navigation} + /> + + {this.renderErrorModal()} + {this.renderCardForm()} + {this.state.verificationUrl != null && this.renderWebview()} + + + Your card will be changed to ensure that it's valid. + Charge amount will be automatically refunded. + + this.submitNewAddedCard()} + style={{ height: 55, marginBottom: 16, backgroundColor: Theme.colors.primary, justifyContent: 'center', alignItems: 'center', borderRadius: 5, marginHorizontal: 16 }}> + Save + + + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(PayatpumpPaymentMethodList) + + +const styles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: "center", + backgroundColor: '#00000090', + }, + modalView: { + margin: 25, + backgroundColor: "white", + borderRadius: 15, + padding: 20, + alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2 + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5 + }, + cardContainer: { + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + borderBottomWidth: 1, + borderBottomColor: Theme.colors.gray, + height: 40, + borderRadius: 5, + marginVertical: 10, + marginHorizontal: 16 + }, + expirationCvvContainer: { + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + borderBottomWidth: 1, + borderBottomColor: Theme.colors.gray, + height: 40, + borderRadius: 5, + marginVertical: 10 + } +}) \ No newline at end of file diff --git a/app/screens/payatpump/pumppaymentsuccess.js b/app/screens/payatpump/pumppaymentsuccess.js new file mode 100644 index 00000000..885cece0 --- /dev/null +++ b/app/screens/payatpump/pumppaymentsuccess.js @@ -0,0 +1,126 @@ +import * as React from 'react'; +import { connect } from "react-redux"; +import { + SafeAreaView, + TouchableOpacity, + View, + Text, + Keyboard, + ScrollView, + TextInput, + Dimensions +} from 'react-native'; +import Theme from '../../components/theme.style.js'; + + +class PayatpumpPaymentSuccess extends React.Component { + + constructor(props) { + super(props) + } + + state = { + email: null, + keyboardHeight: 0, + data: this.props.route?.params?.transactionData + } + + componentDidMount() { + this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow) + this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide) + } + + componentWillUnmount() { + this.keyboardDidShowListener.remove() + this.keyboardDidHideListener.remove() + } + + _keyboardDidShow = (value) => { + this.setState({ keyboardHeight: value.endCoordinates.height - 120 }) + } + + _keyboardDidHide = () => { + this.setState({ keyboardHeight: 0 }) + } + + render() { + return( + + + + + + + + + + + {"Your transaction is successful!" + "\n" + "Payment is credited to"} + + + {`${this.state.data.storeName}`} + + + + + Transaction Date + 07 Oct 2020, 12:00 PM + + + Sales Invoice No. + {this.state.data?.transactionId} + + + + + Total Payment + {`${'\u20B1'}${this.state.data?.totalPayment}`} + + + Card Payment + {`${'\u20B1'}${this.state.data?.cardPayment}`} + + + Points + {`${'\u20B1'}${0}`} + + + Earned Points + {`${'\u20B1'}${0}`} + + + + + Send receipt to Email + this.setState({ email: value })} + style={{ width: Dimensions.get('screen').width - 60, height: 44, textAlign: 'center', fontSize: 18, borderWidth: 0.5, borderRadius: 5, color: this.props.app_theme?.theme.colors.text, borderColor: this.props.app_theme?.theme.colors.text }} + /> + + + + this.props.navigation.navigate('Main')} + style={{ height: 50, backgroundColor: Theme.colors.primary, justifyContent: 'center', alignItems: 'center', margin: 16, borderRadius: 5 }}> + Ok + + + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(PayatpumpPaymentSuccess) \ No newline at end of file diff --git a/app/screens/payatpump/pumppoinstdetails.js b/app/screens/payatpump/pumppoinstdetails.js new file mode 100644 index 00000000..c8ed3293 --- /dev/null +++ b/app/screens/payatpump/pumppoinstdetails.js @@ -0,0 +1,219 @@ + +import * as React from 'react'; +import { connect } from "react-redux"; +import { + SafeAreaView, + TouchableOpacity, + View, + Text, + FlatList, + Alert, + StyleSheet, + Modal, + Image +} from 'react-native'; +import { Input } from 'react-native-elements'; +import CustomHeader from '../../components/header.js'; +import Theme from '../../components/theme.style.js'; +import Assets from '../../components/assets.manager.js'; + + +class PayatpumpPointsDetailsInput extends React.Component { + + constructor(props) { + super(props) + } + + state = { + displaySelectedAmount: "", + selectedAmount: 0, + focus: false, + showModal: false, + balance: this.props.route.params?.totalPointsBalance + } + + componentDidMount() { + console.log(this.props) + } + + componentWillUnmount() { + + } + + onAmountChange = (value) => { + // console.log(Number(value).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,'), this.state.selectedAmount) + this.setState({ displaySelectedAmount: value, selectedAmount: parseFloat(value) }) + } + + renderErrorModal = () => { + return ( + + {}} style={styles.centeredView}> + + {this.state.selectedAmount == 0 ? + <> + + {'Please add Points...'} + this.setState({ showModal: false, selectedAmount: 0 })} style={{ width: 80, height: 30, backgroundColor: Theme.colors.primary, alignItems: 'center', justifyContent: 'center', borderRadius: 5 }}> + Ok + + : + + {`Are you sure you want to redeem ${"\u20B1"}${parseFloat(this.state.selectedAmount).toFixed(2)}?`} + + { + this.setState({ showModal: false }) + }}> + No + + { + this.setState({ showModal: false }) + this.props.route.params.onSelectedPointsAmount(this.state.selectedAmount) + this.props.navigation.goBack() + }}> + Yes + + + + } + + + + ) + } + + renderSelectAmount = () => { + let amounts = ['50', '100', '300', '500'] + return ( + <> + + Select default value + + + index.toString()} + data={amounts} + scrollEnabled={false} + showsVerticalScrollIndicator={false} + renderItem={({item, index}) => { + return ( + + this.onAmountChange(item)} key={index} style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: Theme.colors.white, padding: 10, margin: 8, borderColor: Theme.colors.primary, borderWidth: 2, borderRadius: 5 }}> + {item} + + + ) + }} + numColumns={2} + /> + + + ) + } + + renderDisplayAmountSelected = () => { + return ( + + Or enter desired value to redeem. + this.setState({ focus: true })} + onChangeText={(value) => this.onAmountChange(value)} + containerStyle={{padding: 0}} + inputContainerStyle={{ padding: 0, marginHorizontal: 20, borderBottomWidth: this.state.focus ? 1.75 : 1, borderColor: this.state.focus ? Theme.colors.accent : "gray" }} + inputStyle={{ padding: 0, textAlign: 'center', fontSize: 18, fontWeight: 'bold', color: this.props.app_theme?.theme.colors.text }} + /> + + ) + } + + renderSubmitSelectedPoints = () => { + return ( + + Ensure correct amount before you proceed. + + this.submitPoints()}> + Confirm + + + + ) + } + + submitPoints = () => { + if(this.state.selectedAmount > parseFloat(this.state.balance)) { + Alert.alert( + 'Error', + 'The desired amount of points is greater than the remaining points balance.', + [ + { + text: 'Ok', + style: 'cancel', + } + ], + {cancelable: true}, + ); + return + } + this.setState({ showModal: true }) + } + + render() { + return ( + + this.props.navigation.goBack()} + back={true} + menu={false} + navigation={this.props.navigation} + /> + {this.renderErrorModal()} + {this.renderSelectAmount()} + {this.renderDisplayAmountSelected()} + {this.renderSubmitSelectedPoints()} + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(PayatpumpPointsDetailsInput) + +const styles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: "center", + backgroundColor: '#00000090', + }, + modalView: { + margin: 25, + backgroundColor: "white", + borderRadius: 15, + padding: 20, + alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2 + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5 + }, +}) \ No newline at end of file diff --git a/app/screens/payatpump/pumpstationtransactiondetails.js b/app/screens/payatpump/pumpstationtransactiondetails.js new file mode 100644 index 00000000..ea781558 --- /dev/null +++ b/app/screens/payatpump/pumpstationtransactiondetails.js @@ -0,0 +1,434 @@ +import * as React from 'react'; +import moment from 'moment'; +import { connect } from "react-redux"; +import { Divider } from 'react-native-elements' +import { + SafeAreaView, + TouchableOpacity, + View, + Text, + Alert, + Platform, + ScrollView, + Image, + Modal, + StyleSheet +} from 'react-native'; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Theme from '../../components/theme.style.js'; +import Icon from '../../components/icons.js'; +import REQUEST_POST_PAY from '../../components/api/postpayapi'; +import DB from '../../components/storage'; +import Elements from '../../components/elements'; + + +class PayatpumpStationTransactionDetails extends React.Component { + + constructor(props) { + super(props) + } + + state = { + loading: false, + session: null, + card_number: null, + selectedPoints: null, + selectedPaymentCard: null, + claimId: null, + data: [], + storeName: null, + showModal: false, + totalPrice: 3000 + } + + componentDidMount() { + this.init() + } + + componentWillUnmount() { + + } + + init = async () => { + let session = await DB.session() + let user_profile = await DB.profile() + this.setState({ + card_number: user_profile.data.card_number, + session: session, + claimId: this.props.route.params.response.claimId, + data: this.props.route.params.response.saleItems, + storeName: this.props.route.params.storeName, + selectedPumpNumber: this.props.route.params.selectedPump + }) + console.log(this.props) + } + + onSelectedPointsAmount = (point_amount) => { + this.setState({ selectedPoints: point_amount }) + } + + onSelectedPaymentCard = (payment_card) => { + console.log(payment_card) + this.setState({ selectedPaymentCard: payment_card }) + } + + getTransactionDetails = async (claimId, fundingProviderName, userPaymentSourceId, onSuccess, onError) => { + if(claimId == null) { + Alert.alert("Error", "No transaction id exist.") + return + } + + let length = 8 + let partnerTransactionId = parseInt((Math.random() * 9 + 1) * Math.pow(10, length-1), 10) + let params = { + claimId: claimId, + partnerTransactionId: partnerTransactionId, + walletRequest: { + fundingProviderName: fundingProviderName, //change this value later + fundingPayload: { + userPaymentSourceId: userPaymentSourceId //change this value later + } + }, + userPreference: { + receiptPreference: "doNotPrintReceipt", //printReceipt, doNotPrintReceipt + emailReceiptPreference: true + }, + loyaltyPassthroughInformation: { + loyaltyInfo: [] + }, + fraudContext: {} + } + + let USER_PROFILE = await DB.profile() + REQUEST_POST_PAY('postpay', 'post', { + token: USER_PROFILE.data.auth_p97, + language: 'en-US' + }, {}, params, async (res) => { + if(res.success && res.response != undefined && res.response.status == "success") { + onSuccess(res) + } else { + onError(res) + } + }, (error) => { + console.log(error) + onError(error) + }) + } + + submitPayment = () => { + this.setState({ showModal: false, loading: true }) + this.getTransactionDetails(this.state.claimId, "p97token", this.state.selectedPaymentCard.userPaymentSourceId, onSuccess => { + this.setState({ loading: false }) + if(onSuccess.success) { + let payment = { totalPayment: this.state.data[0]?.originalAmount.amount, cardPayment: this.state.data[0].originalAmount.amount, transactionId: onSuccess.response.transaction_id, storeName: this.state.storeName } + this.props.navigation.navigate('PayatpumpPaymentSuccess', { transactionData: payment }) + } else { + setTimeout(() => { + Alert.alert('Error', "Failed to initiate transaction. Try again.") + }, 300) + } + }, onError => { + this.setState({ loading: false }) + setTimeout(() => { + Alert.alert('Error', "Failed to initiate transaction. Try again.") + }, 300) + }) + } + + renderModal = () => { + return ( + + {}} style={styles.centeredView}> + + {this.state.selectedPoints == null && this.state.selectedPaymentCard == null ? + <> + + {'Please select payment method...'} + this.setState({ showModal: false })} style={{ width: 80, height: 30, backgroundColor: Theme.colors.primary, alignItems: 'center', justifyContent: 'center', borderRadius: 5 }}> + Ok + + : + + {`Are you sure you want to pay ${"\u20B1"}${Theme.formatter.CRNCY(this.state.data[0]?.originalAmount.amount || 0.0)}?`} + + { + this.setState({ showModal: false }) + }}> + No + + this.submitPayment()}> + Yes + + + + } + + + + ) +} + + transactionTable = () => { + const renderItem = () => { + return this.state.data.map((item, index) => { + return ( + + {item.description} + {`${item.quantity} ${item.unitMeasure}`} + {"\u20B1"}{Theme.formatter.CRNCY(item.originalAmount.unitPrice)} + {"\u20B1"}{Theme.formatter.CRNCY(item.originalAmount.amount)} + + ) + }) + } + + return ( + + + + Products + Qty + Price + Total + + + + {renderItem()} + + + + + ) + } + + onBackConfirmation = () => { + Alert.alert( + '', + 'Do you want to cancel this transaction?', + [ + { + text: 'Cancel', + style: 'cancel', + }, + { + text: 'OK', + onPress: () => this.props.navigation.goBack(), + }, + ], + {cancelable: true}, + ); + } + + render() { + return ( + + this.onBackConfirmation()} + back={true} + menu={false} + navigation={this.props.navigation} + /> + + + + + Transaction Date + + + {moment(new Date()).format('DD MMM YYYY hh:mm A')} + + + {this.renderModal()} + {this.transactionTable()} + + Payment Method + + + + + + this.props.navigation.navigate('PayatpumpPaymentList', { onSelectedPaymentCard: (payment_card) => this.onSelectedPaymentCard(payment_card), storeId: this.props.route?.params.storeId})}> + {this.state.selectedPaymentCard ? ( + + + + Change + {this.state.selectedPaymentCard.lastFour} + + + ) : ( + + + Add Card + + )} + + + + {"\u20B1"}{Theme.formatter.CRNCY(this.state.selectedPaymentCard != null && this.state.data[0]?.originalAmount.amount != undefined ? this.state.data[0]?.originalAmount.amount : 0.0)} + + + + + Total Amount + + + {"\u20B1"}{Theme.formatter.CRNCY(this.state.data[0]?.originalAmount.amount || 0.0)} + + + + + {/* + + + + + Points Balance + 3,500.00 + + + + + + this.props.navigation.navigate('PayatpumpPaymentList', { onSelectedPaymentCard: (payment_card) => this.onSelectedPaymentCard(payment_card), storeId: this.props.route?.params.data.storeTenantId})}> + {this.state.selectedPaymentCard ? ( + <> + Change + {this.state.selectedPaymentCard.cardMasked} + + ) : ( + <> + Add Card + + )} + + + + + + + this.props.navigation.navigate('PayatpumpPointsDetailsInput', { totalPointsBalance: 5000, onSelectedPointsAmount: (point_amount) => this.onSelectedPointsAmount(point_amount) })}> + {this.state.selectedPoints && "\u20B1"}{`${this.state.selectedPoints ? Theme.formatter.CRNCY(this.state.selectedPoints) : "Enter point to redeem"}`} + + + + {"\u20B1"}0.00 + + + */} + + Review the details before proceeding. + + + this.setState({ showModal: true })} style={{ flex: 1, paddingHorizontal: 120, height: 44, backgroundColor: Theme.colors.primary, alignItems: 'center', justifyContent: 'center', borderRadius: 10, marginHorizontal: 16 }}> + Pay {"\u20B1"}{Theme.formatter.CRNCY(this.state.data[0]?.originalAmount.amount || 0.0)} + + + + + ); + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(PayatpumpStationTransactionDetails) + +const styles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: "center", + backgroundColor: '#00000090', + }, + modalView: { + margin: 25, + backgroundColor: "white", + borderRadius: 15, + padding: 20, + alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 2 + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5 + }, +}) \ No newline at end of file diff --git a/app/screens/payatpump/search.js b/app/screens/payatpump/search.js new file mode 100644 index 00000000..d8d9bf1e --- /dev/null +++ b/app/screens/payatpump/search.js @@ -0,0 +1,108 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import {ScrollView, SafeAreaView, Text, View, TouchableOpacity, Platform} from 'react-native'; +import {Divider} from 'react-native-elements'; +import Theme from '../../components/theme.style.js'; +import CustomHeader from '../../components/header.js'; +import Icon from '../../components/icons.js'; +import DB from '../../components/storage'; +import REQUEST from '../../components/api'; + +import SearchFragment from './fragments/searchbar.js'; + +class PayatpumpStationSearch extends React.Component { + + constructor(props) { + super(props) + } + + state = { + searchValue: this.props.route.params.value || "", + cities: "", + cityDropDown: [] + } + + componentDidMount() { + this.init() + } + + componentWillUnmount() { + + } + + init = async () => { + let SESSION = await DB.session() + REQUEST('station_search', 'post', { + Authorization: SESSION.token + }, {}, {}, function(res){ + setcities(res.data) + },function(error){ + console.log(error) + }) + } + + search = (value) => { + let result = [] + for(var x=0;x 0) result.push(cities[x]) + } + this.setState({ searchValue: value, cityDropDown: result }) + } + + renderDropDown = () => { + return this.state.cityDropDown.map((city, i) => { + return ( + { + this.props.route.params.onBackPress(this.state.searchValue, city, false) + this.props.navigation.goBack() + }} key={i} style={{flex: 1}}> + {city.name.toUpperCase()} + + + ) + }) + } + + render() { + return ( + + + this.props.navigation.goBack()} navigation={this.props.navigation} /> + + this.search(value)} + onClear={() => this.setState({ searchValue: "", cityDropDown: [] })}/> + { + this.props.route.params.onBackPress(this.state.searchValue, {}, true) + this.props.navigation.goBack() + }} + style={{ flexDirection: 'row', height: 60, padding: 15, marginTop: 80}}> + + My Favorites + + + + + {this.state.cityDropDown.length > 0 && this.state.searchValue != "" ? this.renderDropDown() : null} + + + + + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(PayatpumpStationSearch) \ No newline at end of file diff --git a/app/screens/payatpump/stationdetails.js b/app/screens/payatpump/stationdetails.js new file mode 100644 index 00000000..bb98004f --- /dev/null +++ b/app/screens/payatpump/stationdetails.js @@ -0,0 +1,88 @@ +import React from 'react'; +import {useState, useEffect} from 'react'; +import {SafeAreaView, View} from 'react-native'; +import Theme from '../../components/theme.style.js'; +import CustomHeader from '../../components/header.js'; +import DB from '../../components/storage'; +import REQUEST from '../../components/api'; +import REQUEST_POST_PAY from '../../components/api/postpayapi'; +import Elements from '../../components/elements'; +import MapFragment from './fragments/map.js'; +import PanelFragment from './fragments/stationdetailspanel.js'; + +export default function StationDetails(props){ + + const [loading, setloading] = useState(true) + const [station, setstation] = useState(null) + const [markers, setmarkers] = useState([]) + const [initialRegion, setinitialRegion] = useState(null) + + const init = async () => { + setmarkers(props.route.params?.markers) + setinitialRegion({ + latitude: props.route.params?.data.geoLocation.latitude, + longitude: props.route.params?.data.geoLocation.longitude, + latitudeDelta: 0.0922, + longitudeDelta: 0.0421 + }) + getStationStoreDetails(props.route.params?.data.storeId) + } + + const getStationStoreDetails = async (storeId) => { + await REQUEST_POST_PAY("getStoreDetails", "get", {}, { + storeId: storeId + }, {}, async (response) => { + setloading(false) + if(response.success){ + setstation(response.response) + } + }, (err) => { + setloading(false) + }) + } + + useEffect(() => { + init() + }, []) + + const navigate = (data) => { + props.navigation.navigate('PayatpumpStationDetails', {markers: markers, data: data, onBackPress: () => init()}) + } + + const navigateToPayatpumpTransaction = (response, storeId, selectedPump) => { + props.navigation.navigate('PayatpumpStationTransactionDetails', { response: response, storeId: storeId, storeName: station.storeName, selectedPump: selectedPump.pumpNumber }) + } + + return ( + + + + { + props.route.params?.onBackPress() + props.navigation.goBack() + }} navigation={props.navigation}/> + + navigate(data)} + onMapReady={() => {}} + onMarkerClick={(data) => navigate(data)} + pinSelected={true}/> + + init()} + onTransaction={(response, storeId, selectedPump) => navigateToPayatpumpTransaction(response, storeId, selectedPump)} + lat={props.route.params?.location.latitude} + lon={props.route.params?.location.longitude}/> + + + ) +} \ No newline at end of file diff --git a/app/screens/payatpump/verificationWebview.js b/app/screens/payatpump/verificationWebview.js new file mode 100644 index 00000000..9bbcdae8 --- /dev/null +++ b/app/screens/payatpump/verificationWebview.js @@ -0,0 +1,74 @@ +import * as React from 'react'; +import { SafeAreaView } from 'react-native'; +import { connect } from "react-redux"; +import { WebView } from 'react-native-webview'; +import { Container } from 'native-base'; +import Elements from '../../components/elements' +import Theme from '../../components/theme.style.js'; + +class VerificationWebview extends React.Component { + + constructor(props) { + super(props) + } + + state = { + loading: false, + verificationUrl: this.props.route.params?.verificationUrl, + referenceNumber: this.props.route.params?.referenceNumber + } + + componentDidMount() { + + } + + componentWillUnmount() { + + } + + onStateChange = (webViewState) => { + let url = webViewState.url + if(url.includes('www.paymaya.com') && !webViewState.loading) { + this.props.route.params?.onSuccessAuthentication(this.state.referenceNumber) + setTimeout(() => { + this.props.navigation.goBack() + }, 500); + } + } + + render() { + return ( + + + + this.onStateChange(webViewState)} + /> + + + ) + } +} + +const mapStateToProps = (state) => { + return { + userinfo: state.appUserInfoReducer.userinfo, + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(VerificationWebview) \ No newline at end of file diff --git a/app/screens/products/asphalt.js b/app/screens/products/asphalt.js new file mode 100644 index 00000000..f41d0cb9 --- /dev/null +++ b/app/screens/products/asphalt.js @@ -0,0 +1,84 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import { SafeAreaView, ScrollView, TouchableOpacity, Button, View, Text, Platform } from 'react-native'; +import {useNetInfo} from "@react-native-community/netinfo"; +import NetInfo from "@react-native-community/netinfo"; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Elements from '../../components/elements.js'; +import DB from '../../components/storage'; +import REQUEST from '../../components/api/'; + +export default function Asphalt({navigation, onLoaded}) { + + const [Products, setproducts] = useState({}) + const [asphalt, setasphalt] = useState([]) + + const init = async () => { + const SESSION = await DB.session() + await REQUEST("products", "get", { + Authorization: SESSION.token, + }, {}, {}, + async (res) => { + if(res.status == 1 && res.data.length > 0){ + await setproducts(await res.data) + await setasphalt(await res.data[2].products) + await onLoaded() + }else{ + console.log(res.message, res.data) + onLoaded() + } + }, function(error){ + console.log(error) + onLoaded() + }) + } + + useEffect(() => { + NetInfo.fetch().then(state => { + console.log("Connection type", state.type); + console.log("Is connected?", state.isConnected); + if(state.isConnected){ + init() + }else{ + onLoaded() + navigation.navigation.navigate("ProductsNoInternet") + } + }) + + }, []) + + const renderPoducts = () => { + return asphalt ? asphalt.map((data, index) => { + return ( + { + navigation.navigation.navigate("ProductDetails", { + data: data + }); + }} + key={index} + title={data.name + "\n" + data.description} + image={{uri: data.image || ''}} />) + }) : null + } + + + // const products = Assets.test.mina.map((image, index) => { + // return { + // key: index, + // image: image, + // title: 'DLX CD40 High Quality Mono-grade Diesel Engine Oil' + // } + // }) + + return ( + + + + {renderPoducts()} + + + + ); +} \ No newline at end of file diff --git a/app/screens/products/details.js b/app/screens/products/details.js new file mode 100644 index 00000000..10a5aa34 --- /dev/null +++ b/app/screens/products/details.js @@ -0,0 +1,72 @@ +import * as React from 'react'; +import {useState, setState} from 'react'; +import { connect } from "react-redux"; +import { SafeAreaView, ScrollView, TouchableOpacity, Button, View, Text, Image } from 'react-native'; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Elements from '../../components/elements.js'; +import Theme from '../../components/theme.style.js'; +import {Icon} from 'react-native-elements'; + +const ProductDetails = (navigation) => { + + const [drop1, setdrop1] = useState(false) + const [drop2, setdrop2] = useState(false) + + const product = navigation.route.params.data; + const showImage = false; + console.log([product]) + + const renderDetails = () => { + return ( + + + + setdrop1(!drop1)}> + Application + + + + + + {drop1 ? {product.details[1].details[0].details} : null} + + + + + setdrop2(!drop2)}> + Benefits + + + + + + {drop2 ? {product.details[2].details[0].details} : null} + + ) + } + + return ( + + + + {showImage ? : null} + + {product.name} + {product.description} + Product Description + {product.details[0].details[0].details} + {product.details.length > 1 ? renderDetails() : null} + + + + ); +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(ProductDetails) \ No newline at end of file diff --git a/app/screens/products/fuels.js b/app/screens/products/fuels.js new file mode 100644 index 00000000..ba563480 --- /dev/null +++ b/app/screens/products/fuels.js @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import { SafeAreaView, ScrollView, TouchableOpacity, Button, View, Text, Platform } from 'react-native'; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Elements from '../../components/elements.js'; +import DB from '../../components/storage'; +import REQUEST from '../../components/api/'; + +export default function Fuels({navigation, onLoaded}) { + + const [Products, setproducts] = useState({}) + const [fuels, setfuels] = useState([]) + + const init = async () => { + const SESSION = await DB.session() + await REQUEST("products", "get", { + Authorization: SESSION.token, + }, {}, {}, + async (res) => { + if(res.status == 1 && res.data.length > 0){ + await setproducts(await res.data) + await setfuels(await res.data[1].products) + await onLoaded() + }else{ + console.log(res.message, res.data) + } + }, function(error){ + console.log(error) + }) + } + + useEffect(() => { + init() + }, []) + + const renderPoducts = () => { + return fuels ? fuels.map((data, index) => { + return ( + { + navigation.navigation.navigate("ProductDetails", { + data: data + }); + }} + key={index} + title={data.name + "\n" + data.description} + image={{uri: data.image || ''}} />) + }) : null + } + + // const products = Assets.test.zuvapit.map((image, index) => { + // return { + // key: index, + // image: image, + // title: 'DLX CD40 High Quality Mono-grade Diesel Engine Oil' + // } + // }) + + return ( + + + + {renderPoducts()} + + + + ); +} \ No newline at end of file diff --git a/app/screens/products/index.js b/app/screens/products/index.js new file mode 100644 index 00000000..af7b541d --- /dev/null +++ b/app/screens/products/index.js @@ -0,0 +1,62 @@ +import React from 'react'; +import {useState, useEffect} from 'react'; +import {SafeAreaView, View} from 'react-native'; +import { Container, Header, Tab, Tabs, TabHeading, Icon, Text } from 'native-base'; +import CustomHeader from '../../components/header.js'; +import Elements from '../../components/elements.js'; +import Theme from '../../components/theme.style'; +import Tab1 from './lubes.js'; +import Tab2 from './fuels.js'; +import Tab3 from './asphalt.js'; + +export default function MyProfile(props) { + + const [loaded, setloaded] = useState(0) + + return ( + + + { + props.navigation.reset({ + index: 0, + routes: [{name: 'Main'}], + }); + }} menu={false} navigation={props} /> + + + + setloaded(1)} /> + + + setloaded(2)} /> + + + setloaded(3)} /> + + + + + ); +} \ No newline at end of file diff --git a/app/screens/products/lubes.js b/app/screens/products/lubes.js new file mode 100644 index 00000000..c763a5e5 --- /dev/null +++ b/app/screens/products/lubes.js @@ -0,0 +1,76 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import { SafeAreaView, ScrollView, TouchableOpacity, Button, View, Text, Platform } from 'react-native'; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Elements from '../../components/elements.js'; +import DB from '../../components/storage'; +import REQUEST from '../../components/api/'; + +export default function Lubes({navigation, onLoaded}) { + + const [Products, setproducts] = useState({}) + const [lubes, setlubes] = useState([]) + + const init = async () => { + const SESSION = await DB.session() + await REQUEST("products", "get", { + Authorization: SESSION.token, + }, {}, {}, + async (res) => { + if(res.status == 1 && res.data.length > 0){ + await setproducts(res.data) + await setlubes(res.data[0].products) + await onLoaded() + }else{ + console.log(res.message, res.data) + onLoaded() + } + }, function(error){ + console.log(error) + onLoaded() + }) + } + + useEffect(() => { + init() + }, []) + + // console.log(lubes) + + const renderPoducts = () => { + return lubes ? lubes.map((data, index) => { + return ( + { + navigation.navigation.navigate("ProductDetails", { + data: data + }); + }} + key={index} + title={data.name + "\n" + data.description} + image={{uri: data.image || ''}} />) + }) : false + // return null + } + + + // const products = Assets.test.dixie.map((image, index) => { + // return { + // key: index, + // image: image, + // title: 'DLX CD40 High Quality Mono-grade Diesel Engine Oil' + // } + // }) + + + return ( + + + + {renderPoducts()} + + + + ); +} \ No newline at end of file diff --git a/app/screens/route.js b/app/screens/route.js new file mode 100644 index 00000000..a8f1af38 --- /dev/null +++ b/app/screens/route.js @@ -0,0 +1,619 @@ +import * as React from 'react'; +import DeviceInfo from 'react-native-device-info'; +import { Platform, Alert, AppState, Linking, Appearance } from 'react-native'; +import { NavigationContainer, DarkTheme, DefaultTheme } from '@react-navigation/native'; +import { createStackNavigator } from '@react-navigation/stack'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { connect } from "react-redux"; +import firebase from '@react-native-firebase/app'; +import '@react-native-firebase/messaging'; +import { saveAppTheme } from "../redux/actions/AppThemeActions"; +import { saveFcmToken } from '../redux/actions/AppUserInfoActions'; +import DB from '../components/storage/'; +import Theme from '../components/theme.style.js'; +import Notch from '../components/notch.js'; +import REQUEST from '../components/api'; + +import MainMenu from './main/drawer.js'; +import Login from './login/'; +import Splash from './splash/'; + +import Mpin from './mpin'; +import Setmpin from './mpin/setmpin'; +import Setnewmpin from './mpin/setnewmpin'; +import Otp from './mpin/otp'; +import SecurityQuestion from './mpin/securityquestion'; +import SuccessUpdateMpin from './mpin/succesupdatempin'; + +import PromoDetails from './main/promo/details.js'; +import ProductDetails from './products/details.js'; +import LoyaltyCardDetails from './loyalty/details.js'; + +import MyProfile from './myprofile/'; +import TransactionDetails from './myprofile/transaction/details.js'; +import EditProfile from './myprofile/profile/edit.js'; + +import AddAccountCard from './account/add.js'; +import ActivateCard from './account/activate.js'; +import AccountBirthdate from './account/birthdate.js'; +import AccountSendOtp from './account/sendotp.js'; +import AccountOtp from './account/otp.js'; + +import AccountEnrollActivateCard from './account/activate/index.js'; +import AccountEnrollPin from './account/activate/pin.js'; +import AccountEnrollForm from './account/activate/form.js'; +import AccountEnrollOtp from './account/activate/otp.js'; + +import EnrollActivateCard from './apply/index.js'; +import EnrollPin from './apply/pin.js'; +import EnrollForm from './apply/form.js'; +import EnrollOtp from './apply/otp.js'; + +import AddTracker from './tracker/add.js'; +import AddFuelType from './tracker/addfueltype.js'; +import EditTracker from './tracker/edit.js'; +import TermsConditions from './login/t&c.js'; + +import LoginBirthday from './login/birthday.js'; +import LoginSendOTP from './login/sendotp.js'; +import LoginOTP from './login/otp.js'; + +import ApplySelectCard from './login/apply/card.js'; +import ApplyCardDetails from './login/apply/details.js'; +import ApplyForm from './login/apply/form.js'; +import ApplyOTP from './login/apply/otp.js'; +import LoginEnroll from './login/enroll.js'; + +import Account from './account/'; +import Products from './products/'; +import TopUp from './topup/'; +import CheckOut from './topup/checkout'; +import Tracker from './tracker/'; +import About from './about/'; +import Loyalty from './loyalty/'; +import Contact from './contact/'; +import OnBoarding from './onboard/'; + +// Disable temporarily +import Payatpump from './payatpump'; +import PayatpumpStationSearch from './payatpump/search'; +import PayatpumpDetails from './payatpump/stationdetails' +import PayatpumpStationDetails from './payatpump/fragments/stationdetailspanel'; +import PayatpumpStationTransactionDetails from './payatpump/pumpstationtransactiondetails'; +import PayatpumpPointsDetailsInput from './payatpump/pumppoinstdetails'; +import PayatpumpPaymentList from './payatpump/pumppaymentlist'; +import PayatpumpPaymentMethod from './payatpump/pumppaymentmethod'; +import PayatpumpPaymentSuccess from './payatpump/pumppaymentsuccess'; +import VerificationWebview from './payatpump/verificationWebview'; + +import StationDetails from './main/station/details.js'; +import StationSearch from './main/station/search.js'; + +import TokenezationForm from './topup/form'; + +import messaging from '@react-native-firebase/messaging'; +var PushNotification = require('react-native-push-notification'); + +const RootStack = createStackNavigator(); +const AppStack = createStackNavigator(); + +const navOptions = () => ({ + headerShown: true, + gestureEnabled: false +}); + +const shadowColor = 'transparent'; +const screenOptions = { + headerLeft: () => { return null }, + headerStyle: { + backgroundColor: Theme.colors.primary, + elevation: 0, + height: Notch.isIphoneWithNotch() ? 44 : 20, + shadowColor: shadowColor + }, + headerTintColor: Theme.colors.primary, + headerTitleStyle: { + fontWeight: 'bold' + } +} + +export function AppNavigation(props) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export function compareVersions(v1, comparator, v2) { + var comparator = comparator == '=' ? '==' : comparator; + if(['==','===','<','<=','>','>=','!=','!=='].indexOf(comparator) == -1) { + throw new Error('Invalid comparator. ' + comparator); + } + var v1parts = v1.split('.'), v2parts = v2.split('.'); + var maxLen = Math.max(v1parts.length, v2parts.length); + var part1, part2; + var cmp = 0; + for(var i = 0; i < maxLen && !cmp; i++) { + part1 = parseInt(v1parts[i], 10) || 0; + part2 = parseInt(v2parts[i], 10) || 0; + if(part1 < part2) + cmp = 1; + if(part1 > part2) + cmp = -1; + } + return eval('0' + comparator + cmp); +} + +class App extends React.Component { + + state = { + loading: true, + route: '', + appState: AppState.currentState, + theme: DefaultTheme + } + + componentDidMount() { + setTimeout(() => { + this.validate() + }, 3000) + this.applyTheme() + AppState.addEventListener('change', this._handleAppStateChange) + Appearance.addChangeListener(this._onChangeTheme) + } + + componentWillUnmount() { + try { + AppState.removeEventListener('change', this._handleAppStateChange) + Appearance.removeChangeListener(this._onChangeTheme) + this.unsubscribe() + } catch (error) { + + } + } + + _onChangeTheme = () => { + this.applyTheme() + } + + _handleAppStateChange = (nextAppState) => { + if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') { + this.validateAppVersion() + } + this.setState({ appState: nextAppState }) + this.applyTheme() + } + + fcmTokenAuthorization = async () => { + const authStatus = await firebase.messaging().requestPermission(); + const enabled = authStatus === firebase.messaging.AuthorizationStatus.AUTHORIZED || authStatus === firebase.messaging.AuthorizationStatus.PROVISIONAL; + if (enabled) { + try { + const token = await firebase.messaging().getToken() + if(token) { + let existingToken = await DB.get("fcmToken") || "" + if(token != existingToken){ + this.applySaveFcmToken(token) + } + } + this.props.saveFcmToken(token) + } catch (error) { + console.log(error); + } + } + } + + applySaveFcmToken = (token) => { + DB.set("fcmRegistration", "new", (r) => {}, (e) => {}) + DB.set("fcmToken", token, () => console.log("FCM TOKEN SAVED", token), () => console.log("FCM TOKEN SAVING FAILED")) + } + + applyTheme = () => { + let theme = Appearance.getColorScheme() === 'dark' ? DarkTheme : DefaultTheme + this.setState({ theme: theme }) + this.props.saveAppTheme({ theme: theme }) + } + + validate = async () => { + const SESSION = await DB.session(); + const USER_PROFILE = await DB.profile(); + const enter_mpin = await DB.get('enter_mpin') + const set_mpin = await DB.get('set_mpin') + + this.fcmTokenAuthorization() //check fcm token + + this.validateAppVersion() //check latest + + if ( + !SESSION || + SESSION.token?.length == 0 || + !SESSION.token || + !SESSION.user + ) { + this.setState({ route: "Login" }) + } else { + if(USER_PROFILE) { + if( + USER_PROFILE.data.mpin == undefined + || USER_PROFILE.data.mpin == null + || USER_PROFILE.data.mpin == "" + ) { + this.setState({ route: "Setmpin" }) + } else { + if(set_mpin == null && enter_mpin == null) { + this.setState({ route: "OnBoarding" }) + } else { + if(set_mpin) { + this.setState({ route: "Setmpin" }) + } + if(enter_mpin || enter_mpin == 'false') { + this.setState({ route: "Mpin" }) + } + } + } + } else { + this.setState({ route: "Login" }) + } + } + this.setState({ loading: false }) + }; + + validateAppVersion = async () => { + await REQUEST("getAppVersion", "get", {}, {}, {}, (res) => { + if(res.status == 1) { + let currentVersion = DeviceInfo.getReadableVersion() + let latestVersion = res.data.version + let is_force_update = parseInt(res.data.update_action) + let isNewVersion = compareVersions(currentVersion, "<", latestVersion) + if(isNewVersion) { + var buttons = [ + { + text: "Update Now", + onPress: async () => { + let isDebug = false + if(isDebug) { + let testflightBeta = "https://beta.itunes.apple.com"; + if(Linking.canOpenURL(testflightBeta)) { + Linking.openURL(testflightBeta); + } else { + let testflightJoin = "https://beta.itunes.apple.com"; + Linking.openURL(testflightJoin); + } + } else { + let appstore = "https://apps.apple.com/ph/app/unioil/id1517293002"; + if(Linking.canOpenURL(appstore)) { + Linking.openURL(appstore); + } + } + } + } + ] + if(is_force_update == 0) { + buttons.push({ + text: 'No', + style: 'cancel', + }) + } + if(is_force_update == 2) return + Alert.alert("Update Information", res.data.update_message, buttons) + } + } + }, (error) => { + console.log(error) + }) + } + + registerNotifications = () => { + this.unsubscribe = messaging().onMessage(async (remoteMessage) => { + let allnotifs = await DB.get('notifications'); + console.log('All Notifications', allnotifs); + let notifs = allnotifs ? JSON.parse(allnotifs) : null; + let existence = notifs + ? notifs.find((notif) => { + return notif.messageId == remoteMessage.messageId; + }) + : null; + console.log('Notif Data', allnotifs, existence); + if (!existence) { + let notifObj = { + title: '', + message: '', + playSound: false, + soundName: 'default', + }; + if (Platform.OS == 'ios') { + notifObj.title = remoteMessage.data.notification.title; + notifObj.message = remoteMessage.data.notification.body; + } else { + notifObj.title = remoteMessage.notification.title; + notifObj.message = remoteMessage.notification.body; + } + + PushNotification.localNotification(notifObj); + let result = await DB.AddNotification({ + messageId: + Platform.OS == 'ios' ? remoteMessage.from : remoteMessage.messageId, + title: notifObj.title, + body: notifObj.message, + visible: true, + delivery: true, + recieved: + Platform.OS == 'ios' ? remoteMessage.from : remoteMessage.sentTime, + }); + console.log(result); + } + }); + } + + appStack = () => { + return ; + }; + + render() { + if (this.state.loading) { + return ; + } + return ( + + + + + + + + ); + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme, + token: state.appUserInfoReducer.token + } +} + +const mapDispatchToProps = { + saveAppTheme, + saveFcmToken +} + +export default connect(mapStateToProps, mapDispatchToProps)(App) + +// export default function App(props) { +// const [loading, setloading] = useState(true); +// const [route, setroute] = useState(''); + +// const validate = async () => { +// const SESSION = await DB.session(); +// const USER_PROFILE = await DB.profile(); +// const enter_mpin = await DB.get('enter_mpin') +// const set_mpin = await DB.get('set_mpin') + +// console.log(USER_PROFILE, enter_mpin, set_mpin) + +// if ( +// !SESSION || +// SESSION.token?.length == 0 || +// !SESSION.token || +// !SESSION.user +// ) { +// await setroute('Login'); +// } else { +// if(USER_PROFILE) { +// if( +// USER_PROFILE.data.mpin == undefined +// || USER_PROFILE.data.mpin == null +// || USER_PROFILE.data.mpin == "" +// ) { +// await setroute('Setmpin'); +// } else { +// if(set_mpin == null && enter_mpin == null) { +// await setroute('OnBoarding') +// } else { +// if(set_mpin) { +// await setroute('Setmpin') +// } +// if(enter_mpin || enter_mpin == 'false') { +// await setroute('Mpin') +// } +// } +// } +// } else { +// await setroute('Login') +// } +// } +// await setloading(false); +// }; + +// const showAlert = (status) => { +// switch(status) { +// case 0: +// setTimeout(() => { +// Alert.alert("Deleted Successfully", "\nYour credit card has successfully deleted.", +// [ +// { +// text: 'No', +// style: 'cancel', +// }, +// { +// text: "OK", +// onPress: async () => { +// console.log("HELLO") +// } +// } +// ]) +// }, 300) +// break +// case 1: +// break +// case 2: +// break +// } +// } + +// const validateAppVersion = async () => { +// await REQUEST("getAppVersion", "get", {}, {}, {}, (res) => { +// if(res.status == 1) { +// let currentVersion = DeviceInfo.getReadableVersion() +// let latestVersion = res.data.version +// let is_force_update = parseInt(res.data.update_action) +// let isNewVersion = compareVersions(currentVersion, "<", latestVersion) +// if(isNewVersion) { +// showAlert(is_force_update) +// } +// } +// }, (error) => { +// console.log(error) +// }) +// } + +// useEffect(() => { +// let timer = setTimeout(() => { +// validate() +// }, 3000) + +// return () => { +// clearTimeout(timer) +// } +// }, []) + +// useEffect(() => { +// validateAppVersion() +// }, []) + +// useEffect(() => { +// const unsubscribe = messaging().onMessage(async (remoteMessage) => { +// let allnotifs = await DB.get('notifications'); +// console.log('All Notifications', allnotifs); +// let notifs = allnotifs ? JSON.parse(allnotifs) : null; +// let existence = notifs +// ? notifs.find((notif) => { +// return notif.messageId == remoteMessage.messageId; +// }) +// : null; +// console.log('Notif Data', allnotifs, existence); +// if (!existence) { +// let notifObj = { +// title: '', +// message: '', +// playSound: false, +// soundName: 'default', +// }; +// if (Platform.OS == 'ios') { +// notifObj.title = remoteMessage.data.notification.title; +// notifObj.message = remoteMessage.data.notification.body; +// } else { +// notifObj.title = remoteMessage.notification.title; +// notifObj.message = remoteMessage.notification.body; +// } + +// PushNotification.localNotification(notifObj); +// let result = await DB.AddNotification({ +// messageId: +// Platform.OS == 'ios' ? remoteMessage.from : remoteMessage.messageId, +// title: notifObj.title, +// body: notifObj.message, +// visible: true, +// delivery: true, +// recieved: +// Platform.OS == 'ios' ? remoteMessage.from : remoteMessage.sentTime, +// }); +// console.log(result); +// } +// }); + +// return unsubscribe; +// }, []); + +// const appStack = () => { +// return ; +// }; + +// if (loading) { +// return ; +// } + +// return ( +// +// +// +// +// +// +// +// ); +// } diff --git a/app/screens/splash/index.js b/app/screens/splash/index.js new file mode 100644 index 00000000..cc027f0f --- /dev/null +++ b/app/screens/splash/index.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import { SafeAreaView, Button, View, Text, Image, ImageBackground, Platform } from 'react-native'; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Theme from '../../components/theme.style.js'; + +export default function SplashScreen(navigation) { + + const [logo, setlogo] = useState(Assets.logo.iconWhite); + const [size, setsize] = useState(Theme.screen.h / 2); + + useEffect(() => { + setTimeout(function(){ + setsize(90) + setlogo(Assets.logo.reverse); + }, Platform.OS == 'ios' ? 1400 : 700) + }, []) + + return ( + + + {/* */} + + ); +} \ No newline at end of file diff --git a/app/screens/topup/checkout.js b/app/screens/topup/checkout.js new file mode 100644 index 00000000..5d0f2bd1 --- /dev/null +++ b/app/screens/topup/checkout.js @@ -0,0 +1,328 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import { SafeAreaView, BackHandler, Alert } from 'react-native'; +import { connect } from "react-redux"; +import { saveUserInfo } from "../../redux/actions/AppUserInfoActions"; +import { WebView } from 'react-native-webview'; +import {Container, Toast} from 'native-base'; +import DB from '../../components/storage/'; +import Elements from '../../components/elements' +import REQUEST from '../../components/api/'; +import Theme from '../../components/theme.style.js'; +import Tokenization from '../../components/paymaya/tokenization'; + +class CheckOut extends React.Component { + + constructor(props) { + super(props) + } + + state = { + amount: 0, + data: null, + session: null, + processed: false, + loading: false, + transactionId: null, + isShowPrompt: false + } + + componentDidMount() { + this.init() + BackHandler.addEventListener('hardwareBackPress', this.onBackPress) + return () => { + BackHandler.removeEventListener('hardwareBackPress', this.onBackPress) + } + } + + componentWillUnmount() { + + } + + init = async () => { + const SESSION = await DB.session() + this.setState({ data: this.props.route.params?.data, amount: this.props.route.params?.amount, transactionId: this.props.route.params?.transactionId, session: SESSION }) + } + + getTransactionDetails = async (paymentId, onResponse) => { + let transaction = await Tokenization.getTransactionDetails(paymentId) + if(transaction) onResponse(transaction) + } + + onCheckOutResult = async (result, message) => { + Toast.show({ + text: "Transaction " + message, + buttonText: "", + duration: 3000 + }) + if(result == "success"){ + this.props.navigation.reset({ + index: 0, + routes: [{ name: 'Main' }], + }) + } else { + if(result == "failed") { + if(!this.state.isShowPrompt) { + this.setState({ isShowPrompt: true }) + setTimeout(() => { + Alert.alert( + 'Transaction Failed!', + `Failed to verify transaction. Please try again.`, + [ + { + text: "OK", + onPress: () => { + this.setState({ isShowPrompt: false }) + this.props.navigation.goBack() + }}, + ] + ) + }, 700) + } + } else { + setTimeout(() => { + Alert.alert( + 'Transaction Failed!', + `Transaction cancelled. Please try again.`, + [ + { + text: "OK", + onPress: () => { + this.props.navigation.goBack() + }}, + ] + ) + }, 700) + } + } + } + + onSuccess = () => { + if(this.state.data.type == 'add'){ + this.props.route.params?.onBack() + return; + } + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert( + 'Transaction Completed!', + `You have successfully paid ${parseFloat(this.state.amount).toFixed(2)} points from your credit card \n**** **** **** ${this.state.data.card_number}.`, + [ + { + text: "OK", + onPress: () => { + this.props.navigation.navigate('Main') + }}, + ] + ) + }, 300) : null + } + + onBackPress = () => { + return true + } + + topup = (webViewState) => { + let url = webViewState.url + if(url.includes('/success') && !this.state.processed){ + this.setState({ processed: true, valid: false }) + if(this.state.data.type == 'add') { + this.onSuccess() + this.props.navigation.goBack() + return // check if transaction is only add card or payment topup, if then add card stop the saving to database + } + + this.getTransactionDetails(this.state.transactionId, onResponse => { + if(onResponse.status == "PAYMENT_SUCCESS") { + this.setState({ loading: true }) + this.props.saveUserInfo({ token: this.state.session.token, card_number: this.state.session.user.card_number }).then(profile => { + this.setState({ loading: false }) + DB.updateProfile(profile, () => { + this.onSuccess() + this.props.navigation.goBack() + }, (error) => {}) + }) + } else { + this.onCheckOutResult('faild','failed') + } + }) + + // REQUEST('topup', 'post', { + // Authorization: this.state.session.token, + // card_number: this.state.session.user.card_number + // }, {}, { + // amount: this.state.amount + // }, (res) => { + // this.props.saveUserInfo({ token: this.state.session.token, card_number: this.state.session.user.card_number }).then(res => { + // DB.updateProfile(res, () => { + // this.onSuccess() + // this.props.navigation.goBack() + // }, (error) => {}) + // }) + // }, (error) => { + // this.onCheckOutResult('error','failed because ' + error) + // }) + } else if(url.includes('/failure')) { + this.onCheckOutResult('failed', 'failed') + }else if(url.includes('/cancel')){ + this.onCheckOutResult('cancelled', 'cancelled') + } + } + + render() { + return ( + + + + {!this.state.processed ? this.topup(webViewState)} + /> : null} + + + ) + } +} + +const mapStateToProps = (state) => { + return { + userinfo: state.appUserInfoReducer.userinfo, + app_theme: state.appThemeReducer.theme + } +} + +const mapDispatchToProps = { + saveUserInfo +} + +export default connect(mapStateToProps, mapDispatchToProps)(CheckOut) + +// export default function CheckOut(navigation) { + +// const [amount, setamount] = useState(0) +// const [data, setdata] = useState(null) +// const [session, setsession] = useState(null) +// const [processed, setprocessed] = useState(false) + +// const init = async () => { +// const SESSION = await DB.session() +// await setdata(navigation.route.params?.data) +// await setamount(navigation.route.params?.amount) +// await setsession(SESSION) +// } + +// const onCheckOutResult = async (result, message) => { +// // navigation.route.params.onBack(result, message) +// // navigation.navigation.goBack() +// Toast.show({ +// text: "Transaction " + message, +// buttonText: "", +// duration: 3000 +// }) +// if(result == "success"){ +// navigation.navigation.reset({ +// index: 0, +// routes: [ { name: 'Main' } ], +// }) +// }else{ +// navigation.navigation.goBack() +// } +// } + +// const onSuccess = () => { + +// if(data.type == 'add'){ +// navigation.route.params?.onBack() +// return; +// } + +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert( +// 'Transaction Completed!', +// `You have successfully paid ${parseFloat(amount).toFixed(2)} points from your credit card \n**** **** **** ${data.card_number}.`, +// [ +// { +// text: "OK", +// onPress: () => { +// navigation.navigation.reset({ +// index: 0, +// routes: [ { name: 'Main' } ], +// }) +// } +// }, +// ] +// ) +// }, 700) : +// null +// } + +// useEffect(() => { +// init() +// }, []) + +// const onBackPress = () => { +// return true +// } + +// useEffect(() => { +// BackHandler.addEventListener('hardwareBackPress', onBackPress) +// return () => { +// BackHandler.removeEventListener('hardwareBackPress', onBackPress) +// } +// }, []) + +// return ( +// +// +// {!processed ? { +// // console.log("CHECKOUT FINAL RESULT", webViewState.url) +// let url = webViewState.url +// if(url.includes('/success') && !processed){ +// setprocessed(true) +// // onCheckOutResult('success','success') // REMOVE THIS +// REQUEST('topup', 'post', { +// Authorization: session.token, +// card_number: session.user.card_number +// }, {}, { +// amount: amount +// }, function(res){ +// // onCheckOutResult('success','success') +// onSuccess() +// navigation.navigation.goBack() +// }, function(error){ +// onCheckOutResult('error','failed because ' + error) +// }) +// }else if(url.includes('/failure')){ +// alert("Failed") +// // onCheckOutResult('failed', 'failed') +// }else if(url.includes('/cancel')){ +// alert("Cancelled") +// // onCheckOutResult('cancelled', 'cancelled') +// } +// }} +// /> : null} +// +// +// ) +// } \ No newline at end of file diff --git a/app/screens/topup/form.js b/app/screens/topup/form.js new file mode 100644 index 00000000..5b271d73 --- /dev/null +++ b/app/screens/topup/form.js @@ -0,0 +1,429 @@ +import * as React from 'react' +import {useState, useEffect} from 'react' +import { connect } from "react-redux"; +import {SafeAreaView, View, Modal, Dimensions, Image, Platform, Alert, Keyboard} from 'react-native' +import {Button, Icon} from 'native-base'; +import Theme from '../../components/theme.style' +import CustomHeader from '../../components/header.js' +import Elements from '../../components/elements' +import Assets from '../../components/assets.manager' +import Form from '../../components/paymaya/form' +import DB from '../../components/storage' +import Tokenization from '../../components/paymaya/tokenization' +import REQUEST from '../../components/api/'; +import * as UTILS from './utils' + + +class TokenizationForm extends React.Component { + + constructor(props) { + super(props) + } + + state = { + loading: false, + isVisibleModal: true, + keyboardHeight: 0 + } + + componentDidMount() { + this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow) + this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide) + } + + componentWillUnmount() { + this.keyboardDidShowListener.remove() + this.keyboardDidHideListener.remove() + } + + _keyboardDidShow = (e) => { + this.setState({ keyboardHeight: e.endCoordinates.screenY - e.endCoordinates.height - 44 }); + } + + _keyboardDidHide = () => { + this.setState({ keyboardHeight: 0 }); + } + + transactionEntry = async (transactionId, amount, onSuccess, onError) => { + const SESSION = await DB.session() + REQUEST('topup_transaction_entry', 'post', { + Authorization: SESSION.token + }, {}, { + amount: amount, + paymaya_tranx_id: transactionId + }, (res) => onSuccess(res), (error) => onError(error)) + } + + onCreateVault = async (card) => { + this.setState({ loading: true }) + let merchant = await UTILS.createMerchantDetails() + merchant.card = {card: {name: card.name, number: card.cardnumber, expMonth: card.expmonth, expYear: card.expyear, cvc: card.cvv}} + + let checkout = await Tokenization.initNewCheckout(merchant, this.props.amount) + if(checkout.status == "success") { + this.transactionEntry(checkout.id, this.props.amount, + onSuccess => { + this.setState({ loading: false, isVisibleModal: false }) + this.props.onGoBack(); + this.props.onSuccess({ + amount: this.props.amount, + transactionId: checkout.id, + data: {redirectUrl: checkout.verificationUrl, card_number: checkout.card_number, type: 'create', }, + onBack: (res, msg) => alert(msg) + }); + }, + onError => { + setTimeout(() => { + Alert.alert("Top Up", `\n${onError.error}`) + }, 700) + }) + } else if(checkout.status == "failed"){ + this.setState({ loading: false, isVisibleModal: false }) + if(checkout.result.parameters.length > 0) { + setTimeout(() => { + Alert.alert("Top Up", `\n${checkout.result.parameters[0]?.description}`) + }, 700) + } else { + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert("Top Up", "\nFailed to add new card.") + }, 300) : Alert.alert("Top Up", "\nFailed to add new card.") + } + } else { + this.setState({ loading: false }) + this.props.onGoBack() + } + + /* + * Old code for purchasing value points + */ + // let checkout = await Tokenization.initCheckOut("", "", merchant, this.props.amount) + // if(checkout.status == 'failed'){ + // this.setState({ loading: false, isVisibleModal: false }) + // if(checkout.result.merchant.parameters) { + // Platform.OS == 'ios' ? setTimeout(() => { + // Alert.alert("Top Up", "\nUpdate your profile first before using this feature.") + // }, 300) : Alert.alert("Top Up", "\nUpdate your profile first before using this feature.") + // } else { + // Platform.OS == 'ios' ? setTimeout(() => { + // Alert.alert("Top Up", "\nFailed to add new card.") + // }, 300) : Alert.alert("Top Up", "\nFailed to add new card.") + // } + // } else if(checkout.status == 'success') { + // this.setState({ loading: false, isVisibleModal: false }) + // this.props.onGoBack(); + // this.props.onSuccess({ + // amount: this.props.amount, + // data: {redirectUrl: checkout.verificationUrl, card_number: checkout.card_number, type: 'create'}, + // onBack: (res, msg) => alert(msg) + // }); + // } + // this.setState({ loading: false }) + // this.props.onGoBack() + } + + onAddVault = async (card) => { + this.setState({ loading: true }) + let merchant = await UTILS.createMerchantDetails() + merchant.card = {card: {name: card.name, number: card.cardnumber, expMonth: card.expmonth, expYear: card.expyear, cvc: card.cvv}} + let addedCard = await Tokenization.initNewAddCard(this.props.customerId, merchant) + if(addedCard.status == 'failed') { + this.setState({ loading: false, isVisibleModal: false }) + let message = addedCard?.result?.merchant?.parameters != undefined ? addedCard?.result?.merchant?.parameters[0].description : checkout?.card != undefined ? checkout?.card.parameters[0].description : "Invalid card" + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert("Top Up", message) + }, 800) : Alert.alert("Top Up", message) + this.props.onGoBack() + } else if(addedCard.status == 'success') { + this.setState({ loading: false, isVisibleModal: false }) + if(addedCard.link.code) { + console.log("CUSTOMER ID", addedCard) + this.props.onGoBack() + } else { + this.props.onSuccess({ + amount: this.props.amount, + data: {redirectUrl: addedCard.link.verificationUrl, card_number: addedCard.link.maskedPan, type: 'add'}, + onBack: (res, msg) => { + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert( + 'Add Card', + `\nYour new credit card has successfully added to your account.`, + [ + { + text: "OK", + onPress: async () => { + await this.props.onDone() + } + }, + ] + ) + }, 700) : null + } + }); + } + } + + /* + * Old code for adding new card + */ + // this.setState({ loading: true }) + // let ccard = {card: {name: card.name, number: card.cardnumber, expMonth: card.expmonth, expYear: card.expyear, cvc: card.cvv}} + // let checkout = await Tokenization.initAdd(ccard, this.props.customerId) + // if(checkout.status == 'failed') { + // this.setState({ loading: false, isVisibleModal: false }) + // let message = checkout?.result?.merchant?.parameters != undefined ? checkout?.result?.merchant?.parameters[0].description : checkout?.card != undefined ? checkout?.card.parameters[0].description : "Invalid card" + // Platform.OS == 'ios' ? setTimeout(() => { + // Alert.alert("Top Up", message) + // }, 800) : Alert.alert("Top Up", message) + // this.props.onGoBack() + // console.log("RESULT CARD", checkout) + // } else if(checkout.status == 'success') { + // this.setState({ loading: false, isVisibleModal: false }) + // if(checkout.link.code) { + // console.log("CUSTOMER ID", this.props.customerId, checkout.link.code) + // this.props.onGoBack() + // } else { + // this.props.onSuccess({ + // amount: this.props.amount, + // data: {redirectUrl: checkout.link.verificationUrl, card_number: checkout.link.maskedPan, type: 'add'}, + // onBack: (res, msg) => { + // Platform.OS == 'ios' ? setTimeout(() => { + // Alert.alert( + // 'Add Card', + // `\nYour new credit card has successfully added to your account.`, + // [ + // { + // text: "OK", + // onPress: async () => { + // await this.props.onDone() + // } + // }, + // ] + // ) + // }, 700) : null + // } + // }); + // } + // console.log("RESULT CARD", checkout.link.maskedPan) + // } + } + + handleFormSend = async (card) => { + let cards = this.props.cardlist + let filteredCard = cards.find(function(item) { + return card.cardnumber.includes(item.first6) && card.cardnumber.includes(item.last4) + }) + if(filteredCard && this.props.type === 'add') { + this.setState({ loading: false, isVisibleModal: false }) + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert("Error", "This card is already on your list.") + }, 800) : Alert.alert("Error", "This card is already on your list.") + return + } + if(this.props.type == "create"){ + Alert.alert( + 'Confirm Top Up', + `\nYour credit card will be used to pay ${parseFloat(this.props.amount).toFixed(2)} points.`, + [ + { + text: "NO", + style: "cancel" + }, + { + text: "YES", + onPress: () => { + this.onCreateVault(card) + } + }, + ] + ) + } else if(this.props.type == 'add') { + this.onAddVault(card) + } + } + + render() { + return ( + this.props.onGoBack()} + transparent={true} + presentationStyle="overFullScreen" + style={{flex: 1, backgroundColor: 'rgba(0,0,0,0.9)'}}> + + + + +
this.handleFormSend(card)} /> + + + + ) + } +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(TokenizationForm) + +// export default function TokenizerForm(props){ +// let { +// isVisible, +// onGoBack, +// amount, +// onSuccess, +// } = props; +// const [loading, setloading] = useState(false) + +// const onCreateVault = async (card) => { + +// setloading(true) +// let merchant = await UTILS.createMerchantDetails() +// merchant.card = {card: {name: card.name, number: card.credit_card, expMonth: card.expmonth, expYear: card.expyear, cvc: card.cvv}} +// console.log(merchant) + +// let checkout = await Tokenization.initCheckOut("", "", merchant, amount) +// if(checkout.status == 'failed'){ + +// if(checkout.result.merchant.parameters){ +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert("Top Up", "\nUpdate your profile first before using this feature.") +// }, 300) : Alert.alert("Top Up", "\nUpdate your profile first before using this feature.") +// } + +// // alert(JSON.stringify(checkout.result, null, '\t')) +// console.log("RESULT CARD", checkout.result.card) +// console.log("RESULT MERCHANT", checkout.result.merchant) +// setloading(false) +// }else if(checkout.status == 'success'){ +// console.log("RESULT", checkout) +// setloading(false) +// onGoBack(); +// onSuccess({ +// amount: amount, +// data: {redirectUrl: checkout.verificationUrl, card_number: checkout.card_number, type: 'create'}, +// // onBack: (res, msg) => props.route.params.onReload(res, msg) +// onBack: (res, msg) => alert(msg) +// }); +// } + +// setloading(false) +// props.onGoBack() + +// // let client = {} +// // client.card = { +// // card: { +// // number: card.card_number, +// // expMonth: card.expmonth, +// // expYear: card.expyear, +// // cvc: card.cvv +// // } +// // } + +// } + +// const onAddVault = async (card) => { +// setloading(true) +// let ccard = {card: {name: card.name, number: card.credit_card, expMonth: card.expmonth, expYear: card.expyear, cvc: card.cvv}} +// let checkout = await Tokenization.initAdd(ccard, props.customerId) +// if(checkout.status == 'failed'){ +// setTimeout(() => { +// alert(JSON.stringify(checkout, null, '\t')) +// }, 700) +// console.log("RESULT CARD", checkout) +// setloading(false) +// }else if(checkout.status == 'success'){ +// console.log("RESULT", checkout.link.maskedPan) +// console.log("CUST ID", props.customerId) + +// if(checkout.link.code){ +// console.log(props.customerId) +// }else{ +// onSuccess({ +// amount: amount, +// data: {redirectUrl: checkout.link.verificationUrl, card_number: checkout.link.maskedPan, type: 'add'}, +// // onBack: (res, msg) => props.route.params.onReload(res, msg) +// onBack: (res, msg) => { +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert( +// 'Add Card', +// `\nYour new credit card has successfully added to your account.`, +// [ +// { +// text: "OK", +// onPress: async () => { +// await props.onDone() +// } +// }, +// ] +// ) +// }, 700) : +// null +// } +// }); +// } +// } + +// setloading(false) +// props.onGoBack() +// } + +// const handleJustu = async (card) => { +// if(props.type == "create"){ +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert( +// 'Confirm Top Up', +// `\nYour credit card will be used to pay ${parseFloat(props.amount).toFixed(2)} points.`, +// [ +// { +// text: "NO", +// style: "cancel" +// }, +// { +// text: "YES", +// onPress: () => { +// onCreateVault(card) +// } +// }, +// ] +// ) +// }, 700) : +// null +// }else if(props.type == 'add'){ +// onAddVault(card) +// } +// } + +// return ( +// +// +// +// +// +// +// +// +// +// ) +// } \ No newline at end of file diff --git a/app/screens/topup/index.js b/app/screens/topup/index.js new file mode 100644 index 00000000..f82826e9 --- /dev/null +++ b/app/screens/topup/index.js @@ -0,0 +1,1008 @@ +import * as React from 'react'; +import { connect } from "react-redux"; +import { saveUserInfo } from "../../redux/actions/AppUserInfoActions"; +import { useState, useEffect } from 'react'; +import { SafeAreaView, View, Text, TouchableOpacity, Alert, Platform, Image } from 'react-native'; +import { Container, Header, Content, List, ListItem, Thumbnail, Left, Body, Icon, Button, Right, Toast } from 'native-base'; +import {useNetInfo} from "@react-native-community/netinfo"; +// import NetInfo from "@react-native-community/netinfo"; +import NetInfo from '../../components/netstatus'; +import CustomHeader from '../../components/header.js'; +import Theme from '../../components/theme.style.js'; +import Assets from '../../components/assets.manager.js'; +import Elements from '../../components/elements.js'; +import REQUEST from '../../components/api'; +import DB from '../../components/storage/'; +import { Input } from 'react-native-elements'; +import {TotalAmount, Buyer, Item, Ref, CheckOut} from '../../components/paymaya/checkout'; +import TokenizationForm from './form'; +import Tokenization from '../../components/paymaya/tokenization' +import moment from 'moment'; +import { act } from 'react-test-renderer'; + +// const PAYMENT_ENV = "SANDBOX" +// const PAYMENT_ENV = "PRODUCTION" + +class TopUp extends React.Component { + + constructor(props) { + super(props) + this.current = "" + } + + state = { + connected: false, + isCardFormVisible: false, + loading: false, + popup: false, + Task: "", + DisplayAmount: "", + amount: 0, + focused: false, + checkoutResult: "", + userProfile: null, + session: null, + topupCount: 0, + merchant: {}, + creditcards: [], + activeIndex: 0, + isKeyboardActive: false, + tokenizationFormCount: 0, + transactionType: null + } + + componentDidMount() { + NetInfo.netstatus(isConnected => { + if(isConnected) { + this.init() + } else { + Elements.nointernet2() + } + }) + } + + componentWillUnmount() { + + } + + init = async () => { + let user = await DB.profile() + const session = await DB.session() + this.setState({ connected: true, loading: true, userProfile: user, session: session }) + if(user.data.civilstatus_code == "0" || user.data.gender_code == "0") { + Alert.alert("Information", "Update your Profile first to use this feature.", [{text: "OK", onPress: () => this.props.navigation.goBack()}]) + return + } + + this.countTransactions() + this.getMerchant() + } + + countTransactions = async () => { + const SESSION = await DB.session() + await REQUEST("transactions", "get", { + Authorization: SESSION.token, + card_number: SESSION.user.card_number + }, {}, {}, (res) => { + console.log(res) + let count = 0 + if(res.data.length > 0){ + for(var x=0;x= 5){ + this.setState({ loading: false }) + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert("Top Up", "\nYou have reached your maximum top up for this day.") + }, 700) + : + Alert.alert("Top Up", "\nYou have reached your maximum top up for this day.") + } + }, (error) => { + this.setState({ loading: false }) + console.log(error) + }) + } + + handleDelete = (id, token, uuid) => { + Alert.alert( + "Delete Payment Card", + "Are you sure you want to delete this card?", + [ + { + text: "NO", + style: "cancel" + }, + { + text: "YES", + onPress: () => { + this.setState({ loading: true }) + Tokenization.initRemove(id, token, uuid).then((res) => { + this.setState({ loading: false }) + if(res.result == 'OK'){ + setTimeout(() => { + Alert.alert("Deleted Successfully", "\nYour credit card has successfully deleted.", [{text: "OK", onPress: async () => { + this.init() + let creditcards = this.state.creditcards.filter(function(item){ + return item.uuid != uuid + }).map(function(item){ + return item + }) + this.setState({ creditcards: creditcards }) + }}]) + }, 300) + } + }) + } + }, + ] + ) + } + + getMerchant = async () => { + const USER_PROFILE = await DB.profile() + const SESSION = await DB.session() + await REQUEST("paymaya_tokens", "get", {'Authorization': SESSION.token}, {noID: true, value: USER_PROFILE.data.card_number}, {}, async (res) => { + if(res.data && res.data.cards){ + this.setState({ merchant: res.data, activeIndex: 0 }) + for(var x=0;x { + this.setState({ loading: false }) + console.log(error) + }) + } + + moneyFormat = (price, sign = 'PHP ') => { + const pieces = parseFloat(price).toFixed(2).split('') + let ii = pieces.length - 3 + while ((ii-=3) > 0) { + pieces.splice(ii, 0, ',') + } + return sign + pieces.join('') + } + + setamount = (value) => { + this.setState({ DisplayAmount: this.moneyFormat(value).toString(), amount: value }) + } + + onAmountChange = (value) => { + if(value != this.current){ + let parsed = parseFloat(this.clean(value)) + if(parsed > 1000000) return false + let formatted = this.formatCurrency(parsed) + this.current = formatted + this.setState({ DisplayAmount: formatted, amount: parseFloat(parsed/100) }) + } + } + + clean = (str) => { + return str.replace("PHP ", "").replace(".", "").replace(",", "").toString() + } + + formatCurrency = (val) => { + let fv = isNaN(val) ? (0).toFixed(2) : parseFloat(val / 100).toFixed(2) + return val.length == 6 ? "PHP " + fv.substr(0, 1) + "," + fv.substr(2, fv.length) : "PHP " + fv + } + + validate = () => { + if(this.state.amount < 100 || this.state.amount > 10000){ + Alert.alert("Invalid top up amount", "\nPlease enter value between 100 - 10,000 pesos only.\n") + return false + } + return true + } + + onReload = (res, msg) => { + console.log("onBack", res, msg) + } + + updateTopup = (transactionId, amount, onSuccess, onError) => { + REQUEST('topup_transaction_entry', 'post', { + Authorization: this.state.session.token + }, {}, { + amount: amount, + paymaya_tranx_id: transactionId + }, (res) => onSuccess(res), (error) => onError(error)) + + /* + * Old for topup update + */ + // REQUEST('topup', 'post', { + // Authorization: this.state.session.token, + // card_number: this.state.userProfile.data.card_number + // }, {}, { + // amount: this.state.amount + // }, (res) => { + // this.props.saveUserInfo({ token: this.state.session.token, card_number: this.state.userProfile.data.card_number }).then(res => { + // DB.updateProfile(res, () => { + // this.setState({ loading: false }) + // this.props.navigation.reset({ + // index: 0, + // routes: [ { name: 'Main' } ], + // }) + // }, (error) => {}) + // }) + // }, (error) => { + // this.onCheckOutResult('error','failed because ' + error) + // }) + } + + initCheckout = async () => { + if(!this.validate()) return false + if(this.state.creditcards.length > 0){ + let mct = this.state.creditcards[this.state.activeIndex] + Alert.alert("Confirm Top Up", "\nYour card number will be used to pay for "+parseFloat(this.state.amount).toFixed(2)+" points.", [ + {text: "NO",style: "cancel"}, + {text: "YES", + onPress: async () => { + let payout = await Tokenization.initNewPayout(mct.cardTokenId, this.state.amount); + if(payout.status == "success") { + this.setState({ loading: true }) + this.updateTopup(payout.id, this.state.amount, onSuccess => { + this.setState({ loading: false }) + let data = { + amount: this.state.amount, + transactionId: payout.id, + data: {redirectUrl: payout.verificationUrl, card_number: mct.maskedPan, type: 'create' }, + onBack: (res, msg) => alert(msg) + } + this.props.navigation.navigate('CheckOut', data) + }, onError => { + setTimeout(() => { + Alert.alert("Top Up", `\n${onError.error}`) + }, 700) + }) + } else if(payout.status == "failed"){ + this.setState({ loading: false }) + if(payout.parameters.length > 0) { + setTimeout(() => { + Alert.alert("Top Up", `\n${payout.parameters[0]?.description}`) + }, 700) + } else { + Platform.OS == 'ios' ? setTimeout(() => { + Alert.alert("Top Up", "\nFailed to add new card.") + }, 300) : Alert.alert("Top Up", "\nFailed to add new card.") + } + } + + /* + * Old for initiating purchase of points + */ + // let payout = await Tokenization.initPayOut(mct.customer_id, mct.cardTokenId, this.state.amount) + // console.log(payout) + // if(payout.isPaid){ + // if(payout.status == 'PAYMENT_SUCCESS'){ + // Platform.OS == 'ios' ? setTimeout(() => { + // Alert.alert("Topup Success", `\nYou have successfully paid ${parseFloat(this.state.amount).toFixed(2)} points from your credit card ${payout.fundSource.details.masked}`, + // [{text: "OK", onPress: async () => this.updateTopup()}]) + // }, 300) : + // Alert.alert("Topup Success", `\nYou have successfully paid ${parseFloat(this.state.amount).toFixed(2)} points from your credit card ${payout.fundSource.details.masked}`, + // [{text: "OK", onPress: async () => this.updateTopup()}]) + // } + // } else { + // this.setState({ loading: false }) + // setTimeout(() => alert(JSON.stringify(payout)), 300) + // } + } + } + ]) + } else { + this.setState({ isCardFormVisible: true }) + } + } + + initAddCard = async () => { + this.setState({ isCardFormVisible: true, tokenizationFormCount: this.state.tokenizationFormCount + 1, transactionType: "add" }) + } + + getDisplayCard = (type) => { + if(type == "visa") return Assets.icons.stpvisa + else if(type == "master-card") return Assets.icons.stpmastercard + else if(type == "jcb") return Assets.icons.stpjcb + else return Assets.stpunknown + } + + renderCardform = () => { + return ( + 0 ? this.state.merchant.customer_id : ''} + amount={this.state.amount} + onDone={() => { + this.init() + console.log("onDone") + }} + onBack={(res, msg) => { + this.onReload(res, msg) + }} + onGoBack={() => { + this.setState({ isCardFormVisible: false }) + console.log("onGoBack") + }} + onSuccess={data => { + console.log("onSucces", data) + this.props.navigation.navigate('CheckOut', data) + }} + /> + ) + } + + render() { + + if(!this.state.connected){ + return ( + + + + this.init()} + /> + + + ) + } + + return ( + + {this.state.isCardFormVisible && this.renderCardform()} + + + + + + Select Top Up Value Points + + + + + + this.setamount(100)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> + 100 + + this.setamount(500)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> + 500 + + + + this.setamount(1000)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> + 1000 + + this.setamount(5000)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> + 5000 + + + + + + + Or Enter Desired Value (maximum of 10,000 points) + + this.setState({ focused: true })} + onChangeText={(value) => this.onAmountChange(value)} + containerStyle={{padding: 0}} + inputContainerStyle={{padding: 0, borderBottomWidth: this.state.focused ? 1.75 : 1, borderColor: this.state.focused ? Theme.colors.accent : "gray" }} + inputStyle={{padding: 0, fontFamily: 'Arial', fontSize: 16, color: this.props.app_theme?.theme.colors.text }} + /> + Note: Top Up to a maximum of five(5) times per day. + + + + + Payment Method + + + + {this.state.creditcards.length > 0 ? + + + + + + + {this.state.creditcards[this.state.activeIndex]?.cardType.toString().toUpperCase()} {this.state.creditcards[this.state.activeIndex]?.maskedPan} + **** **** **** {this.state.creditcards[this.state.activeIndex]?.maskedPan} + + + + : + + + + + + + + 0000 0000 0000 0000 + + + + } + + {this.state.creditcards.length <= 5 && ( + this.initAddCard()} style={{flexDirection: 'row', alignItems: 'center', padding: 15, borderBottomWidth: 0.5, borderColor: 'lightgray'}}> + + + Add New Card + + + )} + + {this.state.creditcards.length > 0 && this.state.creditcards.map((card, index) => { + return ( + + + + + + + { + Toast.show({ + text: "**** **** **** " + card?.maskedPan + " is selected.", + buttonText: "", + duration: 1500 + }) + this.setState({ activeIndex: index }) + }}> + {card?.cardType.toString().toUpperCase()} + **** **** **** {card?.maskedPan} + + + + + + + + ) + })} + + + {this.state.topupCount >= 5 ? null : + 0 ? false : true} + style={{ justifyContent: 'center', marginHorizontal: 15, paddingVertical: 15, borderRadius: 10, backgroundColor: this.state.creditcards.length > 0 ? Theme.colors.primary : this.props.app_theme?.theme.dark ? this.props.app_theme?.theme.colors.border : Theme.colors.primary + "15", marginBottom: 16}} + onPress={() => this.initCheckout()}> + Next + + } + + + ) + } +} + +const mapStateToProps = (state) => { + return { + userinfo: state.appUserInfoReducer.userinfo, + app_theme: state.appThemeReducer.theme + } +} + +const mapDispatchToProps = { + saveUserInfo +} + +export default connect(mapStateToProps, mapDispatchToProps)(TopUp) + +// export default function TopUp(navigation) { + +// const [connected, setconnected] = useState(false); +// const [isCardFormVisible, setCardFormVisibility] = useState(false); +// const [loading, setloading] = useState(false); +// const [popup, setpopup] = useState(false); +// const [Task, setTask] = useState(""); +// const [DisplayAmount, setDisplayAmount] = useState(""); +// const [amount, setAmount] = useState(0); +// const [focused, setfocused] = useState(false); +// const [checkoutResult, setCheckOutResult] = useState(""); +// const [userProfile, setUserProfile] = useState(null); +// const [topupCount, setTopupCount] = useState(0); +// const [merchant, setmerchant] = useState({}); +// const [creditcards, setcreditcards] = useState([]); +// const [activeIndex, setActiveIndex] = useState(0); + +// const init = async () => { +// setconnected(true) +// setloading(true) +// await countTransactions() +// let User = await DB.profile() +// let ses = await DB.session() +// console.log("tkn", ses.token) +// await getMerchant() +// await setUserProfile(User) +// setloading(false) +// } + +// const countTransactions = async () => { +// const SESSION = await DB.session() +// const USERPROFILE = await DB.profile() +// await REQUEST("transactions", "get", { +// Authorization: SESSION.token, +// card_number: SESSION.user.card_number +// }, {}, {}, function(res){ +// let count = 0 +// if(res.data.length > 0){ +// for(var x=0;x 5){ +// setloading(false) +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert("Top Up", "\nYou have reached your maximum top up for this day.") +// }, 700) +// : +// Alert.alert("Top Up", "\nYou have reached your maximum top up for this day.") +// } +// }, function(error){ +// console.log(error) +// }) +// } + +// const handleDelete = (id, token, uuid) => { +// Alert.alert( +// "Delete Payment Card", +// "Are you sure you want to delete this card?", +// [ +// { +// text: "NO", +// style: "cancel" +// }, +// { +// text: "YES", +// onPress: () => { +// setloading(true) +// Tokenization.initRemove(id, token, uuid).then((res) => { +// setloading(false) +// if(res.result == 'OK'){ +// Platform.OS == 'ios' ? +// setTimeout(() => { +// Alert.alert("Deleted Successfully", "\nYour credit card has successfully deleted.", [{text: "OK", onPress: async () => await init()}]) +// }, 700) +// : +// Alert.alert("Deleted Successfully", "\nYour credit card has successfully deleted.", [{text: "OK", onPress: async () => await init()}]) +// } +// }) +// } +// }, +// ] +// ) +// } + +// const getMerchant = async () => { +// const SESSION = await DB.session() +// const USERPROFILE = await DB.profile() +// await REQUEST("paymaya_tokens", "get", {'Authorization': SESSION.token}, {noID: true, value: SESSION.user.card_number}, {}, async (res) => { +// console.log("MERCHANT", res) +// if(res.data && res.data.cards){ +// setmerchant(res.data) +// console.log("MERCHANT", res.data.cards) +// // let accounts = await Tokenization.getAccounts(res.data[0].customer_id) +// setActiveIndex(0) +// for(var x=0;x { +// NetInfo.fetch().then(state => { +// console.log("Connection type", state.type); +// console.log("Is connected?", state.isConnected); +// if(state.isConnected){ +// init() +// }else{ +// Elements.nointernet2() +// } +// }) +// }, []) + +// const moneyFormat = (price, sign = 'PHP ') => { +// const pieces = parseFloat(price).toFixed(2).split('') +// let ii = pieces.length - 3 +// while ((ii-=3) > 0) { +// pieces.splice(ii, 0, ',') +// } +// return sign + pieces.join('') +// } + +// const setamount = (value) => { +// setDisplayAmount(moneyFormat(value).toString()) +// setAmount(value) +// } + +// let current = "" +// const onAmountChange = (value) => { +// if(value != current){ +// let parsed = parseFloat(clean(value)) +// if(parsed > 1000000) return false +// let formatted = formatCurrency(parsed) +// current = formatted +// setDisplayAmount(formatted) +// setAmount(parseFloat(parsed/100)) +// console.log(parsed, formatted, current) +// } +// } + +// const clean = (str) => { +// return str.replace("PHP ", "").replace(".", "").replace(",", "").toString() +// } + +// const formatCurrency = (val) => { +// let fv = parseFloat(val / 100).toFixed(2) +// return val.length == 6 ? "PHP " + fv.substr(0, 1) + "," + fv.substr(2, fv.length) : "PHP " + fv +// } + +// const validate = () => { +// if(amount < 100 || amount > 10000){ +// Alert.alert("Invalid top up amount", "\nPlease enter value between 100 - 10,000 pesos only.\n") +// return false +// } +// return true +// } + +// const onReload = (res, msg) => { +// // setTask(msg) +// // setpopup(true) +// // setTimeout(function(){ +// // setpopup(false) +// // }, 1200) +// // navigation.navigation.navigate("Main") +// } + +// const InitCheckout = async () => { + +// if(!validate()) return false + +// console.log('merchant', creditcards[activeIndex]) + +// if(creditcards.length > 0){ +// // HAS AN EXISTING MERCHANT TOKEN +// let mct = creditcards[activeIndex] + +// Platform.OS == 'ios' ? +// setTimeout(() => { +// Alert.alert("Confirm Top Up", "\nYour card number will be used to pay for "+parseFloat(amount).toFixed(2)+" points.", [ +// {text: "NO",style: "cancel"}, +// {text: "YES", +// onPress: async () => { +// setloading(true) +// let payout = await Tokenization.initPayOut(mct.customer_id, mct.cardTokenId, amount) +// console.log(payout) +// if(payout.isPaid){ +// if(payout.status == 'PAYMENT_SUCCESS'){ +// const SESSION = await DB.session() +// REQUEST('topup', 'post', { +// Authorization: SESSION.token, +// card_number: SESSION.user.card_number +// }, {}, { +// amount: amount +// }, function(res){ +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert( +// 'Transaction Completed!', +// `\nYou have successfully paid ${parseFloat(amount).toFixed(2)} points from your credit card \n**** **** **** ${mct.maskedPan}.`, +// [ +// { +// text: "OK", +// onPress: () => { +// navigation.navigation.reset({ +// index: 0, +// routes: [ { name: 'Main' } ], +// }) +// } +// }, +// ]) +// }, 700) : +// null +// }, function(error){ +// console.log(error) +// }) + +// }else{ +// setTimeout(() => alert(JSON.stringify(payout)), 300) +// } +// setloading(false) +// } +// } +// } +// ]) +// }, 300) +// : +// Alert.alert("Confirm Top Up", "\nYour card number will be used to pay for "+parseFloat(amount).toFixed(2)+" points.", [ +// {text: "NO",style: "cancel"}, +// {text: "YES", +// onPress: async () => { +// setloading(true) +// let payout = await Tokenization.initPayOut(mct.customer_id, mct.cardTokenId, amount) +// console.log(payout) +// if(payout.isPaid){ +// if(payout.status == 'PAYMENT_SUCCESS'){ +// Platform.OS == 'ios' ? setTimeout(() => { +// Alert.alert("Topup Success", `\nYou have successfully paid ${parseFloat(amount).toFixed(2)} points from your credit card ${payout.fundSource.details.masked}`) +// }, 300) : +// Alert.alert("Topup Success", `\nYou have successfully paid ${parseFloat(amount).toFixed(2)} points from your credit card ${payout.fundSource.details.masked}`) +// } +// }else{ +// setTimeout(() => alert(JSON.stringify(payout)), 300) +// } +// setloading(false) +// } +// } +// ]) +// }else{ +// //PRE REGISTRATION +// // navigation.navigation.navigate('TokenizationForm', {amount: amount, onBack: (res, msg) => onReload(res, msg)}) +// setCardFormVisibility(true); +// } +// return + +// // navigation.navigation.navigate('CheckOut', { +// // amount: amount, +// // data: {redirectUrl: "https://payments-web-sandbox.paymaya.com/authenticate?id=" + payout.id}, +// // // onBack: (res, msg) => props.route.params.onReload(res, msg) +// // onBack: (res, msg) => alert(msg) +// // }) + +// console.log('initializing...') +// setloading(true) +// const details = { +// "totalAmount": TotalAmount('PHP', amount), +// "buyer": Buyer( +// userProfile.data.firstname, +// userProfile.data.middlename, +// userProfile.data.lastname, +// "+" + userProfile.data.mobile, +// userProfile.data.email), +// "items": [Item('Top up', 1, 'N/A', 'Top up points', TotalAmount('PHP', amount))], +// "redirectUrl": { +// "success": "https://unioil.herokuapp.com/success", +// "failure": "https://unioil.herokuapp.com/failure", +// "cancel": "https://unioil.herokuapp.com/cancel" +// }, +// "requestReferenceNumber": Ref(), +// "metadata": {} +// } + +// CheckOut(PAYMENT_ENV, details, function(result){ +// console.log('CHECKOUT RESULT', result) +// setloading(false) +// navigation.navigation.navigate('CheckOut', {amount: amount, data: result, onBack: (res, msg) => onReload(res, msg)}) +// }) + +// } + +// const InitAddCard = async () => { +// setCardFormVisibility(true) +// } + +// if(!connected){ +// return ( +// +// +// +// { +// NetInfo.fetch().then(state => { +// console.log("Connection type", state.type); +// console.log("Is connected?", state.isConnected); +// if(state.isConnected){ +// init() +// }else{ +// Elements.nointernet2() +// } +// }) +// }} +// /> +// +// +// ) +// } + +// const getDisplayCard = (type) => { +// if(type == "visa") return Assets.icons.stpvisa +// else if(type == "master-card") return Assets.icons.stpmastercard +// else if(type == "jcb") return Assets.icons.stpjcb +// else return Assets.stpunknown +// } + +// return ( + +// +// {isCardFormVisible && ( +// 0 ? 'add' : 'create'} +// customerId={merchant && creditcards.length > 0 ? merchant.customer_id : ''} +// amount={amount} +// onDone={init} +// onBack={(res, msg) => onReload(res, msg)} +// onGoBack={() => setCardFormVisibility(false)} +// onSuccess={data => navigation.navigation.navigate('CheckOut', data)} +// /> +// )} +// +// +// +// +// +// Select Top Up Value Points +// +// + +// +// +// setamount(100)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> +// 100 +// +// setamount(500)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> +// 500 +// +// +// +// setamount(1000)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> +// 1000 +// +// setamount(5000)} style={{flex: 1, margin: 10, padding: 10, borderRadius: 10, borderColor: Theme.colors.primary, borderWidth: 2}}> +// 5000 +// +// +// + +// +// +// Or Enter Desired Value (maximum of 10,000 points) +// +// 0 ? "PHP " + parseFloat(amount) : amount} +// onFocus={() => +// setfocused(true) +// } +// onChangeText={(value) => onAmountChange(value)} +// containerStyle={{padding: 0}} +// inputContainerStyle={{padding: 0, borderBottomWidth: focused ? 1.75 : 1, borderColor: focused ? Theme.colors.accent : "gray" }} +// inputStyle={{padding: 0, fontFamily: 'Arial', fontSize: 16}} +// /> +// Note: Top Up to a maximum of five(5) times per day. +// + +// +// +// Payment Method +// +// + +// {creditcards.length > 0 ? +// +// +// +// +// +// +// {creditcards[activeIndex].cardType.toString().toUpperCase()} {creditcards[activeIndex].maskedPan} +// **** **** **** {creditcards[activeIndex].maskedPan} +// +// +// +// : +// +// +// +// +// +// +// +// 0000 0000 0000 0000 +// +// +// +// } + +// +// +// +// Add New Card +// +// + +// {creditcards.length > 0 && creditcards.map((card, index) => { +// return ( +// +// +// +// +// +// +// { +// Toast.show({ +// text: "**** **** **** " + card.maskedPan + " is selected.", +// buttonText: "", +// duration: 1500 +// }) +// setActiveIndex(index) +// }}> +// {card.cardType.toString().toUpperCase()} +// **** **** **** {card.maskedPan} +// +// +// +// +// +// +// +// ) +// })} +// +// +// +// {topupCount > 5 ? null : +// { +// InitCheckout() +// }} style={{padding: 20, paddingTop: 15, paddingBottom: 15, width: '100%', borderRadius: 10, backgroundColor: Theme.colors.primary}}> +// Next +// +// } +// + +// + +// +// ); +// } \ No newline at end of file diff --git a/app/screens/topup/utils.js b/app/screens/topup/utils.js new file mode 100644 index 00000000..299da7d8 --- /dev/null +++ b/app/screens/topup/utils.js @@ -0,0 +1,40 @@ +import REQUEST from '../../components/api/' +import DB from '../../components/storage' + +export const createMerchantDetails = async () => { + let u = await DB.profile() + return { + "firstName": u.data.firstname, + "middleName": u.data.middlename, + "lastName": u.data.lastname, + "birthday": new Date(u.data.birthdate).toISOString().split("T")[0], + "customerSince": new Date().toISOString().split("T")[0], + "sex": u.data.gender_code, + "contact": { + "phone": "+" + u.data.mobile, + "email": u.data.email + }, + "shippingAddress": { + "firstName": u.data.firstname, + "middleName": u.data.middlename, + "lastName": u.data.lastname, + "phone": "+" + u.data.mobile, + "email": u.data.email, + "line1": "", + "line2": "", + "city": "", + "state": u.data.address, + "zipCode": "", + "countryCode": "PH", + "shippingType": "" + }, + "billingAddress": { + "line1": "", + "line2": "", + "city": "", + "state": u.data.address, + "zipCode": "", + "countryCode": "PH" + } + } +} \ No newline at end of file diff --git a/app/screens/tracker/add.js b/app/screens/tracker/add.js new file mode 100644 index 00000000..eda2ee40 --- /dev/null +++ b/app/screens/tracker/add.js @@ -0,0 +1,134 @@ +import * as React from 'react'; +import { useState } from 'react'; +import { + TouchableOpacity, + View, + Text, + Alert +} from 'react-native'; +import { connect } from "react-redux"; +import { Container } from 'native-base'; +import CustomHeader from '../../components/header.js'; +import Theme from '../../components/theme.style.js'; +import Elements from '../../components/elements.js'; +import DB from '../../components/storage'; +import moment from 'moment'; +import CustomForm from './customform'; +import CustomSafeArea from '../../components/safeArea.component.js'; + +const Tracker = (navigation) => { + + const [fueltype, setfueltype] = useState(''); + const [datepicker, setdatepicker] = useState(false); + + // const fueltype = params.fuelType ? params.fuelType : ""; + const [date, setdate] = useState(null); + const [distance, setdistance] = useState(null); + const [liters, setliters] = useState(null); + + + const Save = async () => { + let SESSION = await DB.session() + let TRACKS = await DB.get("tracker") + let tracks = TRACKS ? JSON.parse(TRACKS) : [] + let data = { + card_number: SESSION.user.card_number, + id: tracks.length == 0 ? tracks.length + 1 : tracks[tracks.length - 1].id + 1, + date: date, + fueltype: fueltype, + distance: distance, + liters: liters + } + tracks.push(data) + + await DB.set("tracker", JSON.stringify(tracks), function(){ + navigation.route.params?.reload("added") + navigation.navigation.goBack() + // navigation.navigation.navigate("Tracker", {activity: 1}) + }, function(error){ + console.log("Failed to add:", error) + }) + } + + return ( + + + + + date ? true : null, + onClick: () => setdatepicker(true), + onChangeText: (val) => setdate(val) + }, + { + name: "Fuel Type", + value: fueltype, + disabled: true, + onFocus: () => fueltype ? true : null, + onClick: () => navigation.navigation.navigate("AddFuelType", {get: (val) => setfueltype(val)}) + }, + { + kb: "numeric", + name: "Distance Travelled", + icon: 'information-circle', + value: distance, + maxLength: 5, + onChangeText: (val) => setdistance(val), + onFocus: () => distance ? setdistance(distance.includes(" km") ? distance.replace(" km", "") : distance) : null, + onBlur: () => distance ? setdistance(distance + " km") : null, + onIconClick: () => Alert.alert("Information", "To measure distance travelled, make sure to reset your trip meter every time you gas full tank.") + }, + { + kb: "numeric", + name: "Liters Fueled up", + value: liters, + maxLength: 5, + onBlur: () => liters ? setliters(liters + " L") : null, + onChangeText: (val) => setliters(val) + } + ]} + /> + + + { + if(date && fueltype && distance && liters){ + Save() + }else{ + Alert.alert("Error", "Please fill-up all the fields.") + } + } + } + style={{ width: '90%', marginLeft: '5%', marginTop: Theme.screen.h / 3, borderRadius: 10, padding: 15, backgroundColor: Theme.colors.primary}}> + Save Tracker + + + + { + setdate(selectedDate ? moment(selectedDate).format("DD MMM YYYY") : date != null ? date : null) + setdatepicker(false) + }} + onCancel={() => setdatepicker(false)}/> + + ); +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(Tracker); diff --git a/app/screens/tracker/addfueltype.js b/app/screens/tracker/addfueltype.js new file mode 100644 index 00000000..641b39dc --- /dev/null +++ b/app/screens/tracker/addfueltype.js @@ -0,0 +1,367 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import { connect } from "react-redux"; +import { SafeAreaView, TouchableOpacity, ImageBackground, Button, View, Text, TextInput } from 'react-native'; +import {useNetInfo} from "@react-native-community/netinfo"; +import NetInfo from "@react-native-community/netinfo"; +import DB from '../../components/storage/'; +import REQUEST from '../../components/api/'; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Theme from '../../components/theme.style.js'; +import Elements from '../../components/elements.js'; +import Icon from '../../components/icons.js'; +import { Input, Divider } from 'react-native-elements'; + + +const Tracker = (navigation) => { + + const netInfo = useNetInfo(); + + const focused = false; + const disabled = false; + const [update, setupdate] = useState(true); + const [dialog, setdialog] = useState(false); + const [confirmdialog, setconfirmdialog] = useState(false); + + const [todelete, settodelete] = useState({}); + const [others, setothers] = useState([]); + const [fueltypes, setfueltypes] = useState([]); + const [error, setError] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + const [onFocus, setOnFocus] = useState(false); + + const init = async () => { + const SESSION = await DB.session() + const OTHERS = await DB.get("otherFuelType") + setothers(OTHERS ? JSON.parse(OTHERS) : []) + try{ + await REQUEST("fuel_types", "get", { + Authorization: SESSION.token + }, {}, {}, + async (res) => { + if(res.data){ + await setfueltypes(await res.data) + }else{ + console.log(res.message, res.data) + } + }, function(error){ + console.log(error) + } + ) + }catch(error){ + console.log(error) + } + } + + useEffect(() => { + NetInfo.fetch().then(state => { + console.log("Connection type", state.type); + console.log("Is connected?", state.isConnected); + if(state.isConnected){ + init() + }else{ + Elements.nointernet2() + } + }); + }, []) + + const renderFuelTypes = () => { + return fueltypes.map((data, index) => { + return ( + + send(data.name)} style={{flex: 1, padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > + {data.name} + + {index < fueltypes.length - 1 ? : null} + + ) + }) + } + + const renderOtherTypes = () => { + console.log("rendered", others) + return others.map((val, index) => { + return ( + + + send(val)} style={{flex: 1, justifyContent: 'center', alignItems: 'flex-start'}} > + {val} + + confirmDelete({index: index, value: val})} style={{flex: 0}}> + + + + + ) + }) + } + + const confirmDelete = (data) => { + settodelete(data); + setconfirmdialog(true) + } + + const removeType = () => { + let types = others + types.splice(todelete.index, 1) + setothers(types) + setconfirmdialog(false) + DB.set("otherFuelType", JSON.stringify(types), function(){ + console.log("Fuel Removed") + }, function(error){ + console.log(error) + }) + } + + const send = (val) => { + navigation.route.params.get(val) + navigation.navigation.goBack() + } + + const add = (value) => { + if(value.length == 0 || value == "") { + setError(true) + setErrorMessage("Fuel type is empty") + return + } + others.push(value) + setothers(others) + setdialog(false) + DB.set("otherFuelType", JSON.stringify(others), function(){ + console.log("New Fuel Added") + }, function(error){ + console.log(error) + }) + } + + return ( + + { + setOnFocus(true) + }} + onCancel={() => { + setdialog(false) + setError(false) + setOnFocus(false) + setErrorMessage(null) + }} + onSubmit={(value) => add(value)}/> + setconfirmdialog(false)} + onSubmit={(value) => removeType()}/> + navigation.navigation.goBack()} navigation={navigation} /> + + {renderFuelTypes()} + + Others + + {update ? renderOtherTypes() : null} + setdialog(true)} style={{flex: 1, flexDirection: 'row', padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > + + Add Fuel Type + + + + ); +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(Tracker) + +// export default function Tracker(navigation) { + +// const netInfo = useNetInfo(); + +// const focused = false; +// const disabled = false; +// const [update, setupdate] = useState(true); +// const [dialog, setdialog] = useState(false); +// const [confirmdialog, setconfirmdialog] = useState(false); + +// const [todelete, settodelete] = useState({}); +// const [others, setothers] = useState([]); +// const [fueltypes, setfueltypes] = useState([]); + +// const init = async () => { +// const SESSION = await DB.session() +// const OTHERS = await DB.get("otherFuelType") +// setothers(OTHERS ? JSON.parse(OTHERS) : []) +// try{ +// await REQUEST("fuel_types", "get", { +// Authorization: SESSION.token +// }, {}, {}, +// async (res) => { +// if(res.data){ +// await setfueltypes(await res.data) +// }else{ +// console.log(res.message, res.data) +// } +// }, function(error){ +// console.log(error) +// } +// ) +// }catch(error){ +// console.log(error) +// } +// } + +// useEffect(() => { +// NetInfo.fetch().then(state => { +// console.log("Connection type", state.type); +// console.log("Is connected?", state.isConnected); +// if(state.isConnected){ +// init() +// }else{ +// Elements.nointernet2() +// } +// }); +// }, []) + +// const renderFuelTypes = () => { +// return fueltypes.map((data, index) => { +// return ( +// +// send(data.name)} style={{flex: 1, padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > +// {data.name} +// +// {index < fueltypes.length - 1 ? : null} +// +// ) +// }) +// } + +// const renderOtherTypes = () => { +// console.log("rendered", others) +// return others.map((val, index) => { +// return ( +// +// +// send(val)} style={{flex: 1, justifyContent: 'center', alignItems: 'flex-start'}} > +// {val} +// +// confirmDelete({index: index, value: val})} style={{flex: 0}}> +// +// +// +// +// ) +// }) +// } + +// const confirmDelete = (data) => { +// settodelete(data); +// setconfirmdialog(true) +// } + +// const removeType = () => { +// let types = others +// types.splice(todelete.index, 1) +// setothers(types) +// setconfirmdialog(false) +// DB.set("otherFuelType", JSON.stringify(types), function(){ +// console.log("Fuel Removed") +// }, function(error){ +// console.log(error) +// }) +// } + +// const send = (val) => { +// navigation.route.params.get(val) +// navigation.navigation.goBack() +// // navigation.navigation.navigate(navigation.route.params.page, {fuelType: val}) +// } + +// const add = (value) => { +// others.push(value) +// setothers(others) +// setdialog(false) +// DB.set("otherFuelType", JSON.stringify(others), function(){ +// console.log("New Fuel Added") +// }, function(error){ +// console.log(error) +// }) +// } + +// return ( +// + +// setdialog(false)} +// onSubmit={(value) => add(value)} +// /> + +// setconfirmdialog(false)} +// onSubmit={(value) => removeType()} +// /> + +// navigation.navigation.goBack()} navigation={navigation} /> +// + +// {renderFuelTypes()} +// {/* send("Auto Gas")} style={{flex: 1, padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > +// Auto Gas +// +// +// send("Euro 5 Diesel")} style={{flex: 1, padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > +// Euro 5 Diesel +// +// +// send("Euro 5 Gasoline 91")} style={{flex: 1, padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > +// Euro 5 Gasoline 91 +// +// +// send("Euro 5 Gasoline 95")} style={{flex: 1, padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > +// Euro 5 Gasoline 95 +// +// +// send("Euro 5 Gasoline 97")} style={{flex: 1, padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > +// Euro 5 Gasoline 97 +// */} + +// +// Others +// + +// {update ? renderOtherTypes() : null} + +// setdialog(true)} style={{flex: 1, flexDirection: 'row', padding: 25, justifyContent: 'center', alignItems: 'flex-start'}} > +// +// Add Fuel Type +// + +// +// +// ); +// } \ No newline at end of file diff --git a/app/screens/tracker/customform.js b/app/screens/tracker/customform.js new file mode 100644 index 00000000..dfdc41ce --- /dev/null +++ b/app/screens/tracker/customform.js @@ -0,0 +1,80 @@ +import * as React from 'react'; +import { + useState, + useRef +} from 'react'; +import { connect } from "react-redux"; +import { + FormControl, + Stack, + Input, + Icon +} from 'native-base'; +import Theme from '../../components/theme.style'; + +const styles = { + activeText: {fontFamily: 'Arial', fontSize: 12, color: Theme.colors.accent}, + default: {fontFamily: 'Arial', paddingBottom: 0, fontSize: 14, color: Theme.colors.gray}, + input: {fontFamily: 'Arial', fontSize: 15}, + placeholder: {fontFamily: 'Arial', fontSize: 14, color: Theme.colors.lightGray} +} + +CustomForm = (props) => { + + let labelRefs = [useRef()] + const [focus, setfocus] = useState(false) + const [activeIndex, setactiveindex] = useState(0) + + const renderItem = (prop, index) => { + return ( + <> + labelRefs[index - 1] = r} + style={activeIndex == index && focus ? styles.activeText : styles.default}> + {prop.name} + + { + setactiveindex(index); + setfocus(true) + prop.onFocus ? prop.onFocus() : null + }} + maxLength={prop.maxLength || 255} + disabled={prop.disabled} + onTouchStart={() => prop.onClick ? prop.onClick() : null} + onChangeText={(val) => prop.onChangeText ? prop.onChangeText(val) : null} + onBlur={(e) => { + setactiveindex(0); + setfocus(false); + prop.onBlur ? prop.onBlur() : null + }} + style={[styles.input, { color: props.app_theme?.theme.colors.text }]} + /> + {prop.icon ? : null} + ) + } + + const renderItems = () => { + return props.items.map((item, i) => { + return renderItem(item, i) + }) + } + + return ( + + {renderItems()} + + ) +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(CustomForm); \ No newline at end of file diff --git a/app/screens/tracker/edit.js b/app/screens/tracker/edit.js new file mode 100644 index 00000000..d8e003df --- /dev/null +++ b/app/screens/tracker/edit.js @@ -0,0 +1,392 @@ +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import { connect } from "react-redux"; +import { SafeAreaView, TouchableOpacity, ImageBackground, Button, View, Text, TextInput, Alert } from 'react-native'; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Theme from '../../components/theme.style.js'; +import Elements from '../../components/elements.js'; +import CustomIcon from '../../components/icons.js'; +import DB from '../../components/storage'; +import CustomForm from './customform'; +import { Container } from 'native-base'; +import moment from 'moment'; +import CustomSafeArea from '../../components/safeArea.component.js'; + + +const Tracker = (navigation) => { + + const [focus, setfocus] = useState(false); + const [activeInput, setActiveInput] = useState(false); + const [datepicker, setdatepicker] = useState(false); + + const params = navigation.route ? navigation.route.params : null + const [date, setdate] = useState(null); + const [fueltype, setfueltype] = useState(null); + const [distance, setdistance] = useState(null); + const [liters, setliters] = useState(null); + + const [update, setupdate] = useState(false); + + if(params.edit && !update){ + setdate(params.edit.date) + setfueltype(params.edit.fueltype) + setdistance(params.edit.distance) + setliters(params.edit.liters) + setupdate(true) + } + + const isFocused = (index) => { + return focus && activeInput == index + } + + const DeleteTracker = async () => { + let TRACKS = await DB.get("tracker") + let tracks = TRACKS ? JSON.parse(TRACKS) : [] + let result = [] + for(var x=0;x { + let TRACKS = await DB.get("tracker") + let tracks = TRACKS ? JSON.parse(TRACKS) : [] + for(var x=0;x { + navigation.route.params?.reload(task) + navigation.navigation.goBack() + } + + return ( + + { + setdate(selectedDate ? moment(selectedDate).format("DD MMM YYYY") : date != null ? date : null) + setdatepicker(false) + }} + onCancel={() => setdatepicker(false)}/> + Alert.alert("Fuel Tracker", "Do you want to delete this fuel tracker?", + [{ + text: 'Cancel', + style: 'cancel', + },{ + text: 'OK', onPress: () => DeleteTracker() + },], + {cancelable: true} + )} style={{alignSelf: 'flex-end', right: 10}}> + + + }/> + + + + setdatepicker(true), + onChangeText: (val) => setdate(val) + }, + { + name: "Fuel Type", + value: fueltype, + disabled: true, + onClick: () => navigation.navigation.navigate("AddFuelType", {get: (val) => setfueltype(val)}) + }, + { + kb: "numeric", + name: "Distance Travelled", + icon: 'information-circle', + value: distance, + onChangeText: (val) => setdistance(val), + onFocus: () => distance ? setdistance(distance.includes(" km") ? distance.replace(" km", "") : distance) : null, + onBlur: () => distance ? setdistance(distance + " km") : null, + onIconClick: () => Alert.alert("Information", "To measure distance travelled, make sure to reset your trip meter every time you gas full tank.") + }, + { + kb: "numeric", + name: "Liters Fueled up", + value: liters, + onFocus: () => liters ? setliters(liters.includes(" km") ? liters.replace(" km", "") : liters) : null, + onBlur: () => liters ? setliters(liters + " L") : null, + onChangeText: (val) => setliters(val) + } + ]}/> + + + Save Tracker + + + + ); +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(Tracker) + +// export default function Tracker(navigation) { + +// const [focus, setfocus] = useState(false); +// const [activeInput, setActiveInput] = useState(false); +// const [datepicker, setdatepicker] = useState(false); + +// const params = navigation.route ? navigation.route.params : null +// const [date, setdate] = useState(null); +// const [fueltype, setfueltype] = useState(null); +// const [distance, setdistance] = useState(null); +// const [liters, setliters] = useState(null); + +// const [update, setupdate] = useState(false); + +// if(params.edit && !update){ +// setdate(params.edit.date) +// setfueltype(params.edit.fueltype) +// setdistance(params.edit.distance) +// setliters(params.edit.liters) +// setupdate(true) +// } + +// // console.log(params); + +// const isFocused = (index) => { +// return focus && activeInput == index +// } + +// const DeleteTracker = async () => { +// let TRACKS = await DB.get("tracker") +// let tracks = TRACKS ? JSON.parse(TRACKS) : [] +// let result = [] +// for(var x=0;x { +// let TRACKS = await DB.get("tracker") +// let tracks = TRACKS ? JSON.parse(TRACKS) : [] +// for(var x=0;x { +// navigation.route.params?.reload(task) +// navigation.navigation.goBack() +// } + +// return ( +// + +// { +// setdate(selectedDate ? moment(selectedDate).format("DD MMM YYYY") : date != null ? date : null) +// setdatepicker(false) +// }} +// onCancel={() => setdatepicker(false)} +// /> + +// Alert.alert("Fuel Tracker", "Do you want to delete this fuel tracker?", +// [{ +// text: 'Cancel', +// style: 'cancel', +// },{ +// text: 'OK', onPress: () => DeleteTracker() +// },], +// {cancelable: true} +// )} style={{alignSelf: 'flex-end', right: 10}}> +// +// +// } +// /> + +// +// + +// setdatepicker(true), +// onChangeText: (val) => setdate(val) +// }, +// { +// name: "Fuel Type", +// value: fueltype, +// disabled: true, +// onClick: () => navigation.navigation.navigate("AddFuelType", {get: (val) => setfueltype(val)}) +// }, +// { +// kb: "numeric", +// name: "Distance Travelled", +// icon: 'information-circle', +// value: distance, +// onChangeText: (val) => setdistance(val), +// onFocus: () => distance ? setdistance(distance.includes(" km") ? distance.replace(" km", "") : distance) : null, +// onBlur: () => distance ? setdistance(distance + " km") : null, +// onIconClick: () => Alert.alert("Information", "To measure distance travelled, make sure to reset your trip meter every time you gas full tank.") +// }, +// { +// kb: "numeric", +// name: "Liters Fueled up", +// value: liters, +// onFocus: () => liters ? setliters(liters.includes(" km") ? liters.replace(" km", "") : liters) : null, +// onBlur: () => liters ? setliters(liters + " L") : null, +// onChangeText: (val) => setliters(val) +// } +// ]} +// /> + +// + +// +// Save Tracker +// + +// + +// {/* + +// { +// setActiveInput(1) +// setfocus(true) +// setdatepicker(true) +// }} +// /> + +// navigation.navigation.navigate("AddFuelType")} +// /> + +// { +// setActiveInput(3) +// setfocus(true) +// distance ? setdistance(distance.includes(" km") ? distance.replace(" km", "") : distance) : null +// }} +// onBlur={() => distance ? setdistance(distance + " km") : null } +// onShowInfo={() => { +// setinfodialog(true) +// }} +// onChangeText={(value) => { +// setdistance(value) +// }} +// /> + +// { +// setActiveInput(4) +// setfocus(true) +// }} +// onChangeText={(value) => { +// setliters(value) +// }} +// /> + +// +// Save Tracker +// + +// */} + +// +// ); +// } \ No newline at end of file diff --git a/app/screens/tracker/index.js b/app/screens/tracker/index.js new file mode 100644 index 00000000..01cee9c6 --- /dev/null +++ b/app/screens/tracker/index.js @@ -0,0 +1,200 @@ +import * as React from 'react'; +import { + ScrollView, + TouchableOpacity, + ImageBackground, + View, + Text +} from 'react-native'; +import { + useState, + useEffect +} from 'react'; +import { connect } from "react-redux"; +import NetInfo from "@react-native-community/netinfo"; +import CustomHeader from '../../components/header.js'; +import Assets from '../../components/assets.manager.js'; +import Theme from '../../components/theme.style.js'; +import Icon from '../../components/icons.js'; +import Elements from '../../components/elements.js'; +import DB from '../../components/storage/'; +import moment from 'moment'; +import CustomSafeArea from '../../components/safeArea.component.js'; +// import {Toast} from 'native-base'; + +const Banner = (props) => { + return ( + + Track your gas consumption and mileage + + Add Tracker + + + ) +} + +const Tracker = (navigation) => { + + const params = navigation.route ? navigation.route.params : null + const [connected, setconnected] = useState(false) + const [popup, setpopup] = useState(false) + const [loaded, setloaded] = useState(false) + const [trackers, settrackers] = useState([]) + const [Task, setTask] = useState("") + + const fetch = (cn, data) => { + let res = [] + for(var x=0;x { + return new moment(a.date).format('x') - new moment(b.date).format('x') + }) + } + + const init = async () => { + setconnected(true) + const SESSION = await DB.session() + const TRACKS = await DB.get("tracker") + let tracks = TRACKS ? JSON.parse(TRACKS) : [] + // settrackers(tracks) + settrackers(fetch(SESSION.user.card_number, tracks)) + setloaded(true) + } + + useEffect(() => { + NetInfo.fetch().then(state => { + console.log("Connection type", state.type); + console.log("Is connected?", state.isConnected); + if(state.isConnected){ + init() + }else{ + Elements.nointernet2() + } + }) + }, []) + + const onReload = (task) => { + init() + console.log("TASK", task) + // Toast.show({ + // text: `Fuel Efficiency tracker ${task}`, + // buttonText: "", + // duration: 3000 + // }) + // setTask(task) + // setpopup(true) + // setTimeout(function(){ + // setpopup(false) + // }, 1200) + } + + const renderTrackings = () => { + return trackers.map((data, index) => { + let estimate = parseFloat(parseInt(data.distance) / parseInt(data.liters)).toFixed(2) + return ( + navigation.navigation.navigate("EditTracker", { + edit: data, + reload: (task) => onReload(task) + })} key={index} style={{padding: 15, flexDirection: 'column', backgroundColor: navigation.app_theme?.theme.dark ? navigation.app_theme?.theme.colors.border : '#fff', borderRadius: 5, borderWidth: 0.5, marginBottom: 5, borderColor: navigation.app_theme?.theme.dark ? navigation.app_theme?.theme.colors.border : 'lightgray', elevation: 0.5}}> + + {data.date} + + + km/L + + {estimate} + + + + + + + {data.fueltype} + + + + + + {Theme.formatter.CRNCY(data.distance.split(" ")[0]).replace(".00", "") + " km"} + + + + + + {Theme.formatter.CRNCY(data.liters.split(" ")[0]).replace(".00", "") + " L"} + + + ) + }) + } + + if(!connected){ + return ( + + + { + NetInfo.fetch().then(state => { + console.log("Connection type", state.type); + console.log("Is connected?", state.isConnected); + if(state.isConnected){ + init() + }else{ + Elements.nointernet2() + } + }) + }} + /> + + ) + } + + return ( + + + {trackers.length == 0 && loaded ? + navigation.navigation.navigate("AddTracker", { + reload: (task) => onReload(task) + })} /> : null} + {trackers.length > 0 && loaded ? + + + {renderTrackings()} + + + navigation.navigation.navigate("AddTracker", { + reload: (task) => onReload(task) + })} + style={{ + justifyContent: 'center', + alignItems: 'center', + position: 'absolute', + bottom: popup ? 65 : 15, + right: 15, + width: 60, + height: 60, + borderRadius: 30, + backgroundColor: Theme.colors.primary, + shadowColor: "#000000", + elevation: 7 + }} + > + + + : null + } + {/* */} + + ); +} + +const mapStateToProps = (state) => { + return { + app_theme: state.appThemeReducer.theme + } +} + +export default connect(mapStateToProps, null)(Tracker) \ No newline at end of file