Created
March 17, 2023 03:50
-
-
Save cayblood/ce6c536aa8d1180c8d545e2a3d4fb3eb to your computer and use it in GitHub Desktop.
remix-forms problem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
import { type ActionFunction, json, type LoaderFunction } from "@remix-run/node"; | |
import { requireUser } from "~/session.server"; | |
import { getUserStatus, syncStripeCustomer } from "~/models/user.server"; | |
import { useLoaderData } from "@remix-run/react"; | |
import Gravatar from "~/components/Gravatar"; | |
import { makeDomainFunction } from "domain-functions"; | |
import { register } from "~/join.server"; | |
import Form from "~/ui/form"; | |
import { z } from "zod"; | |
export const loader: LoaderFunction = async ({ request }) => { | |
let user = await requireUser(request, '/profile'); | |
user = await syncStripeCustomer(user); | |
const status = await getUserStatus(user); | |
return json({ | |
user, | |
status | |
}); | |
} | |
const schema = z.object({ | |
level: z.enum(['voting', 'reduced', 'basic', 'custom']).default('voting'), | |
frequency: z.enum(['annual', 'monthly']).default('annual'), | |
amount: z.number().default(200) | |
}); | |
const mutation = makeDomainFunction(schema)(async (values) => { | |
console.log("values", values); | |
return values; | |
}); | |
export const action: ActionFunction = async ({ request }) => | |
register({ | |
request, | |
schema, | |
mutation | |
}); | |
export function Sidebar() { | |
return ( | |
<div className="text-gray-500"> | |
<p className="text text-base font-inter"> | |
<strong>Your contact information will be kept confidential.</strong> | |
</p> | |
<p className="text text-base font-inter"> | |
Basic membership is free. Members will receive email notifications when | |
new content is posted. You may cancel these notifications at any time. | |
Members who make an annual donation of $100 or more qualify for voting | |
membership and free admission to Association conferences. This | |
requirement is reduced to $40 for subscribers who are students, retired, | |
experiencing financial hardship, or who reside in a developing nation. | |
We also accept cryptocurrency or in-kind donations in lieu of cash. | |
Please <a href="mailto:[email protected]">contact us</a> if | |
you would like to donate this way. | |
</p> | |
</div> | |
); | |
} | |
function MembershipLevelForm() { | |
const { status } = useLoaderData(); | |
return ( | |
<div className="flex flex-col"> | |
<div className="w-full flex flex-col mt-12 p-4 sm:p-8 bg-gray-100 rounded-lg"> | |
<div className="text text-lg font-bold font-inter mb-8 text-center text-gray-500 w-full"> | |
Support the Mormon Transhumanist Association | |
</div> | |
<div className="flex flex-col md:flex-row"> | |
<div className="md:hidden mb-6"> | |
<Sidebar /> | |
</div> | |
<div className="w-full md:w-1/2 self-start"> | |
<Form schema={schema} values={{level: status}}> | |
{({ Field, | |
Button, | |
watch }) => { | |
const level = watch('level'); | |
const frequency = watch('frequency'); | |
const amount = watch('amount'); | |
return ( | |
<> | |
<Field name="level" label="Membership level" radio={true}> | |
{({ Label, RadioGroup, RadioWrapper, Radio }) => ( | |
<> | |
<Label /> | |
<RadioGroup> | |
<RadioWrapper> | |
<Radio value="voting" /> | |
<Label>Voting ($100)</Label> | |
</RadioWrapper> | |
<RadioWrapper> | |
<Radio value="reduced" /> | |
<Label>Voting (reduced) ($40)</Label> | |
</RadioWrapper> | |
<RadioWrapper> | |
<Radio value="basic" /> | |
<Label>Basic</Label> | |
</RadioWrapper> | |
<RadioWrapper> | |
<Radio value="custom" /> | |
<Label>Custom</Label> | |
</RadioWrapper> | |
</RadioGroup> | |
</> | |
)} | |
</Field> | |
{level.length > 0 && level === 'custom' && <Field name="frequency" label="Donation frequency" radio={true} />} | |
{level === 'custom' && | |
<div className="flex flex-col"> | |
<div className="flex flex-row"> | |
<Field name="amount" label="Custom donation amount" /> | |
<div className="ml-4 mb-2 self-end"> | |
per {frequency === 'annual' ? 'year' : 'month'} | |
</div> | |
</div> | |
<div className="mt-4 text text-sm font-inter text-gray-500"> | |
{typeof amount === 'number' && amount >= 100 && 'Instantly qualifies for voting membership.'} | |
{typeof amount === 'number' && amount >= 8.33 && amount < 100 && frequency === 'monthly' && `Qualifies for voting membership in ${Math.ceil(100 / amount)} months`} | |
{typeof amount === 'number' && amount < 100 && frequency === 'annual' && "Doesn't qualify for voting membership"} | |
{typeof amount === 'number' && amount < 8.33 && frequency === 'monthly' && "Doesn't qualify for voting membership"} | |
</div> | |
</div> | |
} | |
<Button disabled={level === status}>Update membership status</Button> | |
</> | |
) | |
}} | |
</Form> | |
</div> | |
<div className="hidden md:flex md:ml-12 md:w-1/2"> | |
<Sidebar /> | |
</div> | |
</div> | |
</div> | |
</div> | |
); | |
} | |
export default function Profile() { | |
const { user, status } = useLoaderData<typeof loader>(); | |
const longStatus = (() => { | |
switch (status) { | |
case 'voting': return 'Voting member ($100 / year)'; | |
case 'reduced': return 'Voting member (reduced) ($40 / year)'; | |
case 'basic': return 'Basic member'; | |
case 'custom': return 'Custom member'; | |
} | |
})(); | |
const paymentLink = new URL("https://buy.stripe.com/dR6cNZ1up5t45Hy8wz"); | |
paymentLink.searchParams.set("prefilled_email", user.email); | |
return ( | |
<> | |
<h1>Profile</h1> | |
<p className="text-base"> | |
Thanks for joining the Mormon Transhumanist Association. We invite you to stay up-to-date with the | |
latest posts and content from the Association, and support our mission | |
to spread awareness of these ideas. Your support makes it possible to | |
hold events and create more of the quality content you see here. | |
</p> | |
<div className="mt-8 flex flex-row"> | |
<div className="mr-8"> | |
<a href="https://en.gravatar.com/emails/"> | |
<Gravatar email={user.email} size={150} allowEdit={true} /> | |
</a> | |
</div> | |
<div> | |
<table> | |
<tbody> | |
<tr> | |
<td className="text-right pr-4">Email:</td> | |
<td>{user.email}</td> | |
</tr> | |
<tr> | |
<td className="text-right pr-4">Membership status:</td> | |
<td>{longStatus}</td> | |
</tr> | |
<tr> | |
<td className="text-right pr-4">Full name:</td> | |
<td>{user.fullName}</td> | |
</tr> | |
<tr> | |
<td className="text-right pr-4">Preferred name:</td> | |
<td>{user.preferredName}</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<MembershipLevelForm /> | |
</> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Lemme suggest you to change the loader type signature to benefit from the Remix type inference: