diff --git a/src/pages/PlacesOfWorshipPage.tsx b/src/pages/PlacesOfWorshipPage.tsx index dcdc62d..d7601a7 100644 --- a/src/pages/PlacesOfWorshipPage.tsx +++ b/src/pages/PlacesOfWorshipPage.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useMemo, useState } from "react"; import { trpc } from "@/providers/trpc"; import { LocationPicker } from "@/components/LocationPicker"; import { Card, CardContent } from "@/components/ui/card"; @@ -45,13 +45,35 @@ const TYPE_LABELS: Record = { other: "Lainnya", }; +function formatRupiah(amount: number) { + return new Intl.NumberFormat("id-ID", { + style: "currency", + currency: "IDR", + maximumFractionDigits: 0, + }).format(amount); +} + +function parseRupiahInput(input: string) { + const digits = input.replace(/\D/g, ""); + return digits ? Number.parseInt(digits, 10) : 0; +} + export default function PlacesOfWorshipPage() { const [search, setSearch] = useState(""); const [typeFilter, setTypeFilter] = useState("all"); const [isAddOpen, setIsAddOpen] = useState(false); const [isEditOpen, setIsEditOpen] = useState(false); const [isViewOpen, setIsViewOpen] = useState(false); + const [isAidOpen, setIsAidOpen] = useState(false); const [selectedPlace, setSelectedPlace] = useState(null); + const [aidForm, setAidForm] = useState({ + recipientId: 0, + aidType: "Sembako", + amount: 0, + quantity: 1, + unit: "paket", + notes: "", + }); const [formData, setFormData] = useState({ name: "", type: "mosque" as "mosque" | "church" | "temple" | "vihara" | "other", @@ -69,6 +91,14 @@ export default function PlacesOfWorshipPage() { search: search || undefined, type: typeFilter === "all" ? undefined : typeFilter, }); + const { data: placeRecipients } = trpc.recipient.list.useQuery( + { placeOfWorshipId: selectedPlace ?? undefined }, + { enabled: isAidOpen && selectedPlace !== null }, + ); + const { data: placeDistributions } = trpc.distribution.list.useQuery( + { placeOfWorshipId: selectedPlace ?? undefined }, + { enabled: isAidOpen && selectedPlace !== null }, + ); const createMutation = trpc.placeOfWorship.create.useMutation({ onSuccess: () => { @@ -94,6 +124,21 @@ export default function PlacesOfWorshipPage() { }, }); + const createAidMutation = trpc.distribution.create.useMutation({ + onSuccess: () => { + utils.distribution.list.invalidate(); + utils.dashboard.stats.invalidate(); + setAidForm({ + recipientId: 0, + aidType: "Sembako", + amount: 0, + quantity: 1, + unit: "paket", + notes: "", + }); + }, + }); + const resetForm = () => { setFormData({ name: "", @@ -159,11 +204,38 @@ export default function PlacesOfWorshipPage() { }; const selectedPlaceData = places?.find((p) => p.id === selectedPlace); + const selectedPlaceRecipients = useMemo( + () => (placeRecipients ?? []).filter((recipient) => recipient.placeOfWorshipId === selectedPlace), + [placeRecipients, selectedPlace], + ); + const selectedPlaceDistributions = useMemo( + () => (placeDistributions ?? []).filter((distribution) => distribution.placeOfWorshipId === selectedPlace), + [placeDistributions, selectedPlace], + ); + const activeRecipients = selectedPlaceRecipients.filter((recipient) => recipient.status === "active"); + const totalAidAmount = selectedPlaceDistributions.reduce( + (sum, distribution) => sum + Number.parseFloat(String(distribution.amount ?? 0)), + 0, + ); const updateFormData = (next: Partial) => { setFormData((current) => ({ ...current, ...next })); }; + const handleAidSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (!selectedPlace || !aidForm.recipientId) return; + createAidMutation.mutate({ + placeOfWorshipId: selectedPlace, + recipientId: aidForm.recipientId, + aidType: aidForm.aidType, + amount: aidForm.amount, + quantity: aidForm.quantity, + unit: aidForm.unit, + notes: aidForm.notes || undefined, + }); + }; + return (
@@ -309,13 +381,16 @@ export default function PlacesOfWorshipPage() { {place.contactPhone}
)} -
+
+ @@ -327,6 +402,156 @@ export default function PlacesOfWorshipPage() {
)} + {/* Aid Dialog */} + + + + Manajemen Bantuan {selectedPlaceData ? `- ${selectedPlaceData.name}` : ""} + + {selectedPlaceData ? ( +
+
+ + +

Total Penerima

+

{selectedPlaceRecipients.length}

+
+
+ + +

Penerima Aktif

+

{activeRecipients.length}

+
+
+ + +

Total Bantuan Tersalurkan

+

{formatRupiah(totalAidAmount)}

+
+
+
+ +
+
+

Pemberian Bantuan Baru

+

Pilih penerima aktif yang terdaftar di rumah ibadah ini

+
+
+
+ + +
+
+ + setAidForm((current) => ({ ...current, aidType: e.target.value }))} + className="h-9" + /> +
+
+
+
+ + setAidForm((current) => ({ ...current, amount: parseRupiahInput(e.target.value) }))} + className="h-9" + /> +
+
+ + setAidForm((current) => ({ ...current, quantity: parseInt(e.target.value) || 1 }))} + className="h-9" + /> +
+
+ + setAidForm((current) => ({ ...current, unit: e.target.value }))} + className="h-9" + /> +
+
+
+ + setAidForm((current) => ({ ...current, notes: e.target.value }))} + className="h-9" + /> +
+ +
+ +
+

History Pemberian Bantuan

+ {selectedPlaceDistributions.length === 0 ? ( +
+ Belum ada history bantuan di rumah ibadah ini +
+ ) : ( +
+ + + + + + + + + + + + {selectedPlaceDistributions.map((distribution) => { + const recipientName = selectedPlaceRecipients.find((recipient) => recipient.id === distribution.recipientId)?.name ?? "-"; + return ( + + + + + + + + ); + })} + +
TanggalPenerimaJenisJumlahCatatan
+ {new Date(distribution.distributionDate).toLocaleDateString("id-ID", { + day: "numeric", + month: "short", + year: "numeric", + })} + {recipientName}{distribution.aidType}{formatRupiah(Number.parseFloat(String(distribution.amount ?? 0)))}{distribution.notes ?? "-"}
+
+ )} +
+
+ ) : null} +
+
+ {/* Edit Dialog */}