From 572d2f429a5061a0e2142cdefa96a7c6360216f1 Mon Sep 17 00:00:00 2001 From: kodemon Date: Mon, 24 Nov 2025 09:11:16 +0100 Subject: [PATCH] feat: add initial login view --- apps/react/package.json | 1 + apps/react/src/components/ui/field.tsx | 242 ++++++++++++++++++ apps/react/src/components/ui/label.tsx | 22 ++ apps/react/src/main.tsx | 6 +- apps/react/src/{routes.tsx => router.tsx} | 13 +- apps/react/src/views/app.controller.ts | 3 +- .../src/views/auth/components/login-form.tsx | 72 ++++++ apps/react/src/views/auth/login.controller.ts | 44 ++-- apps/react/src/views/auth/login.view.tsx | 12 +- deno.lock | 16 ++ 10 files changed, 394 insertions(+), 37 deletions(-) create mode 100644 apps/react/src/components/ui/field.tsx create mode 100644 apps/react/src/components/ui/label.tsx rename apps/react/src/{routes.tsx => router.tsx} (61%) create mode 100644 apps/react/src/views/auth/components/login-form.tsx diff --git a/apps/react/package.json b/apps/react/package.json index 396ec5d..5bb85ed 100644 --- a/apps/react/package.json +++ b/apps/react/package.json @@ -16,6 +16,7 @@ "@radix-ui/react-avatar": "^1.1.11", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", diff --git a/apps/react/src/components/ui/field.tsx b/apps/react/src/components/ui/field.tsx new file mode 100644 index 0000000..4e57275 --- /dev/null +++ b/apps/react/src/components/ui/field.tsx @@ -0,0 +1,242 @@ +import { useMemo } from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/libraries/utils" +import { Label } from "@/components/ui/label" +import { Separator } from "@/components/ui/separator" + +function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) { + return ( +
[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", + className + )} + {...props} + /> + ) +} + +function FieldLegend({ + className, + variant = "legend", + ...props +}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) { + return ( + + ) +} + +function FieldGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
[data-slot=field-group]]:gap-4", + className + )} + {...props} + /> + ) +} + +const fieldVariants = cva( + "group/field data-[invalid=true]:text-destructive flex w-full gap-3", + { + variants: { + orientation: { + vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"], + horizontal: [ + "flex-row items-center", + "[&>[data-slot=field-label]]:flex-auto", + "has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px has-[>[data-slot=field-content]]:items-start", + ], + responsive: [ + "@md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto flex-col [&>*]:w-full [&>.sr-only]:w-auto", + "@md/field-group:[&>[data-slot=field-label]]:flex-auto", + "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + }, + }, + defaultVariants: { + orientation: "vertical", + }, + } +) + +function Field({ + className, + orientation = "vertical", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function FieldContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function FieldLabel({ + className, + ...props +}: React.ComponentProps) { + return ( +