diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 8de4c51..5974e31 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -1,4 +1,5 @@ import { ENV } from "./env"; +import { getPetitions } from "./routes/get-petitions"; import { signPetition } from "./routes/sign-petition"; const CORS_HEADERS = { @@ -29,7 +30,8 @@ const server = Bun.serve({ port: ENV.PORT, routes: { "/health": new Response("alive!"), - "/sign-petition": { + "/sign": { + GET: withCors(getPetitions), POST: withCors(signPetition), OPTIONS: allowCors, }, diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index b971f7b..ea92723 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -1,8 +1,19 @@ import { db } from "./database"; import { signaturesTable } from "./schema"; -export const insertSignature = async (signature: typeof signaturesTable.$inferInsert): Promise => { - const [insertedSignature] = await db.insert(signaturesTable).values(signature).returning(); +export const insertSignature = async ( + signature: typeof signaturesTable.$inferInsert, +): Promise => { + const [insertedSignature] = await db + .insert(signaturesTable) + .values(signature) + .returning(); - return insertedSignature; -} + return insertedSignature; +}; + +export const getSignatures = async (): Promise< + Array +> => { + return db.select().from(signaturesTable); +}; diff --git a/packages/backend/src/routes/get-petitions.ts b/packages/backend/src/routes/get-petitions.ts new file mode 100644 index 0000000..817e524 --- /dev/null +++ b/packages/backend/src/routes/get-petitions.ts @@ -0,0 +1,10 @@ +import { signedPetitionArraySchema } from "types"; +import { getSignatures } from "../models"; + +export const getPetitions = async (_: Request): Promise => { + const signatures = await getSignatures(); + + const parsedSignatures = signedPetitionArraySchema.parse(signatures); + + return Response.json(parsedSignatures, { status: 200 }); +}; diff --git a/packages/frontend/src/components/PetitionForm.tsx b/packages/frontend/src/components/PetitionForm.tsx index 6e9c3c9..ee78a1a 100644 --- a/packages/frontend/src/components/PetitionForm.tsx +++ b/packages/frontend/src/components/PetitionForm.tsx @@ -34,12 +34,22 @@ export const PetitionForm = ({ compact = false }: PetitionFormProps) => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (isSubmitting) { + return; + } + setIsSubmitting(true); + await onSignPetition({ email, comment, name, }); + + setName(undefined); + setEmail(undefined); + setComment(undefined); + setIsSubmitting(false); }; diff --git a/packages/frontend/src/network/index.ts b/packages/frontend/src/network/index.ts index eecb0d1..9e7442b 100644 --- a/packages/frontend/src/network/index.ts +++ b/packages/frontend/src/network/index.ts @@ -3,16 +3,33 @@ import { signedPetitionSchema, signPetitionSchema } from "types"; const backendUrl = import.meta.env.VITE_BACKEND_URL; +export const signedPetitionWithParsedDate = signedPetitionSchema.extend({ + createdAt: z.string().transform((date: string) => new Date(date)), +}); + +const signedPetitionSignatures = z.array(signedPetitionWithParsedDate); + +export const getSignatures = async (): Promise< + z.infer +> => { + const res = await fetch(`${backendUrl}/sign`); + + const body = await res.json(); + const validatedBody = signedPetitionSignatures.parse(body); + + return validatedBody; +}; + export const signPetition = async ( signature: z.infer, -): Promise> => { - const res = await fetch(`${backendUrl}/sign-petition`, { +): Promise> => { + const res = await fetch(`${backendUrl}/sign`, { method: "POST", body: JSON.stringify(signature), }); const body = await res.json(); - const validatedBody = signedPetitionSchema.parse(body); + const validatedBody = signedPetitionWithParsedDate.parse(body); return validatedBody; }; diff --git a/packages/frontend/src/pages/Index.tsx b/packages/frontend/src/pages/Index.tsx index 0f271de..9186088 100644 --- a/packages/frontend/src/pages/Index.tsx +++ b/packages/frontend/src/pages/Index.tsx @@ -11,7 +11,7 @@ import emptyBuildingParking from "@/assets/empty-building-parking.jpg"; import asahiBuilding from "@/assets/asahi-building.jpg"; import doubletreeHilton from "@/assets/doubletree-hilton.jpg"; import { useNavigate } from "react-router-dom"; -import { useState } from "react"; +import { usePetitions } from "@/state"; const STATIC_TESTEMONIES = [ { @@ -36,7 +36,8 @@ const STATIC_TESTEMONIES = [ const Index = () => { const navigate = useNavigate(); - const [signatureCount, setSignatureCount] = useState(0); + + const { signatures } = usePetitions(); const scrollToPetition = () => { document.getElementById("petition")?.scrollIntoView({ behavior: "smooth" }); @@ -67,12 +68,13 @@ const Index = () => { Sign the Petition

- {signatureCount > 0 && ( + {signatures.length > 0 && ( - {signatureCount} people + {signatures.length} people )} - {signatureCount > 0 ? " have" : "Be the first to"} signed so far + {signatures.length > 0 ? " have" : "Be the first to"} signed so + far

@@ -438,7 +440,8 @@ const Index = () => { className="gap-2 bg-primary text-primary-foreground hover:bg-primary/90" > - Read All {signatureCount > 0 && `${signatureCount} `}Testimonies + Read All {signatures.length > 0 && `${signatures.length} `} + Testimonies diff --git a/packages/frontend/src/pages/Testimonies.tsx b/packages/frontend/src/pages/Testimonies.tsx index 404c594..ba99c97 100644 --- a/packages/frontend/src/pages/Testimonies.tsx +++ b/packages/frontend/src/pages/Testimonies.tsx @@ -1,20 +1,16 @@ -import { useState } from "react"; import { Button } from "@/components/ui/button"; import { TestimonialCard } from "@/components/TestimonialCard"; import { useNavigate } from "react-router-dom"; import { formatDistanceToNow } from "date-fns"; import { ArrowLeft, Users } from "lucide-react"; - -interface Signature { - id: string; - name: string; - comment: string | null; - created_at: string; -} +import { usePetitions } from "@/state"; const Testimonies = () => { - const [signatures, setSignatures] = useState([]); - const [totalCount, setTotalCount] = useState(0); + const { signatures } = usePetitions(); + console.log(signatures); + + const totalCount = signatures.length; + const navigate = useNavigate(); return ( @@ -55,7 +51,7 @@ const Testimonies = () => { key={signature.id} name={signature.name} comment={signature.comment || ""} - date={formatDistanceToNow(new Date(signature.created_at), { + date={formatDistanceToNow(new Date(signature.createdAt), { addSuffix: true, })} /> diff --git a/packages/frontend/src/state/index.tsx b/packages/frontend/src/state/index.tsx index bf7ca6b..19c52a3 100644 --- a/packages/frontend/src/state/index.tsx +++ b/packages/frontend/src/state/index.tsx @@ -1,17 +1,22 @@ -import { signPetition } from "@/network"; +import { + getSignatures, + signedPetitionWithParsedDate, + signPetition, +} from "@/network"; import { createContext, ReactNode, useCallback, useContext, + useEffect, useState, } from "react"; import { toast } from "sonner"; -import { signedPetitionSchema, signPetitionSchema } from "types"; +import { signPetitionSchema } from "types"; import { z } from "zod"; // submitted is used to determine if the signature was inserted correctly -type SignatureWithState = z.infer & { +type SignatureWithState = z.infer & { submitted: boolean; }; @@ -36,6 +41,12 @@ export const PetitionStateProvider = ({ [], ); + useEffect(() => { + getSignatures().then((signatures) => { + setSignatures(signatures.map((s) => ({ ...s, submitted: true }))); + }); + }, []); + const onSignPetition = useCallback( async (signature) => { const eagerPetitionId = Date.now().toString(); @@ -43,7 +54,7 @@ export const PetitionStateProvider = ({ setSignatures((petitions) => [ { id: eagerPetitionId, - createdAt: new Date().toISOString(), + createdAt: new Date(), submitted: false, ...signature, }, @@ -68,10 +79,6 @@ export const PetitionStateProvider = ({ return [...petitions]; }); - - toast.error( - "Sorry, had a problem inserting your signature. Please try again.", - ); } catch (err) { console.warn(err); diff --git a/packages/types/index.ts b/packages/types/index.ts index b92238d..48aa789 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -10,3 +10,5 @@ export const signedPetitionSchema = signPetitionSchema.extend({ id: z.uuid(), createdAt: z.date().transform((date: Date) => date.toISOString()), }); + +export const signedPetitionArraySchema = z.array(signedPetitionSchema);