|
|
@@ -1,143 +1,20 @@
|
|
|
-import {
|
|
|
- Image,
|
|
|
- View,
|
|
|
- Text,
|
|
|
- Pressable,
|
|
|
- Dimensions,
|
|
|
- StyleSheet,
|
|
|
- Modal,
|
|
|
- Animated,
|
|
|
- ScrollView,
|
|
|
- Button,
|
|
|
- BackHandler,
|
|
|
- Alert
|
|
|
-} from 'react-native';
|
|
|
+import { View, Text, Pressable, Dimensions, StyleSheet } from 'react-native';
|
|
|
|
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
|
-import { router, useFocusEffect } from 'expo-router';
|
|
|
+import { router } from 'expo-router';
|
|
|
import { CrossLogoSvg } from '../global/SVG';
|
|
|
import CouponTabViewComponent from '../global/couponTabView';
|
|
|
-import { useCallback, useEffect, useState } from 'react';
|
|
|
-import { useChargingStore } from '../../providers/scan_qr_payload_store';
|
|
|
-import { ArrowRightSvg } from '../global/SVG';
|
|
|
-import { useRef } from 'react';
|
|
|
-import NormalButton from '../global/normal_button';
|
|
|
-import axios from 'axios';
|
|
|
-import { chargeStationService } from '../../service/chargeStationService';
|
|
|
+import DisplayedOnlyCouponTabView from '../global/displayedOnlyCouponTabView';
|
|
|
+
|
|
|
const CouponPageComponent = () => {
|
|
|
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);
|
|
|
- console.log('processed', 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 || '永久'
|
|
|
- }));
|
|
|
-
|
|
|
- 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;
|
|
|
- };
|
|
|
|
|
|
- BackHandler.addEventListener('hardwareBackPress', onBackPress);
|
|
|
-
|
|
|
- return () => BackHandler.removeEventListener('hardwareBackPress', onBackPress);
|
|
|
- }, [])
|
|
|
- );
|
|
|
return (
|
|
|
<SafeAreaView className="flex-1 bg-white" edges={['top', 'right', 'left']}>
|
|
|
<View style={{ minHeight: screenHeight, flex: 1 }} className="mx-[5%]">
|
|
|
<View style={{ marginTop: 25 }}>
|
|
|
<Pressable
|
|
|
onPress={() => {
|
|
|
- cleanupData();
|
|
|
if (router.canGoBack()) {
|
|
|
router.back();
|
|
|
} else {
|
|
|
@@ -150,150 +27,9 @@ const CouponPageComponent = () => {
|
|
|
<Text style={{ fontSize: 45, marginVertical: 25 }}>優惠券</Text>
|
|
|
</View>
|
|
|
<View className="flex-1">
|
|
|
- <CouponTabViewComponent titles={['可用優惠券', '已使用/失效']} />
|
|
|
- {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">
|
|
|
- 馬上使用
|
|
|
- </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>
|
|
|
- )}
|
|
|
+ <DisplayedOnlyCouponTabView titles={['可用優惠券', '已使用/失效']} />
|
|
|
</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">優惠劵細節</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} 現金劵
|
|
|
- </Text>
|
|
|
- <Text className=" text-sm lg:text-base my-1 lg:mt-2 lg:mb-4 ">
|
|
|
- 有效期{' '}
|
|
|
- <Text className="font-[500] text-[#02677D]">
|
|
|
- 至 {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">立即使用</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">
|
|
|
- 服務條款與細則
|
|
|
- </Text>
|
|
|
- <View className="flex flex-col items-center space-y-2">
|
|
|
- <Text className="text-xs md:text-sm font-[300]">
|
|
|
- ・ 此券持有人可在本券有效期內於任何位於Crazy Charge
|
|
|
- 之香港分店換取同等價值充電服務,逾期無效。
|
|
|
- </Text>
|
|
|
- <Text className="text-xs lg:text-sm font-[300]">
|
|
|
- ・
|
|
|
- 此優惠券使用時,電費將以正常價格$3.5元/每度電計算,不適用於貓頭鷹時段或其他折扣時段的電力價格計算。
|
|
|
- </Text>
|
|
|
- <Text className="text-xs lg:text-sm font-[300]">
|
|
|
- ・ 此券不能用以套换現金或其他面值之現金券,持有人不獲現金或其他形式之找贖。
|
|
|
- </Text>
|
|
|
- <Text className="text-xs lg:text-sm font-[300]">
|
|
|
- ・ 使用者一旦在本 APP
|
|
|
- 內確認使用電子優惠券,即視為同意依優惠券規則進行消費抵扣,相關優惠券將立即從帳戶中扣除,且扣除後不得退還。
|
|
|
- </Text>
|
|
|
- <Text className="text-xs lg:text-sm font-[300]">
|
|
|
- ・
|
|
|
- 即便實際充電消費金額未達到電子優惠券的面額,亦不會就差額部分進行退款。優惠券的使用旨在為用戶提供充電優惠,而非現金兌換或退款工具。
|
|
|
- </Text>
|
|
|
- <Text className="text-xs lg:text-sm font-[300]">
|
|
|
- ・如有任何爭議,Crazy Charge
|
|
|
- 保留更改有關使用此現金券之條款及細則,而毋須另行通知。
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- </View>
|
|
|
- </View>
|
|
|
- </ScrollView>
|
|
|
- </Animated.View>
|
|
|
- </View>
|
|
|
- </Modal>
|
|
|
</SafeAreaView>
|
|
|
);
|
|
|
};
|