diff --git a/src/components/ViewRegistrationDialog.js b/src/components/ViewRegistrationDialog.js index 8f02dcf..d667a51 100644 --- a/src/components/ViewRegistrationDialog.js +++ b/src/components/ViewRegistrationDialog.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Dialog, DialogContent, @@ -9,12 +9,77 @@ import { } from './ui/dialog'; import { Button } from './ui/button'; import { Card } from './ui/card'; +import { Checkbox } from './ui/checkbox'; +import { Input } from './ui/input'; +import { Label } from './ui/label'; +import { Textarea } from './ui/textarea'; import { User, Mail, Phone, Calendar, UserCheck, Clock, FileText } from 'lucide-react'; import StatusBadge from './StatusBadge'; +import api from '../utils/api'; +import { toast } from 'sonner'; const ViewRegistrationDialog = ({ open, onOpenChange, user }) => { if (!user) return null; + const [formData, setFormData] = useState(null); + const [isSaving, setIsSaving] = useState(false); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const autoSaveTimeoutRef = useRef(null); + const pendingSaveRef = useRef(false); + + const leadSourceOptions = [ + 'Current member', + 'Friend', + 'OutSmart Magazine', + 'Search engine (Google etc.)', + "I've known about LOAF for a long time", + 'Other' + ]; + + const volunteerOptions = [ + 'Welcoming new members at events', + 'Sending out birthday cards', + 'Care Team Calls', + 'Sharing ideas for events', + 'Researching grants', + 'Applying for grants', + 'Assisting with TeatherLOAFers', + 'Assisting with ActiveLOAFers', + 'Assisting with weekday Lunch Bunch', + 'Uploading Photos to the Website', + 'Assisting with eNewsletter', + 'Other administrative task' + ]; + + useEffect(() => { + if (!open || !user) return; + const nextFormData = { + lead_sources: Array.isArray(user.lead_sources) ? user.lead_sources : [], + partner_first_name: user.partner_first_name || '', + partner_last_name: user.partner_last_name || '', + partner_is_member: Boolean(user.partner_is_member), + partner_plan_to_become_member: Boolean(user.partner_plan_to_become_member), + newsletter_publish_name: Boolean(user.newsletter_publish_name), + newsletter_publish_photo: Boolean(user.newsletter_publish_photo), + newsletter_publish_birthday: Boolean(user.newsletter_publish_birthday), + newsletter_publish_none: Boolean(user.newsletter_publish_none), + referred_by_member_name: user.referred_by_member_name || '', + volunteer_interests: Array.isArray(user.volunteer_interests) ? user.volunteer_interests : [], + scholarship_requested: Boolean(user.scholarship_requested), + scholarship_reason: user.scholarship_reason || '' + }; + setFormData(nextFormData); + setHasUnsavedChanges(false); + }, [open, user]); + + useEffect(() => { + return () => { + if (autoSaveTimeoutRef.current) { + clearTimeout(autoSaveTimeoutRef.current); + } + }; + }, []); + const formatDate = (dateString) => { if (!dateString) return '—'; return new Date(dateString).toLocaleDateString('en-US', { @@ -44,6 +109,91 @@ const ViewRegistrationDialog = ({ open, onOpenChange, user }) => { return phone; }; + const saveProfile = async (showToast = true) => { + if (!formData) return; + if (isSaving) { + pendingSaveRef.current = true; + return; + } + + setIsSaving(true); + try { + await api.put('/users/profile', { + lead_sources: formData.lead_sources, + partner_first_name: formData.partner_first_name, + partner_last_name: formData.partner_last_name, + partner_is_member: formData.partner_is_member, + partner_plan_to_become_member: formData.partner_plan_to_become_member, + newsletter_publish_name: formData.newsletter_publish_name, + newsletter_publish_photo: formData.newsletter_publish_photo, + newsletter_publish_birthday: formData.newsletter_publish_birthday, + newsletter_publish_none: formData.newsletter_publish_none, + referred_by_member_name: formData.referred_by_member_name, + volunteer_interests: formData.volunteer_interests, + scholarship_requested: formData.scholarship_requested, + scholarship_reason: formData.scholarship_reason + }); + setHasUnsavedChanges(false); + if (showToast) { + toast.success('Registration details saved'); + } + } catch (error) { + if (showToast) { + toast.error(error.response?.data?.detail || 'Failed to save registration details'); + } + } finally { + setIsSaving(false); + if (pendingSaveRef.current) { + pendingSaveRef.current = false; + saveProfile(showToast); + } + } + }; + + const scheduleAutoSave = () => { + setHasUnsavedChanges(true); + if (autoSaveTimeoutRef.current) { + clearTimeout(autoSaveTimeoutRef.current); + } + autoSaveTimeoutRef.current = setTimeout(() => { + saveProfile(false); + }, 800); + }; + + const handleInputChange = (e) => { + const { name, value } = e.target; + setFormData(prev => { + const next = { ...prev, [name]: value }; + return next; + }); + scheduleAutoSave(); + }; + + const handleCheckboxChange = (name, checked) => { + setFormData(prev => ({ ...prev, [name]: checked })); + scheduleAutoSave(); + }; + + const handleLeadSourceChange = (source) => { + setFormData(prev => { + const sources = prev.lead_sources.includes(source) + ? prev.lead_sources.filter((item) => item !== source) + : [...prev.lead_sources, source]; + return { ...prev, lead_sources: sources }; + }); + scheduleAutoSave(); + }; + + const handleVolunteerChange = (option) => { + setFormData(prev => { + const interests = prev.volunteer_interests.includes(option) + ? prev.volunteer_interests.filter((item) => item !== option) + : [...prev.volunteer_interests, option]; + return { ...prev, volunteer_interests: interests }; + }); + scheduleAutoSave(); + }; + const InfoRow = ({ icon: Icon, label, value }) => (
@@ -109,10 +259,214 @@ const ViewRegistrationDialog = ({ open, onOpenChange, user }) => { Registration Details - + + {formData && ( + <> + {/* How Did You Hear About Us */} + +

+ How Did You Hear About Us? * +

+
+ {leadSourceOptions.map((source) => ( +
+ handleLeadSourceChange(source)} + /> + +
+ ))} +
+
+ + {/* Partner Information */} + +

+ Partner Information (Optional) +

+
+
+ + +
+
+ + +
+
+
+
+ handleCheckboxChange('partner_is_member', checked)} + /> + +
+
+ handleCheckboxChange('partner_plan_to_become_member', checked)} + /> + +
+
+
+ + {/* Newsletter Preferences */} + +

+ Newsletter Publication Preferences * +

+

+ Please check what information may be published in LOAF Newsletter +

+
+
+ handleCheckboxChange('newsletter_publish_name', checked)} + /> + +
+
+ handleCheckboxChange('newsletter_publish_photo', checked)} + /> + +
+
+ handleCheckboxChange('newsletter_publish_birthday', checked)} + /> + +
+
+ handleCheckboxChange('newsletter_publish_none', checked)} + /> + +
+
+
+ + {/* Referral */} + +

+ Referral +

+
+ + +

+ If referred by a current member, you may skip the event attendance requirement. +

+
+
+ + {/* Volunteer Interests */} + +

+ Volunteer Interests (Optional) +

+

+ I may at some time be interested in volunteering with LOAF in the following ways (training is provided) +

+
+ {volunteerOptions.map((option) => ( +
+ handleVolunteerChange(option)} + /> + +
+ ))} +
+
+ + {/* Scholarship Request */} + +
+ handleCheckboxChange('scholarship_requested', checked)} + /> + +
+

+ Scholarship information is kept confidential +

+ {formData.scholarship_requested && ( +
+ +