feat: improve company forms, phone input, and auth redirects
This commit is contained in:
parent
6962d5e5b5
commit
604216ddec
3 changed files with 57 additions and 17 deletions
|
|
@ -15,7 +15,7 @@ export async function middleware(request: NextRequest) {
|
|||
return new NextResponse("Invalid Host header", { status: 403 })
|
||||
}
|
||||
|
||||
const { pathname, search } = request.nextUrl
|
||||
const { pathname, searchParams, search } = request.nextUrl
|
||||
|
||||
if (pathname.startsWith("/api")) {
|
||||
return NextResponse.next()
|
||||
|
|
@ -49,6 +49,18 @@ export async function middleware(request: NextRequest) {
|
|||
? ((session.user as unknown as { machinePersona?: string }).machinePersona ?? "").toLowerCase()
|
||||
: null
|
||||
|
||||
if (pathname === "/login") {
|
||||
const callback = searchParams.get("callbackUrl") ?? undefined
|
||||
const defaultDestination =
|
||||
role === "machine"
|
||||
? machinePersona === "manager"
|
||||
? "/dashboard"
|
||||
: "/portal/tickets"
|
||||
: APP_HOME
|
||||
const target = callback && !callback.startsWith("/login") ? callback : defaultDestination
|
||||
return NextResponse.redirect(new URL(target, request.url))
|
||||
}
|
||||
|
||||
// Ajusta destinos conforme persona da máquina para evitar loops login<->dashboard
|
||||
if (role === "machine") {
|
||||
// Evita enviar colaborador ao dashboard; redireciona para o Portal
|
||||
|
|
@ -56,11 +68,6 @@ export async function middleware(request: NextRequest) {
|
|||
return NextResponse.redirect(new URL("/portal/tickets", request.url))
|
||||
}
|
||||
// Evita mostrar login quando já há sessão de máquina
|
||||
if (pathname === "/login") {
|
||||
const target = machinePersona === "manager" ? "/dashboard" : "/portal/tickets"
|
||||
const url = new URL(target, request.url)
|
||||
return NextResponse.redirect(url)
|
||||
}
|
||||
}
|
||||
|
||||
const isAdmin = role === "admin"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, useTransition } from "react"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, useTransition, useId } from "react"
|
||||
import { formatDistanceToNow } from "date-fns"
|
||||
import { ptBR } from "date-fns/locale"
|
||||
import { useQuery } from "convex/react"
|
||||
|
|
@ -86,6 +86,15 @@ export function AdminCompaniesManager({ initialCompanies }: { initialCompanies:
|
|||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
const nameId = useId()
|
||||
const slugId = useId()
|
||||
const descriptionId = useId()
|
||||
const cnpjId = useId()
|
||||
const domainId = useId()
|
||||
const phoneId = useId()
|
||||
const addressId = useId()
|
||||
const hoursId = useId()
|
||||
|
||||
const machinesQuery = useQuery(api.machines.listByTenant, { includeMetadata: false }) as MachineSummary[] | undefined
|
||||
const machinesByCompanyId = useMemo(() => {
|
||||
const map = new Map<string, MachineSummary[]>()
|
||||
|
|
@ -304,60 +313,80 @@ export function AdminCompaniesManager({ initialCompanies }: { initialCompanies:
|
|||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div className="grid gap-2">
|
||||
<Label>Nome</Label>
|
||||
<Label htmlFor={nameId}>Nome</Label>
|
||||
<Input
|
||||
id={nameId}
|
||||
name="companyName"
|
||||
value={form.name ?? ""}
|
||||
onChange={(e) => setForm((p) => ({ ...p, name: e.target.value }))}
|
||||
placeholder="Nome da empresa ou apelido interno"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>Slug</Label>
|
||||
<Label htmlFor={slugId}>Slug</Label>
|
||||
<Input
|
||||
id={slugId}
|
||||
name="companySlug"
|
||||
value={form.slug ?? ""}
|
||||
onChange={(e) => setForm((p) => ({ ...p, slug: e.target.value }))}
|
||||
placeholder="empresa-exemplo"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2 md:col-span-2">
|
||||
<Label>Descrição</Label>
|
||||
<Input value={form.description ?? ""} onChange={(e) => setForm((p) => ({ ...p, description: e.target.value }))} placeholder="Resumo, segmento ou observações internas" />
|
||||
<Label htmlFor={descriptionId}>Descrição</Label>
|
||||
<Input
|
||||
id={descriptionId}
|
||||
name="companyDescription"
|
||||
value={form.description ?? ""}
|
||||
onChange={(e) => setForm((p) => ({ ...p, description: e.target.value }))}
|
||||
placeholder="Resumo, segmento ou observações internas"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>CNPJ</Label>
|
||||
<Label htmlFor={cnpjId}>CNPJ</Label>
|
||||
<Input
|
||||
id={cnpjId}
|
||||
name="companyCnpj"
|
||||
value={form.cnpj ?? ""}
|
||||
onChange={(e) => setForm((p) => ({ ...p, cnpj: e.target.value }))}
|
||||
placeholder="00.000.000/0000-00"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>Domínio</Label>
|
||||
<Label htmlFor={domainId}>Domínio</Label>
|
||||
<Input
|
||||
id={domainId}
|
||||
name="companyDomain"
|
||||
value={form.domain ?? ""}
|
||||
onChange={(e) => setForm((p) => ({ ...p, domain: e.target.value }))}
|
||||
placeholder="empresa.com.br"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>Telefone</Label>
|
||||
<Label htmlFor={phoneId}>Telefone</Label>
|
||||
<PhoneInput
|
||||
id={phoneId}
|
||||
name="companyPhone"
|
||||
value={form.phone ?? ""}
|
||||
onChange={(value) => setForm((p) => ({ ...p, phone: value || null }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2 md:col-span-2">
|
||||
<Label>Endereço</Label>
|
||||
<Label htmlFor={addressId}>Endereço</Label>
|
||||
<Input
|
||||
id={addressId}
|
||||
name="companyAddress"
|
||||
value={form.address ?? ""}
|
||||
onChange={(e) => setForm((p) => ({ ...p, address: e.target.value }))}
|
||||
placeholder="Rua, número, bairro, cidade/UF"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>Horas contratadas/mês</Label>
|
||||
<Label htmlFor={hoursId}>Horas contratadas/mês</Label>
|
||||
<Input
|
||||
type="number"
|
||||
id={hoursId}
|
||||
name="companyHours"
|
||||
min={0}
|
||||
step="0.25"
|
||||
value={form.contractedHoursPerMonth ?? ""}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ function placeholderFor(country: CountryOption): string {
|
|||
return "Número de telefone"
|
||||
}
|
||||
|
||||
export function PhoneInput({ value, onChange, className }: PhoneInputProps) {
|
||||
export function PhoneInput({ value, onChange, className, id, name }: PhoneInputProps) {
|
||||
const [selectedCountry, setSelectedCountry] = useState<CountryOption>(DEFAULT_COUNTRY)
|
||||
const [localDigits, setLocalDigits] = useState<string>("")
|
||||
|
||||
|
|
@ -144,6 +144,8 @@ export function PhoneInput({ value, onChange, className }: PhoneInputProps) {
|
|||
</SelectContent>
|
||||
</Select>
|
||||
<Input
|
||||
id={id}
|
||||
name={name}
|
||||
type="tel"
|
||||
value={displayValue}
|
||||
onChange={handleInputChange}
|
||||
|
|
@ -158,6 +160,8 @@ export type PhoneInputProps = {
|
|||
value?: string | null
|
||||
onChange?: (value: string) => void
|
||||
className?: string
|
||||
id?: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export function formatPhoneDisplay(rawValue?: string | null): string | null {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue