-
Notifications
You must be signed in to change notification settings - Fork 5
[WIP] Add AGM view for users #1671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v4
Are you sure you want to change the base?
Changes from all commits
7dab2ce
1cc4987
d6af9a7
82a8b11
34e5bd0
7b69db8
a236250
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,115 +1,77 @@ | ||
| "use client" | ||
|
|
||
| import * as React from "react" | ||
| import { z } from "zod" | ||
| import { useForm } from "@tanstack/react-form" | ||
| import { useParams } from "next/navigation" | ||
| import { AnimatePresence, motion } from "motion/react" | ||
|
|
||
| import { cn } from "~/lib/utils" | ||
| import { api } from "~/trpc/react" | ||
| import { toast } from "~/hooks/use-toast" | ||
| import { cn } from "~/lib/utils" | ||
| import { Button } from "~/ui/button" | ||
| import { | ||
| Field, | ||
| FieldContent, | ||
| FieldDescription, | ||
| FieldError, | ||
| FieldGroup, | ||
| FieldLabel, | ||
| FieldLegend, | ||
| FieldSet, | ||
| } from "~/ui/field" | ||
| import { FieldDescription, FieldGroup, FieldLegend, FieldSet } from "~/ui/field" | ||
| import { Spinner } from "~/ui/spinner" | ||
| import { Textarea } from "~/ui/textarea" | ||
|
|
||
| interface AgendaCardProps { | ||
| className?: string | ||
| } | ||
|
|
||
| const formSchema = z.object({ | ||
| agenda: z.string(), | ||
| }) | ||
|
|
||
| export default function AgendaCard({ className, ...props }: AgendaCardProps) { | ||
| function AgendaForm({ meetingId, initialAgenda }: { meetingId: string; initialAgenda: string | null }) { | ||
| const utils = api.useUtils() | ||
| const btnRef = React.useRef<HTMLButtonElement>(null) | ||
| const [btnText, setText] = React.useState("Save") | ||
| // const { mutateAsync } = api.user.checkIfExists.useMutation() | ||
| const [btnText, setBtnText] = React.useState("Save agenda") | ||
| const [loading, startTransition] = React.useTransition() | ||
| const form = useForm({ | ||
| defaultValues: { | ||
| agenda: "", | ||
| }, | ||
| validators: { | ||
| onSubmit: formSchema, | ||
| }, | ||
| onSubmit({ value }) { | ||
| // setText("Checking if email exists") | ||
| startTransition(async () => { | ||
| // const userExists = await mutateAsync(value.email) | ||
| // if (userExists) { | ||
| // setText("Sending code to email") | ||
| // } else { | ||
| // setText("Redirecting to sign up") | ||
| // } | ||
| }) | ||
| }, | ||
| }) | ||
| const [agenda, setAgenda] = React.useState(initialAgenda ?? "") | ||
|
|
||
| const updateMeeting = api.admin.generalMeetings.update.useMutation() | ||
|
|
||
| function handleSave() { | ||
| setBtnText("Saving agenda") | ||
| startTransition(async () => { | ||
| try { | ||
| await updateMeeting.mutateAsync({ id: meetingId, agenda: agenda || null }) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should use |
||
| await utils.generalMeetings.get.invalidate() | ||
| toast({ title: "Agenda saved" }) | ||
| setBtnText("Save agenda") | ||
| } catch { | ||
| toast({ title: "Failed to save agenda", variant: "destructive" }) | ||
| setBtnText("Save agenda") | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| return ( | ||
| <form | ||
| className={cn("flex w-full flex-col gap-y-4 bg-white p-6 dark:bg-neutral-950", className)} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. y remove this? it fixes accessibility focus with tanstack-form
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean the next line. misclick hehe |
||
| onSubmit={(e) => { | ||
| e.preventDefault() | ||
| btnRef.current?.focus() | ||
| void form.handleSubmit() | ||
| }} | ||
| > | ||
| <div className={cn("flex w-full flex-col gap-y-4 bg-white p-6 dark:bg-neutral-950")}> | ||
| <FieldSet className="h-full"> | ||
| <FieldLegend variant="label">Meeting agenda</FieldLegend> | ||
| <FieldDescription>This will be viewable to everyone attending the meeting</FieldDescription> | ||
| <FieldDescription>This will be viewable to everyone attending the meeting. Supports markdown.</FieldDescription> | ||
| <FieldGroup className="h-full"> | ||
| <form.Field name="agenda"> | ||
| {(field) => { | ||
| const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid | ||
| return ( | ||
| <Field data-invalid={isInvalid} className="h-full"> | ||
| {/* <FieldLabel htmlFor={field.name}>Email address</FieldLabel> */} | ||
| <Textarea | ||
| id={field.name} | ||
| name={field.name} | ||
| placeholder="Write something here with markdown" | ||
| value={field.state.value} | ||
| onBlur={field.handleBlur} | ||
| aria-invalid={isInvalid} | ||
| disabled={loading} | ||
| className="h-full" | ||
| onChange={(e) => field.handleChange(e.target.value)} | ||
| /> | ||
| {isInvalid && <FieldError errors={field.state.meta.errors} />} | ||
| </Field> | ||
| ) | ||
| }} | ||
| </form.Field> | ||
| <Textarea | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why change this from uncontrolled to controlled. Will cause unnecessary re-renders |
||
| placeholder="Write something here with markdown" | ||
| value={agenda} | ||
| disabled={loading} | ||
| className="h-full min-h-48" | ||
| onChange={(e) => setAgenda(e.target.value)} | ||
| /> | ||
| </FieldGroup> | ||
| </FieldSet> | ||
|
|
||
| <form.Subscribe selector={(state) => [state.canSubmit]}> | ||
| {([canSubmit]) => ( | ||
| <Button ref={btnRef} type="submit" disabled={loading ?? !canSubmit} className="relative"> | ||
| <AnimatePresence mode="popLayout" initial={false}> | ||
| <motion.span | ||
| key={btnText} | ||
| transition={{ type: "spring", duration: 0.2, bounce: 0 }} | ||
| initial={{ opacity: 0, y: -36 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| exit={{ opacity: 0, y: 36 }} | ||
| > | ||
| {btnText} | ||
| </motion.span> | ||
| </AnimatePresence> | ||
| {loading && <Spinner className="absolute right-4" />} | ||
| </Button> | ||
| )} | ||
| </form.Subscribe> | ||
| </form> | ||
| <Button ref={btnRef} disabled={loading} className="relative" onClick={handleSave}> | ||
| <AnimatePresence mode="popLayout" initial={false}> | ||
| <motion.span | ||
| key={btnText} | ||
| transition={{ type: "spring", duration: 0.2, bounce: 0 }} | ||
| initial={{ opacity: 0, y: -36 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| exit={{ opacity: 0, y: 36 }} | ||
| > | ||
| {btnText} | ||
| </motion.span> | ||
| </AnimatePresence> | ||
| {loading && <Spinner className="absolute right-4" />} | ||
| </Button> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| export default function AgendaCard() { | ||
| const { slug } = useParams<{ slug: string }>() | ||
| const [meeting] = api.generalMeetings.get.useSuspenseQuery({ slug }) | ||
|
|
||
| return <AgendaForm meetingId={meeting.id} initialAgenda={meeting.agenda} /> | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shout out to my homies