Skip to content

Commit 6dbe8c5

Browse files
committed
feat: product details modal
1 parent 64f1fc1 commit 6dbe8c5

10 files changed

+349
-5
lines changed

components/forms/add-register-form.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ const AddRegisterForm = () => {
117117
return (
118118
<Form {...form}>
119119
<form onSubmit={form.handleSubmit(onSubmit)}>
120-
<div className="flex flex-col gap-2 px-4">
120+
<div className="flex flex-col gap-2">
121121
<FormField
122122
control={form.control}
123123
name="name"

components/home/home-register-items.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import useModal from "@/hooks/use-modal-store";
2+
13
import { cn, formatValue } from "@/lib/utils";
24

35
import { Entry, Expense } from "@prisma/client";
@@ -8,6 +10,8 @@ interface HomeRegisterItemsTypeProps {
810
}
911

1012
const HomeRegisterItems = ({ type, registers }: HomeRegisterItemsTypeProps) => {
13+
const { onOpen } = useModal();
14+
1115
return (
1216
<>
1317
{registers.length <= 0 && (
@@ -22,6 +26,7 @@ const HomeRegisterItems = ({ type, registers }: HomeRegisterItemsTypeProps) => {
2226
<button
2327
key={register.id}
2428
className="flex w-full justify-around p-4 border mb-2 rounded-xl"
29+
onClick={() => onOpen("registerDetails", { ...register, type })}
2530
>
2631
<p>{register.name}</p>
2732
<p>{}</p>

components/modals/create-register.tsx

+30-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,21 @@ import AddRegisterForm from "../forms/add-register-form";
1818

1919
import { useEffect } from "react";
2020

21+
import { useMediaQuery } from "@/hooks/use-media-query";
22+
import {
23+
Dialog,
24+
DialogContent,
25+
DialogDescription,
26+
DialogHeader,
27+
DialogTitle,
28+
DialogTrigger,
29+
} from "../ui/dialog";
30+
2131
const CreateRegister = () => {
2232
const { isOpen, onClose, type } = useModal();
2333

34+
const isDesktop = useMediaQuery("(min-width: 768px)");
35+
2436
const isModalOpen = isOpen && type === "createRegister";
2537

2638
useEffect(() => {
@@ -39,14 +51,30 @@ const CreateRegister = () => {
3951
onClose();
4052
};
4153

54+
if (isDesktop) {
55+
return (
56+
<Dialog open={isModalOpen} onOpenChange={handleClose}>
57+
<DialogContent className="sm:max-w-[425px]">
58+
<DialogHeader>
59+
<DialogTitle>Add New Register</DialogTitle>
60+
<DialogDescription>Keep tracking your finances.</DialogDescription>
61+
</DialogHeader>
62+
<AddRegisterForm />
63+
</DialogContent>
64+
</Dialog>
65+
);
66+
}
67+
4268
return (
4369
<Drawer open={isModalOpen} onClose={handleClose}>
4470
<DrawerContent>
4571
<DrawerHeader>
4672
<DrawerTitle>Add New Register</DrawerTitle>
47-
<DrawerDescription>Keep tracking your finances</DrawerDescription>
73+
<DrawerDescription>Keep tracking your finances.</DrawerDescription>
4874
</DrawerHeader>
49-
<AddRegisterForm />
75+
<div className="px-4">
76+
<AddRegisterForm />
77+
</div>
5078
<DrawerFooter>
5179
<DrawerClose onClick={handleClose}>
5280
<Button variant="outline" className="w-full">
+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"use client";
2+
3+
// FIX CREATED AT FORMAT
4+
5+
import useModal from "@/hooks/use-modal-store";
6+
7+
import {
8+
Drawer,
9+
DrawerClose,
10+
DrawerContent,
11+
DrawerDescription,
12+
DrawerFooter,
13+
DrawerHeader,
14+
DrawerTitle,
15+
} from "@/components/ui/drawer";
16+
17+
import { Button } from "../ui/button";
18+
19+
import AddRegisterForm from "../forms/add-register-form";
20+
21+
import { useEffect } from "react";
22+
23+
import { useMediaQuery } from "@/hooks/use-media-query";
24+
import {
25+
Dialog,
26+
DialogContent,
27+
DialogDescription,
28+
DialogFooter,
29+
DialogHeader,
30+
DialogTitle,
31+
} from "../ui/dialog";
32+
import { Separator } from "../ui/separator";
33+
34+
const RegisterDetails = () => {
35+
const { isOpen, onClose, type, data } = useModal();
36+
37+
const isDesktop = useMediaQuery("(min-width: 768px)");
38+
39+
const isModalOpen = isOpen && type === "registerDetails";
40+
41+
useEffect(() => {
42+
const keyboardHandleClose = (e: KeyboardEvent) => {
43+
if (e.key === "Escape" || e.key === "Esc") onClose();
44+
};
45+
46+
window.addEventListener("keydown", keyboardHandleClose);
47+
48+
return () => {
49+
window.removeEventListener("keydown", keyboardHandleClose);
50+
};
51+
}, [onClose]);
52+
53+
const handleClose = () => {
54+
onClose();
55+
};
56+
57+
if (isDesktop) {
58+
return (
59+
<Dialog open={isModalOpen} onOpenChange={handleClose}>
60+
<DialogContent className="sm:max-w-[425px]">
61+
<DialogHeader>
62+
<DialogTitle>Details</DialogTitle>
63+
<DialogDescription>See more details.</DialogDescription>
64+
</DialogHeader>
65+
66+
<Separator />
67+
<div>
68+
<p className="capitalize font-medium text-xl">{data.name}</p>
69+
<p className="mt-2">{data.description}</p>
70+
<div className="mt-5 flex justify-between">
71+
<p className="font-bold">{data.category}</p>
72+
<span className="text-zinc-500">
73+
{data.createdAt?.getFullYear()}
74+
</span>
75+
</div>
76+
</div>
77+
<DialogFooter className="w-full flex">
78+
<Button className="w-1/2" variant={"outline"}>
79+
Close
80+
</Button>
81+
<Button className="w-1/2" variant={"secondary"}>
82+
Go to {data.type}
83+
</Button>
84+
</DialogFooter>
85+
</DialogContent>
86+
</Dialog>
87+
);
88+
}
89+
90+
return (
91+
<Drawer open={isModalOpen} onClose={handleClose}>
92+
<DrawerContent>
93+
<DrawerHeader>
94+
<DrawerTitle>Details</DrawerTitle>
95+
<DrawerDescription>See more details.</DrawerDescription>
96+
</DrawerHeader>
97+
<Separator />
98+
<div className="px-4 pt-4">
99+
<div>
100+
<p className="capitalize font-medium text-xl">{data.name}</p>
101+
<p className="mt-2">{data.description}</p>
102+
<div className="mt-5 flex justify-between">
103+
<p className="font-bold">{data.category}</p>
104+
<span className="text-zinc-500">
105+
{data.createdAt?.getFullYear()}
106+
</span>
107+
</div>
108+
</div>
109+
</div>
110+
<DrawerFooter className="flex">
111+
<Button variant={"outline"}>Close</Button>
112+
<Button variant={"secondary"}>Go to {data.type}</Button>
113+
</DrawerFooter>
114+
</DrawerContent>
115+
</Drawer>
116+
);
117+
};
118+
119+
export default RegisterDetails;

components/providers/modal-provider.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useEffect, useState } from "react";
44

55
import CreateRegister from "../modals/create-register";
6+
import RegisterDetails from "../modals/register-details";
67

78
const ModalProvider = () => {
89
const [isMounted, setIsMounted] = useState(false);
@@ -16,6 +17,7 @@ const ModalProvider = () => {
1617
return (
1718
<>
1819
<CreateRegister />
20+
<RegisterDetails />
1921
</>
2022
);
2123
};

components/ui/dialog.tsx

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import * as DialogPrimitive from "@radix-ui/react-dialog";
5+
import { X } from "lucide-react";
6+
7+
import { cn } from "@/lib/utils";
8+
9+
const Dialog = DialogPrimitive.Root;
10+
11+
const DialogTrigger = DialogPrimitive.Trigger;
12+
13+
const DialogPortal = DialogPrimitive.Portal;
14+
15+
const DialogClose = DialogPrimitive.Close;
16+
17+
const DialogOverlay = React.forwardRef<
18+
React.ElementRef<typeof DialogPrimitive.Overlay>,
19+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
20+
>(({ className, ...props }, ref) => (
21+
<DialogPrimitive.Overlay
22+
ref={ref}
23+
className={cn(
24+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
25+
className
26+
)}
27+
{...props}
28+
/>
29+
));
30+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
31+
32+
const DialogContent = React.forwardRef<
33+
React.ElementRef<typeof DialogPrimitive.Content>,
34+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
35+
>(({ className, children, ...props }, ref) => (
36+
<DialogPortal>
37+
<DialogOverlay />
38+
<DialogPrimitive.Content
39+
ref={ref}
40+
className={cn(
41+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
42+
className
43+
)}
44+
{...props}
45+
>
46+
{children}
47+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
48+
<X className="h-4 w-4" />
49+
<span className="sr-only">Close</span>
50+
</DialogPrimitive.Close>
51+
</DialogPrimitive.Content>
52+
</DialogPortal>
53+
));
54+
DialogContent.displayName = DialogPrimitive.Content.displayName;
55+
56+
const DialogHeader = ({
57+
className,
58+
...props
59+
}: React.HTMLAttributes<HTMLDivElement>) => (
60+
<div
61+
className={cn(
62+
"flex flex-col space-y-1.5 text-center sm:text-left",
63+
className
64+
)}
65+
{...props}
66+
/>
67+
);
68+
DialogHeader.displayName = "DialogHeader";
69+
70+
const DialogFooter = ({
71+
className,
72+
...props
73+
}: React.HTMLAttributes<HTMLDivElement>) => (
74+
<div
75+
className={cn(
76+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
77+
className
78+
)}
79+
{...props}
80+
/>
81+
);
82+
DialogFooter.displayName = "DialogFooter";
83+
84+
const DialogTitle = React.forwardRef<
85+
React.ElementRef<typeof DialogPrimitive.Title>,
86+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
87+
>(({ className, ...props }, ref) => (
88+
<DialogPrimitive.Title
89+
ref={ref}
90+
className={cn(
91+
"text-lg font-semibold leading-none tracking-tight",
92+
className
93+
)}
94+
{...props}
95+
/>
96+
));
97+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
98+
99+
const DialogDescription = React.forwardRef<
100+
React.ElementRef<typeof DialogPrimitive.Description>,
101+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
102+
>(({ className, ...props }, ref) => (
103+
<DialogPrimitive.Description
104+
ref={ref}
105+
className={cn("text-sm text-muted-foreground", className)}
106+
{...props}
107+
/>
108+
));
109+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
110+
111+
export {
112+
Dialog,
113+
DialogPortal,
114+
DialogOverlay,
115+
DialogClose,
116+
DialogTrigger,
117+
DialogContent,
118+
DialogHeader,
119+
DialogFooter,
120+
DialogTitle,
121+
DialogDescription,
122+
};

0 commit comments

Comments
 (0)