import React, { useState, useEffect } from 'react'; import api from '../utils/api'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from './ui/dialog'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Label } from './ui/label'; import { Textarea } from './ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from './ui/select'; import { Card } from './ui/card'; import { toast } from 'sonner'; import { Loader2, Repeat, Search, Calendar, Heart, X, User } from 'lucide-react'; const CreateSubscriptionDialog = ({ open, onOpenChange, onSuccess }) => { // Search state const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [selectedUser, setSelectedUser] = useState(null); const [searchLoading, setSearchLoading] = useState(false); const [allUsers, setAllUsers] = useState([]); // Plan state const [plans, setPlans] = useState([]); const [selectedPlan, setSelectedPlan] = useState(null); const [useCustomPeriod, setUseCustomPeriod] = useState(false); // Form state const [formData, setFormData] = useState({ plan_id: '', amount: '', payment_date: new Date().toISOString().split('T')[0], payment_method: 'cash', custom_period_start: new Date().toISOString().split('T')[0], custom_period_end: '', notes: '' }); const [loading, setLoading] = useState(false); // Fetch users and plans when dialog opens useEffect(() => { const fetchData = async () => { if (!open) return; try { const [usersResponse, plansResponse] = await Promise.all([ api.get('/admin/users'), api.get('/admin/subscriptions/plans') ]); setAllUsers(usersResponse.data); setPlans(plansResponse.data.filter(p => p.active)); } catch (error) { toast.error('Failed to load data'); } }; fetchData(); }, [open]); // Filter users based on search query useEffect(() => { if (!searchQuery.trim()) { setSearchResults([]); return; } setSearchLoading(true); const query = searchQuery.toLowerCase(); const filtered = allUsers.filter(user => user.first_name?.toLowerCase().includes(query) || user.last_name?.toLowerCase().includes(query) || user.email?.toLowerCase().includes(query) ).slice(0, 10); // Limit to 10 results setSearchResults(filtered); setSearchLoading(false); }, [searchQuery, allUsers]); // Update amount when plan changes useEffect(() => { if (selectedPlan && !formData.amount) { const suggestedAmount = (selectedPlan.suggested_price_cents || selectedPlan.minimum_price_cents || selectedPlan.price_cents) / 100; setFormData(prev => ({ ...prev, amount: suggestedAmount.toFixed(2) })); } }, [selectedPlan]); // Calculate donation breakdown const getAmountBreakdown = () => { if (!selectedPlan || !formData.amount) return null; const totalCents = Math.round(parseFloat(formData.amount) * 100); const minimumCents = selectedPlan.minimum_price_cents || selectedPlan.price_cents || 3000; const donationCents = Math.max(0, totalCents - minimumCents); return { total: totalCents, base: minimumCents, donation: donationCents }; }; const formatPrice = (cents) => { return `$${(cents / 100).toFixed(2)}`; }; const breakdown = getAmountBreakdown(); const handleSelectUser = (user) => { setSelectedUser(user); setSearchQuery(''); setSearchResults([]); }; const handleClearUser = () => { setSelectedUser(null); setFormData({ plan_id: '', amount: '', payment_date: new Date().toISOString().split('T')[0], payment_method: 'cash', custom_period_start: new Date().toISOString().split('T')[0], custom_period_end: '', notes: '' }); setSelectedPlan(null); setUseCustomPeriod(false); }; const handleSubmit = async (e) => { e.preventDefault(); if (!selectedUser) { toast.error('Please select a user'); return; } if (!formData.plan_id) { toast.error('Please select a subscription plan'); return; } if (!formData.amount || parseFloat(formData.amount) <= 0) { toast.error('Please enter a valid payment amount'); return; } // Validate minimum amount const amountCents = Math.round(parseFloat(formData.amount) * 100); const minimumCents = selectedPlan.minimum_price_cents || selectedPlan.price_cents || 3000; if (amountCents < minimumCents) { toast.error(`Amount must be at least ${formatPrice(minimumCents)}`); return; } if (useCustomPeriod && (!formData.custom_period_start || !formData.custom_period_end)) { toast.error('Please specify both start and end dates for custom period'); return; } setLoading(true); try { const payload = { plan_id: formData.plan_id, amount_cents: amountCents, payment_date: new Date(formData.payment_date).toISOString(), payment_method: formData.payment_method, override_plan_dates: useCustomPeriod, notes: formData.notes || null }; if (useCustomPeriod) { payload.custom_period_start = new Date(formData.custom_period_start).toISOString(); payload.custom_period_end = new Date(formData.custom_period_end).toISOString(); } await api.post(`/admin/users/${selectedUser.id}/activate-payment`, payload); toast.success(`Subscription created for ${selectedUser.first_name} ${selectedUser.last_name}!`); // Reset form handleClearUser(); onOpenChange(false); if (onSuccess) onSuccess(); } catch (error) { const errorMessage = error.response?.data?.detail || 'Failed to create subscription'; toast.error(errorMessage); } finally { setLoading(false); } }; const handleClose = () => { handleClearUser(); setSearchQuery(''); setSearchResults([]); onOpenChange(false); }; return ( Create Subscription Search for an existing member and create a subscription with manual payment processing.
{/* User Search Section */} {!selectedUser ? (
setSearchQuery(e.target.value)} className="pl-10 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple" /> {searchLoading && ( )}
{/* Search Results */} {searchResults.length > 0 && (
{searchResults.map((user) => ( ))}
)} {searchQuery && !searchLoading && searchResults.length === 0 && (

No members found matching "{searchQuery}"

)}
) : ( /* Selected User Card */

{selectedUser.first_name} {selectedUser.last_name}

{selectedUser.email}

)} {/* Payment Form - Only show when user is selected */} {selectedUser && ( <> {/* Plan Selection */}
{selectedPlan && (

{selectedPlan.description || `${selectedPlan.billing_cycle} subscription`}

)}
{/* Payment Amount */}
setFormData({ ...formData, amount: e.target.value })} className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple" required /> {selectedPlan && (

Minimum: {formatPrice(selectedPlan.minimum_price_cents || selectedPlan.price_cents || 3000)}

)}
{/* Amount Breakdown */} {breakdown && breakdown.total >= breakdown.base && (
Membership Fee: {formatPrice(breakdown.base)}
{breakdown.donation > 0 && (
Additional Donation: {formatPrice(breakdown.donation)}
)}
Total: {formatPrice(breakdown.total)}
)} {/* Payment Date */}
setFormData({ ...formData, payment_date: e.target.value })} className="pl-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple" required />
{/* Payment Method */}
{/* Subscription Period */}
setUseCustomPeriod(e.target.checked)} className="rounded border-[var(--neutral-800)]" />
{useCustomPeriod ? (
setFormData({ ...formData, custom_period_start: e.target.value })} className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple" required={useCustomPeriod} />
setFormData({ ...formData, custom_period_end: e.target.value })} className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple" required={useCustomPeriod} />
) : ( selectedPlan && (
{selectedPlan.custom_cycle_enabled ? ( <>

Plan uses custom billing cycle:
{(() => { const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const startMonth = months[(selectedPlan.custom_cycle_start_month || 1) - 1]; const endMonth = months[(selectedPlan.custom_cycle_end_month || 12) - 1]; return `${startMonth} ${selectedPlan.custom_cycle_start_day} - ${endMonth} ${selectedPlan.custom_cycle_end_day} (recurring annually)`; })()}

Subscription will end on the upcoming cycle end date based on today's date.

) : (

Will use plan's billing cycle: {selectedPlan.billing_cycle}
Starts today, ends {selectedPlan.billing_cycle === 'monthly' ? '30 days' : selectedPlan.billing_cycle === 'quarterly' ? '90 days' : selectedPlan.billing_cycle === 'yearly' ? '1 year' : selectedPlan.billing_cycle === 'lifetime' ? 'lifetime' : '1 year'} from now

)}
) )}
{/* Notes */}