diff --git a/app/(main)/manage/[storeId]/add/components/DonationForm.tsx b/app/(main)/manage/[storeId]/add/components/DonationForm.tsx new file mode 100644 index 0000000..5a79cb2 --- /dev/null +++ b/app/(main)/manage/[storeId]/add/components/DonationForm.tsx @@ -0,0 +1,339 @@ +"use client"; + +import { DonationInsert } from "@/app/types/donation"; +import { useForm, Controller } from "react-hook-form"; +import { PatternFormat } from "react-number-format"; +import { createDonation } from "@/app/actions/donation"; + +type FormData = { + donor_type?: "individual" | "business"; + + individual_name?: string; + business_name?: string; + business_contact_name?: string; + + email: string; + phone: string; + address: string; + receiving_site: string; + + receive_emails: boolean; + receive_mailings: boolean; + remain_anonymous: boolean; + + estimated_value: number; + items_donated: string; +}; + +export default function DonationForm() { + const { + register, + handleSubmit, + watch, + control, + reset, + clearErrors, + formState: { errors, isSubmitSuccessful } + } = useForm({ + defaultValues: { + donor_type: undefined, + phone: "", + estimated_value: undefined, + } + }); + + const donorType = watch("donor_type"); + + const onSubmit = async (data: FormData) => { + const donation: DonationInsert = { + donor_is_individual: data.donor_type === "individual", + + donor_individual_name: + data.donor_type === "individual" + ? data.individual_name ?? null + : null, + + donor_business_name: + data.donor_type === "business" + ? data.business_name ?? null + : null, + + donor_business_contact_name: + data.donor_type === "business" + ? data.business_contact_name ?? null + : null, + + donor_email: data.email ?? null, + donor_phone: data.phone ?? null, + donor_street_address: data.address ?? null, + + donor_receive_emails: data.receive_emails, + donor_receive_mailings: data.receive_mailings, + donor_remain_anonymous: data.remain_anonymous, + + estimated_value: data.estimated_value, + items_donated: data.items_donated, + + receiver_first_name: "FIRST-NAME", + receiver_last_name: "LAST-NAME", + }; + try { + const result = await createDonation(donation); + console.log("Donation created:", result); + + // Reset all fields to empty/default values + reset({ + donor_type: undefined, + individual_name: "", + business_name: "", + business_contact_name: "", + email: "", + phone: "", + address: "", + receiving_site: "", + receive_emails: false, + receive_mailings: false, + remain_anonymous: false, + estimated_value: undefined, + items_donated: "", + }); + clearErrors(); + + } catch (error) { + console.error("Failed to create donation:", error); + } + }; + + return ( +
+
+

Donation Form

+
+
+ + {/* Receiving Site */} +
+ + +
+ + {/* Donor Type */} + ( + <> + + + {errors.donor_type &&

{errors.donor_type.message}

} + + )} + /> + + {/* Conditional Name Fields */} + {donorType === "individual" && ( +
+ + +
+ )} + + {donorType === "business" && ( +
+
+ + +
+
+ + +
+
+ )} + + {/* Email */} +
+ + + {errors.email &&

{errors.email.message}

} +
+ + {/* Phone */} +
+ + { + const digits = value?.replace(/\D/g, ""); + return ( + !digits || digits.length === 10 || "Phone number must be 10 digits" + ); + }, + }} + render={({ field }) => ( + { + // store digits only: "4155551234" + field.onChange(values.value); + }} + style={{ + padding: "8px", + borderRadius: "4px", + border: "1px solid #ccc", + }} + /> + )} + /> + {errors.phone &&

{errors.phone.message}

} +
+ + {/* Street Address */} +
+ + + {errors.address &&

{errors.address.message}

} +
+ + {/* Checkboxes */} +
+ + + +
+ + {/* Estimated Value */} +
+ + + + + {errors.estimated_value && ( +

+ {errors.estimated_value.message} +

+ )} +
+ + {/* Items Donated */} +
+ + + {errors.items_donated &&

{errors.items_donated.message}

} +
+ + + + {isSubmitSuccessful && ( +
+ Form submitted successfully! Thank you for your donation. +
+ )} + +
+ ); +} diff --git a/app/(main)/manage/[storeId]/add/page.tsx b/app/(main)/manage/[storeId]/add/page.tsx new file mode 100644 index 0000000..ea7b119 --- /dev/null +++ b/app/(main)/manage/[storeId]/add/page.tsx @@ -0,0 +1,9 @@ +import DonationForm from "./components/DonationForm"; + +export default function AddStoreItemsPage() { + return ( +
+ +
+ ); +} diff --git a/app/api/donations/actions.ts b/app/api/donations/actions.ts new file mode 100644 index 0000000..eafda36 --- /dev/null +++ b/app/api/donations/actions.ts @@ -0,0 +1,39 @@ +"use server"; + +import { DonationInsert } from '@/app/types/donation'; +import { createClient } from '@/app/lib/supabase/server-client'; + +export async function createDonation(data: DonationInsert) { + const supabase = await createClient(); + + const { data: inserted, error } = await supabase + .from("donations") + .insert(data) + .select() // after inserting, return full new row + .single(); // expect just one row, if more or less, throw an error + + if (error) { + console.error("Error creating donation: ", error); + throw new Error(error.message); + } + + return inserted; // return the row that was created +} + +// deletes donation based on donation_id +export async function deleteDonation(donation_id: string) { + const supabase = await createClient(); + + const { error } = await supabase + .from("donations") + .delete() + .eq("donation_id", donation_id); + + if (error) { + console.error("Error deleting donation:", error); + throw new Error(error.message); + } + + return { success: true }; +} + diff --git a/app/types/donation.ts b/app/types/donation.ts index d10733c..33ecc5b 100644 --- a/app/types/donation.ts +++ b/app/types/donation.ts @@ -25,4 +25,4 @@ export type Donation = { store_street_address?: string; }; -export type DonationInsert = Omit; +export type DonationInsert = Omit; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3b8a0d0..2ccd30b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "react": "19.2.0", "react-dom": "19.2.0", "react-hook-form": "^7.70.0", + "react-number-format": "^5.4.4", "zustand": "^5.0.9" }, "devDependencies": { @@ -5924,22 +5925,6 @@ "react": "^19.2.0" } }, - "node_modules/react-hook-form": { - "version": "7.70.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.70.0.tgz", - "integrity": "sha512-COOMajS4FI3Wuwrs3GPpi/Jeef/5W1DRR84Yl5/ShlT3dKVFUfoGiEZ/QE6Uw8P4T2/CLJdcTVYKvWBMQTEpvw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-hook-form" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18 || ^19" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5947,6 +5932,16 @@ "dev": true, "license": "MIT" }, + "node_modules/react-number-format": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.4.tgz", + "integrity": "sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==", + "license": "MIT", + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/read-cmd-shim": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-6.0.0.tgz", @@ -7010,6 +7005,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-places-autocomplete": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/use-places-autocomplete/-/use-places-autocomplete-4.0.1.tgz", + "integrity": "sha512-AybOR/qzXcdaMCGSFveycfL3kztwseAOdagbYoJD8c3amll+gEiPmUkSNhYNUEBqbR+JmJG6/oBTRgihNbE+1A==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", diff --git a/package.json b/package.json index c8450cd..b78c74d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "react": "19.2.0", "react-dom": "19.2.0", "react-hook-form": "^7.70.0", - "zustand": "^5.0.9" + "zustand": "^5.0.9", + "react-number-format": "^5.4.4" }, "devDependencies": { "@tailwindcss/postcss": "^4",