wip client to id

This commit is contained in:
2025-08-11 21:25:48 -03:00
parent 70e87e9526
commit 6e0e5f788d
13 changed files with 396 additions and 56 deletions

View File

@@ -57,70 +57,71 @@ body {
:root {
--radius: 0.65rem;
--background: oklch(1 0 0);
--foreground: oklch(0.141 0.005 285.823);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.141 0.005 285.823);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.141 0.005 285.823);
--primary: oklch(0.723 0.219 149.579);
--primary-foreground: oklch(0.982 0.018 155.826);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--muted: oklch(0.967 0.001 286.375);
--muted-foreground: oklch(0.552 0.016 285.938);
--accent: oklch(0.967 0.001 286.375);
--accent-foreground: oklch(0.21 0.006 285.885);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.92 0.004 286.32);
--input: oklch(0.92 0.004 286.32);
--ring: oklch(0.723 0.219 149.579);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.723 0.219 149.579);
--sidebar-primary-foreground: oklch(0.982 0.018 155.826);
--sidebar-accent: oklch(0.967 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: oklch(0.723 0.219 149.579);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.141 0.005 285.823);
--background: oklch(0 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.21 0.006 285.885);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.21 0.006 285.885);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.696 0.17 162.48);
--primary-foreground: oklch(0.393 0.095 152.535);
--secondary: oklch(0.274 0.006 286.033);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.274 0.006 286.033);
--muted-foreground: oklch(0.705 0.015 286.067);
--accent: oklch(0.274 0.006 286.033);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.527 0.154 150.069);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.006 285.885);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.696 0.17 162.48);
--sidebar-primary-foreground: oklch(0.393 0.095 152.535);
--sidebar-accent: oklch(0.274 0.006 286.033);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.527 0.154 150.069);
--sidebar-ring: oklch(0.556 0 0);
}
@layer base {

View File

@@ -0,0 +1,59 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
export { Button, buttonVariants }

View File

@@ -0,0 +1,21 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
<input
type={type}
data-slot="input"
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className
)}
{...props}
/>
)
}
export { Input }

View File

@@ -0,0 +1,22 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cn } from "@/lib/utils"
function Label({
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
return (
<LabelPrimitive.Root
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
)}
{...props}
/>
)
}
export { Label }

View File

@@ -16,6 +16,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<Meta />
<Links />
</head>
@@ -34,14 +35,14 @@ export default function App() {
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = 'Oops!'
let details = 'An unexpected error occurred.'
let details = 'Ocorreu um erro inesperado.'
let stack: string | undefined
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? '404' : 'Error'
message = error.status === 404 ? '404' : 'Erro'
details =
error.status === 404
? 'The requested page could not be found.'
? 'A página solicitada não foi encontrada.'
: error.statusText || details
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message

View File

@@ -1,5 +1,26 @@
import type { Route } from './+types/home'
import { isValidCPF } from '@brazilian-utils/brazilian-utils'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import logo from './logo.svg'
const cpf = z.string().refine(isValidCPF, { message: 'CPF inválido' })
const email = z.string().email({ message: 'Email inválido' })
const schema = z.object({
username: z.union([cpf, email]),
password: z.string()
})
type Schema = z.infer<typeof schema>
export function meta({}: Route.MetaArgs) {
return [
{ title: 'EDUSEG®' },
@@ -12,9 +33,69 @@ export function loader({ context }: Route.LoaderArgs) {
}
export default function Home({ loaderData }: Route.ComponentProps) {
const { register, handleSubmit } = useForm({
resolver: zodResolver(schema)
})
const onSubmit = (data: Schema) => {
console.log(data)
}
return (
<>
<div>...</div>
<div className="w-full max-w-xs grid gap-6">
<div className="flex justify-center">
<div className="border border-white/15 bg-white/5 px-2.5 py-3 rounded-xl">
<img src={logo} alt="React Router" className="block size-12" />
</div>
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="grid gap-6">
<div className="text-center space-y-1.5">
<h1 className="text-2xl font-semibold font-display text-balance">
Faça login
</h1>
<p className="text-white/50 text-sm">
Não tem uma conta?{' '}
<a href="#" className="font-medium text-white">
Cadastre-se
</a>
.
</p>
</div>
<div className="grid gap-3">
<Label htmlFor="email">Email ou CPF</Label>
<Input id="email" type="text" required />
</div>
<div className="grid gap-3">
<div className="flex items-center">
<Label htmlFor="password">Senha</Label>
<a
href="#"
className="ml-auto text-sm underline-offset-4 hover:underline"
>
Esqueceu sua senha?
</a>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" className="w-full bg-lime-400 cursor-pointer">
Entrar
</Button>
</div>
</form>
<p className="text-white/50 text-xs text-center">
Ao fazer login, você concorda com nossa{' '}
<a href="#" className="underline hover:no-underline">
política de privacidade
</a>
.
</p>
</div>
</>
)
}

View File

@@ -1,9 +1,20 @@
import { ChevronLeftIcon } from 'lucide-react'
import { Outlet } from 'react-router'
export default function Layout() {
return (
<div className="flex min-h-svh flex-col items-center justify-center">
<div className="w-full max-w-sm relative pt-12">
<div className="flex min-h-svh w-full items-center justify-center p-6 md:p-10 relative">
<a
href="//eduseg.com.br"
className="flex items-center gap-0.5 absolute top-5 left-5 text-sm z-1"
>
<ChevronLeftIcon className="size-5" /> Página incial
</a>
<div className="w-full max-w-sm relative z-1">
<Outlet />
</div>
<div className="w-full top-1/2 max-w-sm absolute pt-12 -translate-y-1/2">
<div
aria-hidden="true"
className="absolute inset-0 grid grid-cols-2 opacity-20"
@@ -12,10 +23,6 @@ export default function Layout() {
<div className="blur-[106px] h-42 bg-gradient-to-r from-lime-400 to-lime-600"></div>
</div>
</div>
<div>
<Outlet />
</div>
</div>
)
}

View File

@@ -0,0 +1 @@
<svg width="18" height="24" viewBox="0 0 18 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16.2756 23.4353L8.93847 20.1298C8.7383 20.0015 8.48167 20.0015 8.27893 20.1298L0.941837 23.4353C0.533793 23.6945 0 23.4019 0 22.9194V1.12629C0.00256631 0.787535 0.277162 0.512939 0.615915 0.512939H16.6066C16.9454 0.512939 17.22 0.787535 17.22 1.12629V22.9194C17.22 23.4019 16.6862 23.6945 16.2781 23.4353H16.2756Z" fill="#8CD366"></path><path d="M10.7274 3.71313H3.34668V6.41803H10.7274V3.71313Z" fill="#2E3524"></path><path d="M9.42115 8.4939H3.34668V10.6496H9.42115V8.4939Z" fill="#2E3524"></path><path d="M10.7274 12.7263H3.34668V15.4312H10.7274V12.7263Z" fill="#2E3524"></path><path d="M12.9984 13.6731H12.9958C12.5111 13.6731 12.1182 14.066 12.1182 14.5508V14.5533C12.1182 15.0381 12.5111 15.431 12.9958 15.431H12.9984C13.4831 15.431 13.8761 15.0381 13.8761 14.5533V14.5508C13.8761 14.066 13.4831 13.6731 12.9984 13.6731Z" fill="#2E3524"></path></svg>

After

Width:  |  Height:  |  Size: 961 B