import React, { useState, useEffect, useRef } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Alert, ActivityIndicator, Modal, ScrollView, Linking, Platform, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { router, useLocalSearchParams } from 'expo-router'; // import { usePaymentSheet } from '@stripe/stripe-react-native'; // Removed for web import { AppColors, BorderRadius, FontSizes, FontWeights, Spacing, Shadows } from '@/constants/theme'; import { useAuth } from '@/contexts/AuthContext'; import { api } from '@/services/api'; import { BeneficiaryMenu } from '@/components/ui/BeneficiaryMenu'; import type { Beneficiary } from '@/types'; // const STRIPE_API_URL = 'https://wellnuo.smartlaunchhub.com/api/stripe'; const SUBSCRIPTION_PRICE = 49; type SubscriptionState = 'active' | 'canceling' | 'none'; export default function SubscriptionScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const [isProcessing, setIsProcessing] = useState(false); const [isCanceling, setIsCanceling] = useState(false); const [beneficiary, setBeneficiary] = useState(null); const [isLoading, setIsLoading] = useState(true); const [showSuccessModal, setShowSuccessModal] = useState(false); // const [justSubscribed, setJustSubscribed] = useState(false); const [transactions, setTransactions] = useState<{ id: string; type: 'subscription' | 'one_time'; amount: number; currency: string; status: string; date: string; description: string; invoicePdf?: string; hostedUrl?: string; receiptUrl?: string; }[]>([]); // const [isLoadingTransactions, setIsLoadingTransactions] = useState(false); // const { initPaymentSheet, presentPaymentSheet } = usePaymentSheet(); // Removed for web // const { user } = useAuth(); // Unused here for now useEffect(() => { loadBeneficiary(); loadTransactions(); }, [id]); const loadTransactions = async () => { if (!id) return; // setIsLoadingTransactions(true); try { const response = await api.getTransactionHistory(parseInt(id, 10)); if (response.ok && response.data) { setTransactions(response.data.transactions); } } catch (error) { // Failed to load transactions } finally { // setIsLoadingTransactions(false); } }; const loadBeneficiary = async () => { if (!id) return; try { const response = await api.getWellNuoBeneficiary(parseInt(id, 10)); if (response.ok && response.data) { setBeneficiary(response.data); } } catch (error) { // Failed to load beneficiary } finally { setIsLoading(false); } }; const subscription = beneficiary?.subscription; // Determine subscription state const getSubscriptionState = (): SubscriptionState => { if (!subscription) return 'none'; const isStatusActive = subscription.status === 'active' || subscription.status === 'trialing'; const isNotExpired = !subscription.endDate || new Date(subscription.endDate) > new Date(); if (isStatusActive && isNotExpired) { return subscription.cancelAtPeriodEnd ? 'canceling' : 'active'; } return 'none'; }; const subscriptionState = getSubscriptionState(); const handleSubscribe = async () => { // Web Implementation alert('Subscriptions are currently only available in the mobile app.'); }; const handleCancelSubscription = () => { // Web Implementation alert('Please use the mobile app to manage your subscription.'); }; const confirmCancelSubscription = async () => { // Stub }; const handleReactivateSubscription = async () => { // Web Implementation alert('Please use the mobile app to manage your subscription.'); }; const formatDate = (date: Date) => { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }; const openReceipt = (url?: string) => { if (url) Linking.openURL(url); }; const handleSuccessModalClose = () => { setShowSuccessModal(false); router.replace(`/(tabs)/beneficiaries/${id}`); }; // Loading state if (isLoading) { return ( router.back()}> Subscription ); } if (!beneficiary) { return ( router.back()}> Subscription Unable to load beneficiary ); } // Render subscription status card based on state const renderStatusCard = () => { switch (subscriptionState) { case 'active': return ( Active Subscription {subscription?.endDate ? `Renews ${formatDate(new Date(subscription.endDate))}` : 'Renews monthly'} ${SUBSCRIPTION_PRICE} /month ); case 'canceling': return ( Subscription Ending Access until {subscription?.endDate ? formatDate(new Date(subscription.endDate)) : 'period end'} After this date, monitoring and alerts for {beneficiary.displayName} will stop. ); case 'none': default: return ( No Active Subscription Subscribe to unlock monitoring for {beneficiary.displayName} ${SUBSCRIPTION_PRICE} /month ); } }; // Render action button based on state const renderActionButton = () => { switch (subscriptionState) { case 'active': return ( {isCanceling ? ( ) : ( Cancel Subscription )} ); case 'canceling': return ( {isProcessing ? ( ) : ( <> Reactivate Subscription )} ); case 'none': default: return ( {isProcessing ? ( ) : ( <> Subscribe )} ); } }; return ( {/* Header */} router.back()}> Subscription {/* Status Card */} {renderStatusCard()} {/* Action Button */} {renderActionButton()} {/* Security note */} Secure payment by Stripe {/* Transaction History */} {transactions.length > 0 && ( Payment History {transactions.map((tx) => ( openReceipt(tx.invoicePdf || tx.hostedUrl || tx.receiptUrl)} disabled={!tx.invoicePdf && !tx.hostedUrl && !tx.receiptUrl} > {tx.description} {formatDate(new Date(tx.date))} ${tx.amount.toFixed(2)} {(tx.invoicePdf || tx.hostedUrl || tx.receiptUrl) && ( )} ))} )} Web version: Subscription management is limited. Please use mobile app. {/* Success Modal */} Subscription Active! Monitoring for {beneficiary?.name} is now enabled. Continue ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: AppColors.background, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: Spacing.md, paddingVertical: Spacing.sm, borderBottomWidth: 1, borderBottomColor: AppColors.border, }, backButton: { padding: Spacing.xs, }, headerTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, placeholder: { width: 32, }, centerContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, errorText: { fontSize: FontSizes.base, color: AppColors.textMuted, }, scrollContent: { flex: 1, }, content: { padding: Spacing.lg, paddingBottom: Spacing.xxl, }, contentCentered: { flexGrow: 1, justifyContent: 'center', }, // Status Card Styles statusCard: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.xl, alignItems: 'center', borderWidth: 2, borderColor: AppColors.success, ...Shadows.sm, }, statusCardCanceling: { borderColor: '#F59E0B', backgroundColor: '#FFFBEB', }, statusCardNone: { borderColor: AppColors.border, backgroundColor: AppColors.surface, }, statusIconActive: { width: 56, height: 56, borderRadius: 28, backgroundColor: AppColors.success, alignItems: 'center', justifyContent: 'center', marginBottom: Spacing.md, }, statusIconCanceling: { width: 56, height: 56, borderRadius: 28, backgroundColor: '#F59E0B', alignItems: 'center', justifyContent: 'center', marginBottom: Spacing.md, }, statusIconNone: { width: 56, height: 56, borderRadius: 28, backgroundColor: AppColors.surfaceSecondary, alignItems: 'center', justifyContent: 'center', marginBottom: Spacing.md, }, statusTitle: { fontSize: FontSizes.xl, fontWeight: FontWeights.bold, color: AppColors.textPrimary, marginBottom: Spacing.xs, }, statusTitleNone: { fontSize: FontSizes.xl, fontWeight: FontWeights.bold, color: AppColors.textSecondary, marginBottom: Spacing.xs, }, statusSubtitle: { fontSize: FontSizes.sm, color: AppColors.success, marginBottom: Spacing.md, }, statusSubtitleCanceling: { fontSize: FontSizes.sm, color: '#B45309', fontWeight: FontWeights.medium, marginBottom: Spacing.sm, }, statusSubtitleNone: { fontSize: FontSizes.sm, color: AppColors.textMuted, textAlign: 'center', marginBottom: Spacing.md, }, cancelingNote: { fontSize: FontSizes.sm, color: '#92400E', textAlign: 'center', lineHeight: 20, }, priceRow: { flexDirection: 'row', alignItems: 'baseline', }, priceAmount: { fontSize: FontSizes['2xl'], fontWeight: FontWeights.bold, color: AppColors.primary, }, pricePeriod: { fontSize: FontSizes.base, color: AppColors.textMuted, marginLeft: 2, }, // Action Section actionSection: { marginTop: Spacing.xl, gap: Spacing.md, }, primaryButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: Spacing.sm, backgroundColor: AppColors.primary, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, ...Shadows.sm, }, reactivateButton: { backgroundColor: AppColors.success, }, buttonDisabled: { opacity: 0.7, }, primaryButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, cancelButton: { paddingVertical: Spacing.sm, alignItems: 'center', }, cancelButtonText: { fontSize: FontSizes.sm, color: AppColors.textMuted, textDecorationLine: 'underline', }, securityRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: Spacing.xs, }, securityText: { fontSize: FontSizes.xs, color: AppColors.textMuted, }, // Transaction Section transactionSection: { marginTop: Spacing.xl, }, sectionTitle: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, marginBottom: Spacing.md, }, transactionItem: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', backgroundColor: AppColors.surface, padding: Spacing.md, borderRadius: BorderRadius.md, marginBottom: Spacing.sm, }, transactionLeft: { flex: 1, }, transactionDesc: { fontSize: FontSizes.sm, fontWeight: FontWeights.medium, color: AppColors.textPrimary, }, transactionDate: { fontSize: FontSizes.xs, color: AppColors.textMuted, marginTop: 2, }, transactionRight: { flexDirection: 'row', alignItems: 'center', gap: Spacing.xs, }, transactionAmount: { fontSize: FontSizes.sm, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, // Modal modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'center', alignItems: 'center', padding: Spacing.lg, }, modalContent: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.xl, width: '100%', maxWidth: 320, alignItems: 'center', ...Shadows.lg, }, modalIcon: { marginBottom: Spacing.md, }, modalTitle: { fontSize: FontSizes.xl, fontWeight: FontWeights.bold, color: AppColors.textPrimary, marginBottom: Spacing.sm, }, modalMessage: { fontSize: FontSizes.base, color: AppColors.textSecondary, }, modalButton: { backgroundColor: AppColors.primary, paddingHorizontal: Spacing.xl, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, marginTop: Spacing.lg, width: '100%', alignItems: 'center', }, modalButtonText: { color: AppColors.white, fontWeight: FontWeights.semibold, fontSize: FontSizes.base, }, });