| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- // app/(auth)/(tabs)/(home)/selectCoupon.tsx
- import {
- Image,
- View,
- Text,
- Pressable,
- Dimensions,
- StyleSheet,
- Modal,
- Animated,
- ScrollView,
- BackHandler
- } from 'react-native';
- import { SafeAreaView } from 'react-native-safe-area-context';
- import { router, useFocusEffect } from 'expo-router';
- import { CrossLogoSvg } from '../../../../component/global/SVG';
- import CouponTabViewComponent from '../../../../component/global/couponTabView';
- import { useCallback, useEffect, useState } from 'react';
- import { useChargingStore } from '../../../../providers/scan_qr_payload_store';
- import { ArrowRightSvg } from '../../../../component/global/SVG';
- import { useRef } from 'react';
- import NormalButton from '../../../../component/global/normal_button';
- import { useTranslation } from '../../../../util/hooks/useTranslation';
- //this is from optionPage => 優惠券
- const SelectCouponComponent = () => {
- const { t } = useTranslation();
- const screenHeight = Dimensions.get('window').height;
- const {
- promotion_code,
- coupon_detail,
- stationID,
- setSumOfCoupon,
- setCurrentPriceStore,
- current_price_store,
- setProcessedCouponStore,
- setPromotionCode,
- setCouponDetail,
- setTotalPower
- } = useChargingStore();
- const [scaleValue, setScaleValue] = useState(1);
- const [isBottomSheetVisible, setIsBottomSheetVisible] = useState(false);
- const [processedCoupons, setProcessedCoupons] = useState([]);
- const translateY = useRef(new Animated.Value(300)).current;
- const showBottomSheet = () => {
- setIsBottomSheetVisible(true);
- Animated.timing(translateY, {
- toValue: 0,
- duration: 300,
- useNativeDriver: true
- }).start();
- };
- const hideBottomSheet = () => {
- Animated.timing(translateY, {
- toValue: 0,
- duration: 0,
- useNativeDriver: true
- }).start(() => setIsBottomSheetVisible(false));
- };
- //process coupon so that coupons with same expire date and amount are grouped together to show abcoupon x 2
- useEffect(() => {
- if (Array.isArray(coupon_detail) && coupon_detail.length > 0) {
- const processed = processCoupons(coupon_detail);
- setProcessedCoupons(processed);
- setProcessedCouponStore(processed);
- }
- }, [coupon_detail]);
- //fetch original price for coupon valid calculation
- const processCoupons = (coupon_details_array: any) => {
- //coupon_details_array contains all information. i skim it down here.
- if (!coupon_details_array || coupon_details_array.length === 0) {
- return [];
- }
- const skimmedDownArray = coupon_details_array?.map((couponDetailObj: any) => ({
- amount: couponDetailObj.coupon.amount,
- id: couponDetailObj.id,
- expire_date: couponDetailObj.expire_date || t('common.permanent')
- }));
- const totalCouponAmount = skimmedDownArray.reduce((acc: number, coupon: any) => acc + coupon.amount, 0);
- setSumOfCoupon(totalCouponAmount);
- //process the skimmed array by combining coupons with same expire_date and amount
- const processedArray = (skimmedDownArray: { amount: number; id: string; expire_date: string }[]) => {
- const groupedCoupons: { [key: string]: any } = {};
- for (const coupon of skimmedDownArray) {
- const key = `${coupon.amount}-${coupon.expire_date}`;
- if (!groupedCoupons[key]) {
- groupedCoupons[key] = {
- coupon_detail: {
- amount: coupon.amount,
- expire_date: coupon.expire_date
- },
- frequency: 1
- };
- } else {
- groupedCoupons[key].frequency++;
- }
- }
- return Object.values(groupedCoupons);
- };
- return processedArray(skimmedDownArray);
- };
- const cleanupData = () => {
- setPromotionCode([]);
- setCouponDetail([]);
- setProcessedCouponStore([]);
- setSumOfCoupon(0);
- setTotalPower(null);
- };
- // Add this effect to handle Android back button
- useFocusEffect(
- useCallback(() => {
- const onBackPress = () => {
- cleanupData();
- if (router.canGoBack()) {
- router.back();
- } else {
- router.replace('/scanQrPage');
- }
- return true;
- };
- const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress);
- return () => subscription.remove()
- }, [])
- );
- return (
- <SafeAreaView className="flex-1 bg-white" edges={['top', 'right', 'left']}>
- <View style={{ minHeight: screenHeight, flex: 1 }}>
- <View className="mx-[5%]" style={{ marginTop: 25 }}>
- <Pressable
- onPress={() => {
- cleanupData();
- if (router.canGoBack()) {
- router.back();
- } else {
- router.replace('/optionPage');
- }
- }}
- >
- <CrossLogoSvg />
- </Pressable>
- <Text style={{ fontSize: 45, marginVertical: 25 }}>{t('selectCoupons.coupons.title')}</Text>
- </View>
- <View className="flex-1">
- <CouponTabViewComponent titles={[t('selectCoupons.coupons.available'), t('selectCoupons.coupons.used_expired')]} />
- {promotion_code.length > 0 && (
- <View
- style={{
- position: 'absolute',
- alignItems: 'center',
- left: 0,
- right: 0,
- padding: 20,
- height: '100%',
- top: '65%'
- }}
- >
- <Pressable
- className="bg-white rounded-full items-center justify-center w-full flex flex-row"
- style={[styles.floatingButton, { transform: [{ scale: scaleValue }] }]}
- onPressIn={() => setScaleValue(0.96)}
- onPressOut={() => setScaleValue(1)}
- onPress={showBottomSheet}
- >
- <Text className="text-[#02677D] text-2xl lg:text-4xl text-center py-1 pr-4 lg:pr-6 font-[600] lg:py-4">
- {t('common.use_now')}
- </Text>
- <View className="flex mb-2 bg-[#02677D] rounded-full items-center justify-center w-10 h-10 relative">
- <ArrowRightSvg />
- <View className="rounded-full bg-[#02677D] h-5 w-5 absolute bottom-7 left-7 z-10 border border-white flex items-center justify-center">
- <Text className="text-white text-xs text-center">{promotion_code.length}</Text>
- </View>
- </View>
- </Pressable>
- </View>
- )}
- </View>
- </View>
- <Modal transparent={true} visible={isBottomSheetVisible} animationType="none">
- <View style={{ flex: 1 }}>
- <Pressable style={{ flex: 1 }} onPress={hideBottomSheet}>
- <View className="flex-1 bg-black/50" />
- </Pressable>
- <Animated.View
- style={{
- position: 'absolute',
- bottom: 0,
- left: 0,
- right: 0,
- height: '80%',
- backgroundColor: 'white',
- transform: [{ translateY }],
- borderTopLeftRadius: 30,
- borderTopRightRadius: 30,
- overflow: 'hidden'
- }}
- >
- <ScrollView
- className="flex-1 flex-col bg-white p-8 "
- contentContainerStyle={{ paddingBottom: 100 }}
- >
- <Text className="text-lg md:text-xl lg:text-2xl">{t('selectCoupons.coupon_details')}</Text>
- <View style={{ height: 1, backgroundColor: '#ccc', marginVertical: 24 }} />
- {/* coupon row */}
- {processedCoupons &&
- processedCoupons?.map((couponObj: any) => (
- <View
- key={`${couponObj.coupon_detail.amount}-${couponObj.coupon_detail.expire_date}`}
- className="flex flex-row items-center justify-between"
- >
- <View className="flex flex-row items-start ">
- <Image
- className="w-6 lg:w-8 xl:w-10 h-6 lg:h-8 xl:h-10"
- source={require('../../../../assets/couponlogo.png')}
- />
- <View
- key={couponObj.coupon_detail.id}
- className="flex flex-col ml-2 lg:ml-4 "
- >
- <Text className="text-base lg:text-xl ">
- ${couponObj.coupon_detail.amount} {t('selectCoupons.coupons.cash_voucher')}
- </Text>
- <Text className=" text-sm lg:text-base my-1 lg:mt-2 lg:mb-4 ">
- {t('selectCoupons.coupons.valid_until')}{' '}
- <Text className="font-[500] text-[#02677D]">
- {couponObj.coupon_detail.expire_date === t('common.permanent')
- ? t('common.permanent')
- : t('common.to_date', { date: couponObj.coupon_detail.expire_date.slice(0, 10) })}
- </Text>
- </Text>
- </View>
- </View>
- {/* x 1 */}
- <View className="flex flex-row items-center ">
- <Text>X </Text>
- <View className="w-8 h-8 rounded-full bg-[#02677D] flex items-center justify-center">
- <Text className="text-white text-center text-lg">
- {couponObj.frequency}
- </Text>
- </View>
- </View>
- </View>
- ))}
- <View style={{ height: 1, backgroundColor: '#ccc', marginVertical: 12 }} />
- {/* 服務條款 */}
- <NormalButton
- title={<Text className="text-white text-sm lg:text-lg">{t('common.use_now')}</Text>}
- onPress={() => {
- setIsBottomSheetVisible(false);
- router.push('/totalPayment');
- }}
- extendedStyle={{ marginTop: 12, marginBottom: 24 }}
- />
- <View>
- <View className="">
- <Text className="text-base md:text-lg lg:text-xl pb-2 lg:pb-3 xl:pb-4">
- {t('selectCoupons.coupon.terms_and_conditions')}
- </Text>
- <View className="flex flex-col items-center space-y-2">
- <Text className="text-xs md:text-sm font-[300]">
- {t('selectCoupons.coupon.term1')}
- </Text>
- <Text className="text-xs lg:text-sm font-[300]">
- {t('selectCoupons.coupon.term2')}
- </Text>
- <Text className="text-xs lg:text-sm font-[300]">
- {t('selectCoupons.coupon.term3')}
- </Text>
- <Text className="text-xs lg:text-sm font-[300]">
- {t('selectCoupons.coupon.term4')}
- </Text>
- <Text className="text-xs lg:text-sm font-[300]">
- {t('selectCoupons.coupon.term5')}
- </Text>
- <Text className="text-xs lg:text-sm font-[300]">
- {t('selectCoupons.coupon.term6')}
- </Text>
- </View>
- </View>
- </View>
- </ScrollView>
- </Animated.View>
- </View>
- </Modal>
- </SafeAreaView>
- );
- };
- const styles = StyleSheet.create({
- floatingButton: {
- elevation: 5,
- shadowColor: '#000',
- shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.25,
- shadowRadius: 3.84
- }
- });
- export default SelectCouponComponent;
|