diff --git a/package.json b/package.json index aab88ca..4a9bf37 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@hookform/resolvers": "^3.6.0", "@prisma/client": "^5.15.0", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-slot": "^1.0.2", diff --git a/src/@/components/ui/checkbox.tsx b/src/@/components/ui/checkbox.tsx new file mode 100644 index 0000000..df61a13 --- /dev/null +++ b/src/@/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { Check } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } diff --git a/src/app/lib/get.ts b/src/app/lib/get.ts index 6b0a2f7..578e2ba 100644 --- a/src/app/lib/get.ts +++ b/src/app/lib/get.ts @@ -1,14 +1,12 @@ +"use server" import prisma from "./db" export async function getStories() { - "use server" return prisma.story.findMany() } export async function getPubs() { - "use server" return prisma.pub.findMany() } export async function getGenres() { - "use server" return prisma.genre.findMany() } diff --git a/src/app/submission/create/page.tsx b/src/app/submission/create/page.tsx index ef0b37f..05f2d1c 100644 --- a/src/app/submission/create/page.tsx +++ b/src/app/submission/create/page.tsx @@ -1,5 +1,5 @@ +"use server" import SubmissionForm from "app/ui/forms/sub"; - -export default function Page() { +export default async function Page() { return } diff --git a/src/app/submission/page.tsx b/src/app/submission/page.tsx index 9d06684..665d853 100644 --- a/src/app/submission/page.tsx +++ b/src/app/submission/page.tsx @@ -1,14 +1,7 @@ -import { - Table, - TableBody, - TableCaption, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table" import FancyForm from "app/ui/forms/fancyForm" +import { getGenres } from "app/lib/get" -export default function Page() { - return +export default async function Page() { + const genres = await getGenres() + return } diff --git a/src/app/tailwind.css b/src/app/tailwind.css index c6d7e9e..77f8741 100644 --- a/src/app/tailwind.css +++ b/src/app/tailwind.css @@ -616,25 +616,21 @@ body { z-index: 50; } -.mx-auto { - margin-left: auto; - margin-right: auto; -} - .-mx-1 { margin-left: -0.25rem; margin-right: -0.25rem; } +.mx-auto { + margin-left: auto; + margin-right: auto; +} + .my-1 { margin-top: 0.25rem; margin-bottom: 0.25rem; } -.mt-4 { - margin-top: 1rem; -} - .ml-2 { margin-left: 0.5rem; } @@ -643,6 +639,18 @@ body { margin-left: auto; } +.mt-4 { + margin-top: 1rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + .flex { display: flex; } @@ -651,20 +659,12 @@ body { display: inline-flex; } -.table { - display: table; -} - .\!table { display: table !important; } -.h-12 { - height: 3rem; -} - -.h-24 { - height: 6rem; +.table { + display: table; } .h-10 { @@ -675,18 +675,18 @@ body { height: 2.75rem; } -.h-9 { - height: 2.25rem; -} - -.h-4 { - height: 1rem; +.h-12 { + height: 3rem; } .h-2 { height: 0.5rem; } +.h-24 { + height: 6rem; +} + .h-3 { height: 0.75rem; } @@ -695,22 +695,22 @@ body { height: 0.875rem; } +.h-4 { + height: 1rem; +} + +.h-9 { + height: 2.25rem; +} + .h-px { height: 1px; } -.w-full { - width: 100%; -} - .w-10 { width: 2.5rem; } -.w-4 { - width: 1rem; -} - .w-2 { width: 0.5rem; } @@ -723,6 +723,18 @@ body { width: 0.875rem; } +.w-4 { + width: 1rem; +} + +.w-full { + width: 100%; +} + +.w-\[340px\] { + width: 340px; +} + .min-w-\[8rem\] { min-width: 8rem; } @@ -731,6 +743,10 @@ body { max-width: 24rem; } +.shrink-0 { + flex-shrink: 0; +} + .caption-bottom { caption-side: bottom; } @@ -745,6 +761,14 @@ body { user-select: none; } +.flex-row { + flex-direction: row; +} + +.items-start { + align-items: flex-start; +} + .items-center { align-items: center; } @@ -775,6 +799,18 @@ body { margin-bottom: calc(2rem * var(--tw-space-y-reverse)); } +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); +} + .overflow-auto { overflow: auto; } @@ -811,8 +847,8 @@ body { border-color: hsl(var(--input)); } -.bg-muted\/50 { - background-color: hsl(var(--muted) / 0.5); +.border-primary { + border-color: hsl(var(--primary)); } .bg-background { @@ -823,6 +859,18 @@ body { background-color: hsl(var(--destructive)); } +.bg-muted { + background-color: hsl(var(--muted)); +} + +.bg-muted\/50 { + background-color: hsl(var(--muted) / 0.5); +} + +.bg-popover { + background-color: hsl(var(--popover)); +} + .bg-primary { background-color: hsl(var(--primary)); } @@ -831,34 +879,26 @@ body { background-color: hsl(var(--secondary)); } -.bg-muted { - background-color: hsl(var(--muted)); -} - -.bg-popover { - background-color: hsl(var(--popover)); +.bg-slate-950 { + --tw-bg-opacity: 1; + background-color: rgb(2 6 23 / var(--tw-bg-opacity)); } .fill-current { fill: currentColor; } -.p-4 { - padding: 1rem; -} - .p-1 { padding: 0.25rem; } -.px-4 { - padding-left: 1rem; - padding-right: 1rem; +.p-4 { + padding: 1rem; } -.py-10 { - padding-top: 2.5rem; - padding-bottom: 2.5rem; +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; } .px-3 { @@ -866,26 +906,16 @@ body { padding-right: 0.75rem; } +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + .px-8 { padding-left: 2rem; padding-right: 2rem; } -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.py-4 { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; @@ -896,6 +926,21 @@ body { padding-bottom: 0.375rem; } +.py-10 { + padding-top: 2.5rem; + padding-bottom: 2.5rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + .pl-8 { padding-left: 2rem; } @@ -931,6 +976,11 @@ body { line-height: 1rem; } +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + .font-black { font-weight: 900; } @@ -943,6 +993,10 @@ body { font-weight: 600; } +.font-normal { + font-weight: 400; +} + .capitalize { text-transform: capitalize; } @@ -955,14 +1009,22 @@ body { letter-spacing: 0.1em; } -.text-muted-foreground { - color: hsl(var(--muted-foreground)); +.text-destructive { + color: hsl(var(--destructive)); } .text-destructive-foreground { color: hsl(var(--destructive-foreground)); } +.text-muted-foreground { + color: hsl(var(--muted-foreground)); +} + +.text-popover-foreground { + color: hsl(var(--popover-foreground)); +} + .text-primary { color: hsl(var(--primary)); } @@ -975,12 +1037,13 @@ body { color: hsl(var(--secondary-foreground)); } -.text-popover-foreground { - color: hsl(var(--popover-foreground)); +.text-current { + color: currentColor; } -.text-destructive { - color: hsl(var(--destructive)); +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); } .underline { @@ -1069,10 +1132,6 @@ body { color: hsl(var(--muted-foreground)); } -.hover\:bg-muted\/50:hover { - background-color: hsl(var(--muted) / 0.5); -} - .hover\:bg-accent:hover { background-color: hsl(var(--accent)); } @@ -1081,6 +1140,10 @@ body { background-color: hsl(var(--destructive) / 0.9); } +.hover\:bg-muted\/50:hover { + background-color: hsl(var(--muted) / 0.5); +} + .hover\:bg-primary\/90:hover { background-color: hsl(var(--primary) / 0.9); } @@ -1148,12 +1211,20 @@ body { pointer-events: none; } +.data-\[state\=open\]\:bg-accent[data-state=open] { + background-color: hsl(var(--accent)); +} + .data-\[state\=selected\]\:bg-muted[data-state=selected] { background-color: hsl(var(--muted)); } -.data-\[state\=open\]\:bg-accent[data-state=open] { - background-color: hsl(var(--accent)); +.data-\[state\=checked\]\:bg-primary[data-state=checked] { + background-color: hsl(var(--primary)); +} + +.data-\[state\=checked\]\:text-primary-foreground[data-state=checked] { + color: hsl(var(--primary-foreground)); } .data-\[disabled\]\:opacity-50[data-disabled] { diff --git a/src/app/ui/forms/demo.tsx b/src/app/ui/forms/demo.tsx new file mode 100644 index 0000000..50cdb29 --- /dev/null +++ b/src/app/ui/forms/demo.tsx @@ -0,0 +1,128 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { toast } from "@/components/ui/use-toast" + +const items = [ + { + id: "recents", + label: "Recents", + }, + { + id: "home", + label: "Home", + }, + { + id: "applications", + label: "Applications", + }, + { + id: "desktop", + label: "Desktop", + }, + { + id: "downloads", + label: "Downloads", + }, + { + id: "documents", + label: "Documents", + }, +] as const + +const FormSchema = z.object({ + items: z.array(z.string()).refine((value) => value.some((item) => item), { + message: "You have to select at least one item.", + }), +}) + +export function CheckboxReactHookFormMultiple() { + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + items: ["recents", "home"], + }, + }) + + function onSubmit(data: z.infer) { + toast({ + title: "You submitted the following values:", + description: ( +
+					{JSON.stringify(data, null, 2)}
+				
+ ), + }) + } + + return ( +
+ + ( + +
+ Sidebar + + Select the items you want to display in the sidebar. + +
+ {items.map((item) => ( + { + return ( + + + { + return checked + ? field.onChange([...field.value, item.id]) + : field.onChange( + field.value?.filter( + (value) => value !== item.id + ) + ) + }} + /> + + + {item.label} + + + ) + }} + /> + ))} + +
+ )} + /> + + + + ) +} + diff --git a/src/app/ui/forms/fancyForm.tsx b/src/app/ui/forms/fancyForm.tsx index 3d1f070..19f10f9 100644 --- a/src/app/ui/forms/fancyForm.tsx +++ b/src/app/ui/forms/fancyForm.tsx @@ -15,23 +15,25 @@ import { FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input" +import { Checkbox } from "@/components/ui/checkbox" const formSchema = z.object({ title: z.string().min(2).max(50), word_count: z.number(), - // genres: z.array() + genres: z.object({ id: z.number(), name: z.string() }).array() }) -export default function FancyForm() { + +export default function FancyForm({ genres }) { // 1. Define your form. const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { title: "", - word_count: 500, + word_count: 0, + genres: genres }, }) - // 2. Define a submit handler. function onSubmit(values: z.infer) { // Do something with the form values. @@ -71,9 +73,44 @@ export default function FancyForm() { name="genres" render={({ field }) => ( - +
+ Genres + genres baby +
+ {genres.map((item) => ( + { + return ( + + + { + return checked + ? field.onChange([...field.value, item.id]) + : field.onChange( + field.value?.filter( + (value) => value !== item.id + ) + ) + }} + /> + + + {item.name} + + + ) + }} + /> + ))}
- )} /> diff --git a/tsconfig.json b/tsconfig.json index af33da5..0edc2db 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "noEmit": true, "incremental": true, "module": "esnext", + "target": "es2017", "esModuleInterop": true, "moduleResolution": "node", "resolveJsonModule": true,