Skip to content

Commit

Permalink
feat(nextjs): loading state and form redo for create event
Browse files Browse the repository at this point in the history
  • Loading branch information
not-ani committed Aug 1, 2024
1 parent 50ce148 commit 538b1d0
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 26 deletions.
64 changes: 38 additions & 26 deletions apps/nextjs/src/app/(home)/events/_components/CreateEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useForm } from "react-hook-form";
import { createEventSchema } from "@amaxa/db/schema";
import { cn } from "@amaxa/ui";
import { Button } from "@amaxa/ui/button";
import { LoadingButton } from "@amaxa/ui/loading-button";
import { Calendar } from "@amaxa/ui/calendar";
import {
Dialog,
Expand Down Expand Up @@ -44,11 +45,14 @@ type CreateEventProps = z.infer<typeof createEventSchema>;

export const CreateEvent = () => {
const router = useRouter();
const [open, setOpen] = React.useState(false);

const { mutate: create } = api.events.create.useMutation({
const { mutate: create, isPending } = api.events.create.useMutation({
onSuccess: () => {
toast.success("Event created");
router.refresh();
form.reset();
toggleDialogState();
},
onError: (error) => {
showErrorToast(error);
Expand All @@ -63,11 +67,15 @@ export const CreateEvent = () => {
create(data);
}

function toggleDialogState() {
setOpen((prev) => !prev);
}

return (
<div>
<Dialog>
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>Create an Event</Button>
<Button >Create an Event</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
Expand Down Expand Up @@ -155,18 +163,17 @@ export const CreateEvent = () => {
</FormItem>
)}
/>

<FormField
name="desc"
name="image"
control={form.control}
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormLabel>Event Image URL</FormLabel>
<FormControl>
<Textarea {...field} />
<Input {...field} />
</FormControl>
<FormDescription>
A description of the event
</FormDescription>
<FormDescription>URL for the event image</FormDescription>
<FormMessage />
</FormItem>
)}
Expand Down Expand Up @@ -194,25 +201,11 @@ export const CreateEvent = () => {
</FormItem>
)}
/>

<FormField
name="image"
control={form.control}
render={({ field }) => (
<FormItem>
<FormLabel>Event Image URL</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormDescription>URL for the event image</FormDescription>
<FormMessage />
</FormItem>
)}
/>

<div className="h-4" />
<FormField
name="isVirtual"
control={form.control}

render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
Expand All @@ -233,8 +226,27 @@ export const CreateEvent = () => {
)}
/>

<FormField
name="desc"
control={form.control}
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>
<FormDescription>
A description of the event
</FormDescription>
<FormMessage />
</FormItem>
)}
/>



<DialogFooter className="pt-10">
<Button type="submit">Save changes</Button>
<LoadingButton disabled={isPending} loading={isPending} type="submit">Save changes</LoadingButton>
</DialogFooter>
</form>
</Form>
Expand Down
2 changes: 2 additions & 0 deletions apps/nextjs/src/app/(home)/events/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ export default async function Home(props: {
</div>
);
}

export const dynamic = "force-dynamic"
81 changes: 81 additions & 0 deletions packages/ui/src/loading-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { Loader2 } from 'lucide-react';
import { cn } from '.';

const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
);

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
loading?: boolean;
}

const LoadingButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, loading, children, ...props }, ref) => {
if (asChild) {
return (
<Slot ref={ref} {...props}>
<>
{React.Children.map(children as React.ReactElement, (child: React.ReactElement) => {
return React.cloneElement(child, {
className: cn(buttonVariants({ variant, size }), className),
children: (
<>
{loading && (
<Loader2 className={cn('h-4 w-4 animate-spin', 'mr-2')} />
)}
{child.props.children}
</>
),
});
})}
</>
</Slot>
);
}

return (
<button
className={cn(buttonVariants({ variant, size, className }))}
disabled={loading}
ref={ref}
{...props}
>
<>
{loading && <Loader2 className={cn('h-4 w-4 animate-spin', 'mr-2')} />}
{children}
</>
</button>
);
},
);
LoadingButton.displayName = 'LoadingButton';

export { LoadingButton, buttonVariants };

0 comments on commit 538b1d0

Please sign in to comment.