From ed8d71bc768fc619c5240e0d596cec44bd4af113 Mon Sep 17 00:00:00 2001 From: Moataz Farid Date: Wed, 27 Nov 2024 20:30:43 +0200 Subject: [PATCH 1/2] adding payments and refactor services --- .env | 1 + src/App.tsx | 2 + src/components/Layout.tsx | 3 +- src/components/modals/BeneficiaryModal.tsx | 2 +- src/components/modals/DonationModal.tsx | 2 +- src/components/modals/DonorModal.tsx | 2 +- src/components/modals/FeedingRoundModal.tsx | 2 +- .../modals/FeedingRoundPhotosModal.tsx | 2 +- src/components/modals/PaymentModal.tsx | 241 ++++++++++++++++++ .../modals/TreasuryCategoryModal.tsx | 2 +- src/hooks/useFirebaseQuery.ts | 27 +- src/pages/PaymentList.tsx | 191 ++++++++++++++ src/services/firebase.ts | 112 +++++++- src/services/firebase/beneficiaryService.ts | 85 ++++++ src/services/firebase/constants.ts | 9 + src/services/firebase/donationService.ts | 70 +++++ src/services/firebase/donorService.ts | 66 +++++ src/services/firebase/feedingRoundService.ts | 116 +++++++++ src/services/firebase/index.ts | 12 + src/services/firebase/paymentService.ts | 119 +++++++++ src/services/firebase/treasuryService.ts | 94 +++++++ src/store/index.ts | 18 +- src/types/index.ts | 18 ++ 23 files changed, 1181 insertions(+), 15 deletions(-) create mode 100644 .env create mode 100644 src/components/modals/PaymentModal.tsx create mode 100644 src/pages/PaymentList.tsx create mode 100644 src/services/firebase/beneficiaryService.ts create mode 100644 src/services/firebase/constants.ts create mode 100644 src/services/firebase/donationService.ts create mode 100644 src/services/firebase/donorService.ts create mode 100644 src/services/firebase/feedingRoundService.ts create mode 100644 src/services/firebase/index.ts create mode 100644 src/services/firebase/paymentService.ts create mode 100644 src/services/firebase/treasuryService.ts diff --git a/.env b/.env new file mode 100644 index 0000000..083c815 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +NODE_ENV=development \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index b594b89..a092c91 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,7 @@ import FeedingRoundList from './pages/FeedingRoundList'; import TreasuryList from './pages/TreasuryList'; import TreasuryCategories from './pages/TreasuryCategories'; import BeneficiaryList from './pages/BeneficiaryList'; +import PaymentList from './pages/PaymentList'; import { useFirebaseQuery } from './hooks/useFirebaseQuery'; const queryClient = new QueryClient({ @@ -40,6 +41,7 @@ function AppContent() { } /> } /> } /> + } /> ); diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index d4ef5aa..63bb5fb 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Link, Outlet, useLocation } from 'react-router-dom'; -import { Heart, Users, Wallet, Utensils, Menu, CircleDollarSign, UserRound } from 'lucide-react'; +import { Heart, Users, Wallet, Utensils, Menu, CircleDollarSign, UserRound, CreditCard } from 'lucide-react'; const Layout: React.FC = () => { const location = useLocation(); @@ -13,6 +13,7 @@ const Layout: React.FC = () => { { name: 'Treasury', href: '/treasury', icon: Wallet }, { name: 'Treasury Categories', href: '/treasury-categories', icon: CircleDollarSign }, { name: 'Beneficiaries', href: '/beneficiaries', icon: UserRound }, + { name: 'Payments', href: '/payments', icon: CreditCard }, ]; const isActive = (path: string) => location.pathname === path; diff --git a/src/components/modals/BeneficiaryModal.tsx b/src/components/modals/BeneficiaryModal.tsx index 520d67f..14242e9 100644 --- a/src/components/modals/BeneficiaryModal.tsx +++ b/src/components/modals/BeneficiaryModal.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useForm } from 'react-hook-form'; import { X } from 'lucide-react'; -import { beneficiaryServices } from '../../services/firebase'; +import { beneficiaryServices } from '../../services/firebase/beneficiaryService'; import { useQueryClient } from '@tanstack/react-query'; import { Beneficiary, SupportType } from '../../types'; diff --git a/src/components/modals/DonationModal.tsx b/src/components/modals/DonationModal.tsx index c354f99..22ec6fc 100644 --- a/src/components/modals/DonationModal.tsx +++ b/src/components/modals/DonationModal.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useForm } from 'react-hook-form'; import { X } from 'lucide-react'; -import { donationServices } from '../../services/firebase'; +import { donationServices } from '../../services/firebase/donationService'; import { useQueryClient } from '@tanstack/react-query'; import { useStore } from '../../store'; import { useFirebaseQuery } from '../../hooks/useFirebaseQuery'; diff --git a/src/components/modals/DonorModal.tsx b/src/components/modals/DonorModal.tsx index edc8e4a..77fe1c3 100644 --- a/src/components/modals/DonorModal.tsx +++ b/src/components/modals/DonorModal.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useForm } from 'react-hook-form'; import { X } from 'lucide-react'; -import { donorServices } from '../../services/firebase'; +import { donorServices } from '../../services/firebase/donorService'; import { useQueryClient } from '@tanstack/react-query'; import { Donor } from '../../types/donor'; import { validateFirebaseConnection } from '../../config/firebase'; diff --git a/src/components/modals/FeedingRoundModal.tsx b/src/components/modals/FeedingRoundModal.tsx index 3e89696..749af5d 100644 --- a/src/components/modals/FeedingRoundModal.tsx +++ b/src/components/modals/FeedingRoundModal.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useForm } from 'react-hook-form'; import { X } from 'lucide-react'; -import { feedingRoundServices } from '../../services/firebase'; +import { feedingRoundServices } from '../../services/firebase/feedingRoundService'; import { useQueryClient } from '@tanstack/react-query'; import { useStore } from '../../store'; import { FeedingRound } from '../../types'; diff --git a/src/components/modals/FeedingRoundPhotosModal.tsx b/src/components/modals/FeedingRoundPhotosModal.tsx index 4882585..3034e99 100644 --- a/src/components/modals/FeedingRoundPhotosModal.tsx +++ b/src/components/modals/FeedingRoundPhotosModal.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { X, Link as LinkIcon, ExternalLink, Trash2 } from 'lucide-react'; import { useQueryClient } from '@tanstack/react-query'; -import { feedingRoundServices } from '../../services/firebase'; +import { feedingRoundServices } from '../../services/firebase/feedingRoundService'; import { FeedingRound } from '../../types'; import { format } from 'date-fns'; diff --git a/src/components/modals/PaymentModal.tsx b/src/components/modals/PaymentModal.tsx new file mode 100644 index 0000000..876589e --- /dev/null +++ b/src/components/modals/PaymentModal.tsx @@ -0,0 +1,241 @@ +import React from 'react'; +import { useForm } from 'react-hook-form'; +import { X } from 'lucide-react'; +import { paymentServices } from '../../services/firebase/paymentService'; +import { useQueryClient } from '@tanstack/react-query'; +import { useStore } from '../../store'; +import { Payment, PaymentType } from '../../types'; + +interface PaymentModalProps { + isOpen: boolean; + onClose: () => void; + payment?: Payment | null; + beneficiaryId?: string; +} + +interface PaymentFormData { + beneficiaryId: string; + amount: string; + categoryId: string; + date: string; + paymentType: PaymentType; + notes: string; +} + +export function PaymentModal({ isOpen, onClose, payment, beneficiaryId }: PaymentModalProps) { + const queryClient = useQueryClient(); + const { treasuryCategories, beneficiaries } = useStore(); + + const { register, handleSubmit, formState: { errors }, watch } = useForm({ + defaultValues: payment ? { + beneficiaryId: payment.beneficiaryId, + amount: String(payment.amount), + categoryId: payment.categoryId, + date: payment.date, + paymentType: payment.paymentType, + notes: payment.notes || '' + } : { + beneficiaryId: beneficiaryId || '', + amount: '', + categoryId: '', + date: new Date().toISOString().split('T')[0], + paymentType: 'ONE_TIME', + notes: '' + } + }); + + React.useEffect(() => { + console.log('PaymentModal mounted with beneficiaries:', beneficiaries); + }, []); + + const activeBeneficiaries = React.useMemo(() => { + console.log('Filtering beneficiaries:', beneficiaries); + if (!beneficiaries?.length) { + console.log('No beneficiaries to filter'); + return []; + } + + const filtered = beneficiaries.filter(b => { + const isActive = b.status === 'ACTIVE'; + console.log(`Beneficiary ${b.id} (${b.name}) status:`, b.status, 'isActive:', isActive); + return isActive; + }); + + console.log('Filtered beneficiaries:', filtered); + return filtered; + }, [beneficiaries]); + + const availableCategories = React.useMemo(() => + treasuryCategories.filter(c => c.balance > 0), + [treasuryCategories] + ); + + const onSubmit = async (data: PaymentFormData) => { + try { + if (!data.beneficiaryId && !beneficiaryId) { + alert('Please select a beneficiary'); + return; + } + + const paymentData = { + ...data, + amount: parseFloat(data.amount), + beneficiaryId: beneficiaryId || data.beneficiaryId, + treasuryId: data.categoryId, + representativeId: 'SYSTEM' + }; + + if (payment?.id) { + await paymentServices.update(payment.id, paymentData); + } else { + await paymentServices.create(paymentData); + } + + queryClient.invalidateQueries({ queryKey: ['payments'] }); + queryClient.invalidateQueries({ queryKey: ['treasury'] }); + onClose(); + } catch (error) { + console.error('Error saving payment:', error); + alert(error instanceof Error ? error.message : 'Failed to save payment'); + } + }; + + if (!isOpen) return null; + + return ( +
+
+
+ +
+
+ +
+ +
+
+

+ {payment ? 'Edit Payment' : 'New Payment'} +

+
+
+ +
+ {!beneficiaryId && ( +
+ + + {errors.beneficiaryId && ( +

{errors.beneficiaryId.message}

+ )} +
+ )} + +
+ + + {errors.categoryId && ( +

{errors.categoryId.message}

+ )} +
+ +
+ + + {errors.amount && ( +

{errors.amount.message}

+ )} +
+ +
+ + + {errors.date && ( +

{errors.date.message}

+ )} +
+ +
+ + + {errors.paymentType && ( +

{errors.paymentType.message}

+ )} +
+ +
+ +