| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- import {
- Image,
- View,
- Text,
- Pressable,
- Dimensions,
- StyleSheet,
- Modal,
- Animated,
- ScrollView,
- Button,
- BackHandler,
- Alert
- } 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';
- //this is from optionPage => 優惠券
- const SelectCouponComponent = () => {
- 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 || '永久'
- }));
- 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 }}>優惠券</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>
- )}
- </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>
- );
- };
- const styles = StyleSheet.create({
- floatingButton: {
- elevation: 5,
- shadowColor: '#000',
- shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.25,
- shadowRadius: 3.84
- }
- });
- export default SelectCouponComponent;
|