323 lines
12 KiB
TypeScript
323 lines
12 KiB
TypeScript
import React, { useState } from "react";
|
||
import { View, ScrollView, Image, TouchableOpacity } from "react-native";
|
||
import { router, useLocalSearchParams } from "expo-router";
|
||
import ScreenWrapper from "~/components/ui/ScreenWrapper";
|
||
import BackButton from "~/components/ui/backButton";
|
||
import { Text } from "~/components/ui/text";
|
||
import { Button } from "~/components/ui/button";
|
||
import { ROUTES } from "~/lib/routes";
|
||
import { Icons } from "~/assets/icons";
|
||
import { Check } from "lucide-react-native";
|
||
import { useAuthWithProfile } from "~/lib/hooks/useAuthWithProfile";
|
||
import { useTranslation } from "react-i18next";
|
||
|
||
const campaigns = [
|
||
{
|
||
id: "1",
|
||
title: "Fresh Fruits for Improving Children’s Health",
|
||
image: Icons.profileImage,
|
||
progress: 0.8,
|
||
raised: "$54,090",
|
||
timeLeft: "8 Hours left",
|
||
},
|
||
{
|
||
id: "2",
|
||
title: "Support Local Farmers Market Initiative",
|
||
image: Icons.mainBG,
|
||
progress: 0.45,
|
||
raised: "$21,400",
|
||
timeLeft: "2 Days left",
|
||
},
|
||
{
|
||
id: "3",
|
||
title: "School Meal Program for Kids",
|
||
image: Icons.qrImage,
|
||
progress: 0.62,
|
||
raised: "$33,250",
|
||
timeLeft: "1 Day left",
|
||
},
|
||
];
|
||
|
||
export default function DonationScreen() {
|
||
const { t } = useTranslation();
|
||
const params = useLocalSearchParams<Record<string, string>>();
|
||
const { profile } = useAuthWithProfile();
|
||
const amountInCents = Number(params.amount ?? "0");
|
||
const baseAmount = isNaN(amountInCents)
|
||
? "0.00"
|
||
: (amountInCents / 100).toFixed(2);
|
||
|
||
const [donationType, setDonationType] = useState<"one-time" | "monthly">(
|
||
"monthly"
|
||
);
|
||
const [donationAmount, setDonationAmount] = useState(baseAmount);
|
||
const [donateAnonymously, setDonateAnonymously] = useState(false);
|
||
const [selectedCampaignId, setSelectedCampaignId] = useState<string | null>(
|
||
null
|
||
);
|
||
|
||
const isCampaignSelected = !!selectedCampaignId;
|
||
|
||
const displayName = donateAnonymously
|
||
? t("donation.anonymousLabel")
|
||
: profile?.fullName || t("donation.displayNameFallback");
|
||
|
||
const goToConfirm = (options?: { skipped?: boolean }) => {
|
||
const selectedCampaign = campaigns.find((c) => c.id === selectedCampaignId);
|
||
|
||
router.push({
|
||
pathname: ROUTES.TRANSACTION_CONFIRM,
|
||
params: {
|
||
...params,
|
||
donationSkipped: options?.skipped ? "true" : "false",
|
||
donationType,
|
||
donationAmount,
|
||
donateAnonymously: donateAnonymously ? "true" : "false",
|
||
donationCampaignId: selectedCampaign?.id ?? "",
|
||
donationCampaignTitle: selectedCampaign?.title ?? "",
|
||
fromSelectRecipientFlow: "true",
|
||
},
|
||
});
|
||
};
|
||
|
||
const handleSkip = () => {
|
||
goToConfirm({ skipped: true });
|
||
};
|
||
|
||
const handleDonate = () => {
|
||
goToConfirm({ skipped: false });
|
||
};
|
||
|
||
return (
|
||
<ScreenWrapper edges={[]}>
|
||
<BackButton />
|
||
|
||
<ScrollView
|
||
className="flex-1 bg-white"
|
||
showsVerticalScrollIndicator={false}
|
||
>
|
||
<View className="pt-4 pb-8">
|
||
<View className="px-5">
|
||
<Text className="text-3xl font-dmsans-bold text-gray-900 mb-2">
|
||
{t("donation.title")}
|
||
</Text>
|
||
<Text className="text-base font-dmsans text-gray-500 mb-6">
|
||
{t("donation.subtitle")}
|
||
</Text>
|
||
</View>
|
||
|
||
<ScrollView
|
||
horizontal
|
||
showsHorizontalScrollIndicator={false}
|
||
pagingEnabled
|
||
snapToAlignment="center"
|
||
decelerationRate="fast"
|
||
className="px-5 pr-5 py-5"
|
||
contentContainerStyle={{ paddingRight: 20 }}
|
||
>
|
||
{campaigns.map((campaign) => {
|
||
const isSelected = selectedCampaignId === campaign.id;
|
||
return (
|
||
<TouchableOpacity
|
||
key={campaign.id}
|
||
activeOpacity={0.9}
|
||
onPress={() => setSelectedCampaignId(campaign.id)}
|
||
>
|
||
<View
|
||
className="mr-4 bg-white rounded-2xl"
|
||
style={{
|
||
width: 320,
|
||
height: 280,
|
||
shadowColor: "#000",
|
||
shadowOpacity: 0.08,
|
||
shadowRadius: 20,
|
||
shadowOffset: { width: 0, height: 6 },
|
||
elevation: 4,
|
||
borderWidth: isSelected ? 2 : 0,
|
||
borderColor: isSelected
|
||
? "rgba(19,83,53,0.5)"
|
||
: "transparent",
|
||
}}
|
||
>
|
||
<Image
|
||
source={campaign.image}
|
||
resizeMode="cover"
|
||
style={{ width: "100%", height: 150 }}
|
||
/>
|
||
|
||
{/* Content */}
|
||
<View className="p-4 flex-1 justify-between">
|
||
<Text
|
||
numberOfLines={1}
|
||
ellipsizeMode="tail"
|
||
className="text-base font-dmsans-medium text-gray-900 mb-3"
|
||
>
|
||
{campaign.title}
|
||
</Text>
|
||
|
||
{/* Progress bar */}
|
||
<View className="w-full h-1.5 rounded-full bg-gray-200 mb-2 overflow-hidden">
|
||
<View
|
||
style={{ width: `${campaign.progress * 100}%` }}
|
||
className="h-full bg-primary"
|
||
/>
|
||
</View>
|
||
|
||
<View className="flex-row justify-between items-center mb-2">
|
||
<Text className="text-xs font-dmsans text-gray-500">
|
||
{t("donation.donationRaisedLabel")}
|
||
</Text>
|
||
<Text className="text-xs font-dmsans text-gray-500">
|
||
{Math.round(campaign.progress * 100)}%
|
||
</Text>
|
||
</View>
|
||
|
||
<View className="flex-row justify-between items-center mt-2">
|
||
<Text className="text-lg font-dmsans-bold text-gray-900">
|
||
{campaign.raised}
|
||
</Text>
|
||
<Text className="text-xs font-dmsans text-gray-500">
|
||
{campaign.timeLeft}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</TouchableOpacity>
|
||
);
|
||
})}
|
||
</ScrollView>
|
||
|
||
<View
|
||
className="px-5 mt-6"
|
||
style={{ opacity: isCampaignSelected ? 1 : 0.4 }}
|
||
pointerEvents={isCampaignSelected ? "auto" : "none"}
|
||
>
|
||
<View className="bg-gray-100 rounded-xl p-4">
|
||
<Text className="text-lg font-dmsans-bold text-gray-900 mb-4 text-center">
|
||
{t("donation.chooseAmountTitle")}
|
||
</Text>
|
||
|
||
{/* One-Time / Monthly toggle */}
|
||
<View className="flex-row bg-white rounded-full p-1 mb-4">
|
||
<TouchableOpacity
|
||
className={`flex-1 rounded-full py-2 items-center justify-center ${
|
||
donationType === "one-time" ? "bg-primary" : ""
|
||
}`}
|
||
activeOpacity={0.8}
|
||
onPress={() => setDonationType("one-time")}
|
||
>
|
||
<Text
|
||
className={`text-sm font-dmsans-medium ${
|
||
donationType === "one-time"
|
||
? "text-white"
|
||
: "text-gray-600"
|
||
}`}
|
||
>
|
||
{t("donation.donationTypeOneTime")}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity
|
||
className={`flex-1 rounded-full py-2 items-center justify-center flex-row gap-1 ${
|
||
donationType === "monthly" ? "bg-primary" : ""
|
||
}`}
|
||
activeOpacity={0.8}
|
||
onPress={() => setDonationType("monthly")}
|
||
>
|
||
<Text
|
||
className={`text-sm font-dmsans-medium ${
|
||
donationType === "monthly"
|
||
? "text-white"
|
||
: "text-gray-600"
|
||
}`}
|
||
>
|
||
{t("donation.donationTypeMonthly")}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
{/* Amount row */}
|
||
<View className="flex-row items-center bg-white rounded-full px-4 py-3 mb-3">
|
||
<Text className="flex-1 text-2xl font-dmsans-bold text-gray-900">
|
||
{donationAmount}
|
||
</Text>
|
||
</View>
|
||
|
||
{/* Quick amount chips */}
|
||
<View className="flex-row justify-between mb-4">
|
||
{["5", "10", "25", "50", "100"].map((value) => {
|
||
const isActive = donationAmount === value;
|
||
return (
|
||
<TouchableOpacity
|
||
key={value}
|
||
activeOpacity={0.8}
|
||
onPress={() => setDonationAmount(value)}
|
||
>
|
||
<View
|
||
className={`px-4 py-2 rounded-full border ${
|
||
isActive
|
||
? "bg-primary border-primary"
|
||
: "bg-white border-gray-200"
|
||
}`}
|
||
>
|
||
<Text
|
||
className={`text-sm font-dmsans-medium ${
|
||
isActive ? "text-white" : "text-gray-700"
|
||
}`}
|
||
>
|
||
${value}
|
||
</Text>
|
||
</View>
|
||
</TouchableOpacity>
|
||
);
|
||
})}
|
||
</View>
|
||
|
||
<TouchableOpacity
|
||
className="flex-row items-center mb-3"
|
||
activeOpacity={0.8}
|
||
onPress={() => setDonateAnonymously(!donateAnonymously)}
|
||
>
|
||
<View
|
||
className={`w-5 h-5 rounded-md border items-center justify-center mr-3 ${
|
||
donateAnonymously
|
||
? "bg-primary border-primary"
|
||
: "border-gray-400 bg-white"
|
||
}`}
|
||
>
|
||
{donateAnonymously && <Check size={14} color="#ffffff" />}
|
||
</View>
|
||
<Text className="text-sm font-dmsans text-gray-700">
|
||
{t("donation.donateAnonymouslyLabel")}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
|
||
<View className="bg-white rounded-xl px-4 py-3 mb-3">
|
||
<Text className="text-base font-dmsans-medium text-gray-900">
|
||
{displayName}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
|
||
<View className="px-5 pb-6 pt-2 flex-row gap-3 bg-white">
|
||
<View className="flex-1">
|
||
<Button className="bg-gray-100 rounded-full" onPress={handleSkip}>
|
||
<Text className="text-gray-700 font-dmsans-medium text-base text-center">
|
||
{t("donation.skipButton")}
|
||
</Text>
|
||
</Button>
|
||
</View>
|
||
<View className="flex-1">
|
||
<Button className="bg-primary rounded-full" onPress={handleDonate}>
|
||
<Text className="text-white font-dmsans-medium text-base text-center">
|
||
{t("donation.donateButton")}
|
||
</Text>
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
</ScreenWrapper>
|
||
);
|
||
}
|