update login

This commit is contained in:
2025-11-28 16:07:59 -03:00
parent 2b0efc654a
commit c71e19eacb
14 changed files with 251 additions and 16 deletions

View File

@@ -47,7 +47,10 @@ export function meta({}: Route.MetaArgs) {
}
export async function action({ request, context }: Route.ActionArgs) {
const issuerUrl = new URL('/session', context.cloudflare.env.ISSUER_URL)
const issuerUrl = new URL(
'/authentication',
context.cloudflare.env.ISSUER_URL
)
const formData = Object.fromEntries(await request.formData())
try {
@@ -73,7 +76,7 @@ export async function action({ request, context }: Route.ActionArgs) {
headers.set('Location', url.toString())
return new Response(await r.text(), {
status: 302,
status: 402,
headers
})
} catch (error) {

View File

@@ -9,7 +9,7 @@ export default [
layout('routes/layout.tsx', [
index('routes/index.tsx'),
route('certs', 'routes/certs.tsx'),
route('orders', 'routes/orders.tsx'),
route('history', 'routes/history.tsx'),
route('settings', 'routes/settings/layout.tsx', [
index('routes/settings/profile.tsx'),
route('emails', 'routes/settings/emails/index.tsx'),

View File

@@ -31,7 +31,7 @@ async function proxy({
? await response.text()
: await response.arrayBuffer()
return new Response(body, {
return new Response(body ? body : null, {
status: response.status,
headers: response.headers
})

View File

@@ -42,7 +42,7 @@ const navMain = [
},
{
title: 'Histórico de compras',
url: '/orders'
url: '/history'
}
]

View File

@@ -94,7 +94,7 @@ export function Add() {
<Spinner />
</div>
)}
Adicionar
Adicionar email
</Button>
</div>
</fieldset>

View File

@@ -35,12 +35,19 @@ export function Primary({ items = [] }: { items: Email[] }) {
async ({ email }) => {
// Doesn't use `user` because the data could be outdated
const selected = emails.find((e) => e.email === email)
const new_email = selected?.email
const old_email = primary?.email
if (new_email === old_email) {
return
}
const r = await fetch(`/api/users/${user.id}/emails/primary`, {
method: 'PATCH',
headers: new Headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify({
new_email: selected?.email,
old_email: primary?.email,
new_email,
old_email,
email_verified: selected?.email_verified
})
})
@@ -87,7 +94,7 @@ export function Primary({ items = [] }: { items: Email[] }) {
})}
</NativeSelect>
<Button type="submit" className="overflow-hidden cursor-pointer">
Alterar
Alterar email
</Button>
</form>
</CardContent>

View File

@@ -1,5 +1,164 @@
import type { Route } from './+types/password'
export default function Route({}: Route.ComponentProps) {
return <></>
import { useToggle } from 'ahooks'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { toast } from 'sonner'
import { useFetcher } from 'react-router'
import { useEffect } from 'react'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@repo/ui/components/ui/form'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '@repo/ui/components/ui/card'
import { Input } from '@repo/ui/components/ui/input'
import { Button } from '@repo/ui/components/ui/button'
import { Spinner } from '@repo/ui/components/ui/spinner'
import { Checkbox } from '@repo/ui/components/ui/checkbox'
import { Label } from '@repo/ui/components/ui/label'
import { request as req, HttpMethod } from '@repo/util/request'
import { userContext } from '@repo/auth/context'
import type { User } from '@repo/auth/auth'
const formSchema = z
.object({
new_password: z.string().min(6, 'Deve ter no mínimo 6 caracteres'),
confirm_password: z.string().min(6, 'Deve ter no mínimo 6 caracteres')
})
.refine((data) => data.new_password === data.confirm_password, {
message: 'As senhas não coincidem',
path: ['confirm_password']
})
type Schema = z.infer<typeof formSchema>
export async function action({ request, context }: Route.ActionArgs) {
const user = context.get(userContext) as User
const body = await request.json()
const r = await req({
url: `users/${user.sub}/password`,
headers: new Headers({ 'Content-Type': 'application/json' }),
method: HttpMethod.POST,
body: JSON.stringify(body),
request,
context
})
if (!r.ok) {
const error = await r.json().catch(() => ({}))
return { ok: false, error }
}
return { ok: true }
}
export default function Route({}: Route.ComponentProps) {
const [show, { toggle }] = useToggle()
const inputType = show ? 'text' : 'password'
const fetcher = useFetcher()
const form = useForm({
resolver: zodResolver(formSchema)
})
const { control, formState, handleSubmit, reset } = form
const onSubmit = async ({ new_password }: Schema) => {
await fetcher.submit(JSON.stringify({ new_password }), {
method: 'post',
encType: 'application/json'
})
}
useEffect(() => {
if (fetcher.data?.ok) {
toast.success('A senha foi alterada')
return reset()
}
switch (fetcher.data?.error?.type) {
case 'UserConflictError':
toast.error('O colaborador já foi vinculado anteriormente')
}
}, [fetcher.data, reset])
return (
<Form {...form}>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-2.5">
<Card>
<CardHeader>
<CardTitle className="text-lg">Alterar senha</CardTitle>
<CardDescription>
Sua senha deve ter no mínimo 6 caracteres. Recomendamos que inclua
uma combinação de números, letras e caracteres especiais (ex.:
!$@%).
</CardDescription>
</CardHeader>
<CardContent className="space-y-2.5">
<FormField
control={control}
name="new_password"
defaultValue=""
render={({ field }) => (
<FormItem>
<FormLabel>Nova senha</FormLabel>
<FormControl>
<Input type={inputType} autoComplete="false" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={control}
name="confirm_password"
defaultValue=""
render={({ field }) => (
<FormItem>
<FormLabel>Confirmar nova senha</FormLabel>
<FormControl>
<Input type={inputType} autoComplete="false" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex items-center gap-3">
<Checkbox id="showPassword" onClick={toggle} tabIndex={-1} />
<Label htmlFor="showPassword" className="cursor-pointer">
Mostrar senha
</Label>
</div>
</CardContent>
</Card>
<div className="flex justify-end">
<Button
type="submit"
className="relative overflow-hidden cursor-pointer"
>
{formState.isSubmitting && (
<div className="absolute inset-0 bg-lime-500 flex items-center justify-center">
<Spinner />
</div>
)}
Alterar senha
</Button>
</div>
</form>
</Form>
)
}

View File

@@ -55,9 +55,10 @@ export default function Route({}: Route.ComponentProps) {
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<Card>
<CardHeader>
<CardTitle className="text-2xl">Minha conta</CardTitle>
<CardTitle className="text-2xl">Meu perfil</CardTitle>
<CardDescription>
Gerenciar as configurações da sua conta.
Mantenha os dados do seu perfil atualizados para que apareçam
corretamente em seus cursos e certificados.
</CardDescription>
</CardHeader>
@@ -136,7 +137,7 @@ export default function Route({}: Route.ComponentProps) {
disabled={formState.isSubmitting}
>
{formState.isSubmitting && <Spinner />}
Editar
Atualizar perfil
</Button>
</div>
</form>