Ajusta portal do cliente e desbloqueia abertura de chamados
This commit is contained in:
parent
12c7fa23ae
commit
6a04ef4843
4 changed files with 34 additions and 9 deletions
|
|
@ -79,8 +79,8 @@ export function PortalShell({ children }: PortalShellProps) {
|
||||||
<GalleryVerticalEnd className="size-4" />
|
<GalleryVerticalEnd className="size-4" />
|
||||||
</span>
|
</span>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="text-xs font-semibold uppercase tracking-[0.08em] text-neutral-500 sm:tracking-[0.12em]">
|
<span className="text-xs font-semibold tracking-[0.04em] text-neutral-500 sm:tracking-[0.06em]">
|
||||||
Portal do cliente
|
Portal do Cliente
|
||||||
</span>
|
</span>
|
||||||
<span className="text-lg font-semibold text-neutral-900">Raven</span>
|
<span className="text-lg font-semibold text-neutral-900">Raven</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useMemo, useState } from "react"
|
import { useCallback, useMemo, useState } from "react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { useMutation } from "convex/react"
|
import { useMutation } from "convex/react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
@ -45,10 +45,20 @@ export function PortalTicketForm() {
|
||||||
const [subcategoryId, setSubcategoryId] = useState<string | null>(null)
|
const [subcategoryId, setSubcategoryId] = useState<string | null>(null)
|
||||||
const [attachments, setAttachments] = useState<Array<{ storageId: string; name: string; size?: number; type?: string }>>([])
|
const [attachments, setAttachments] = useState<Array<{ storageId: string; name: string; size?: number; type?: string }>>([])
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
const [hasSubcategoryOptions, setHasSubcategoryOptions] = useState(false)
|
||||||
|
|
||||||
const isFormValid = useMemo(() => {
|
const isFormValid = useMemo(() => {
|
||||||
return Boolean(subject.trim() && description.trim() && categoryId && subcategoryId)
|
return Boolean(
|
||||||
}, [subject, description, categoryId, subcategoryId])
|
subject.trim() &&
|
||||||
|
description.trim() &&
|
||||||
|
categoryId &&
|
||||||
|
(hasSubcategoryOptions ? subcategoryId : true)
|
||||||
|
)
|
||||||
|
}, [subject, description, categoryId, subcategoryId, hasSubcategoryOptions])
|
||||||
|
|
||||||
|
const handleSecondaryOptionsChange = useCallback((hasOptions: boolean) => {
|
||||||
|
setHasSubcategoryOptions((prev) => (prev === hasOptions ? prev : hasOptions))
|
||||||
|
}, [])
|
||||||
|
|
||||||
async function handleSubmit(event: React.FormEvent) {
|
async function handleSubmit(event: React.FormEvent) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
@ -76,7 +86,7 @@ export function PortalTicketForm() {
|
||||||
queueId: undefined,
|
queueId: undefined,
|
||||||
requesterId: viewerId,
|
requesterId: viewerId,
|
||||||
categoryId: categoryId as Id<"ticketCategories">,
|
categoryId: categoryId as Id<"ticketCategories">,
|
||||||
subcategoryId: subcategoryId as Id<"ticketSubcategories">,
|
subcategoryId: subcategoryId ? (subcategoryId as Id<"ticketSubcategories">) : undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (plainDescription.length > 0) {
|
if (plainDescription.length > 0) {
|
||||||
|
|
@ -159,9 +169,10 @@ export function PortalTicketForm() {
|
||||||
subcategoryId={subcategoryId}
|
subcategoryId={subcategoryId}
|
||||||
onCategoryChange={setCategoryId}
|
onCategoryChange={setCategoryId}
|
||||||
onSubcategoryChange={setSubcategoryId}
|
onSubcategoryChange={setSubcategoryId}
|
||||||
|
onSecondaryOptionsChange={handleSecondaryOptionsChange}
|
||||||
layout="stacked"
|
layout="stacked"
|
||||||
categoryLabel="Categoria *"
|
categoryLabel="Categoria *"
|
||||||
subcategoryLabel="Subcategoria *"
|
subcategoryLabel={hasSubcategoryOptions ? "Subcategoria *" : "Subcategoria (opcional)"}
|
||||||
secondaryEmptyLabel="Selecione uma categoria"
|
secondaryEmptyLabel="Selecione uma categoria"
|
||||||
/>
|
/>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ interface CategorySelectProps {
|
||||||
categoryId: string | null
|
categoryId: string | null
|
||||||
subcategoryId: string | null
|
subcategoryId: string | null
|
||||||
onCategoryChange: (categoryId: string) => void
|
onCategoryChange: (categoryId: string) => void
|
||||||
onSubcategoryChange: (subcategoryId: string) => void
|
onSubcategoryChange: (subcategoryId: string | null) => void
|
||||||
|
onSecondaryOptionsChange?: (hasSecondary: boolean) => void
|
||||||
autoSelectFirst?: boolean
|
autoSelectFirst?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
categoryLabel?: string
|
categoryLabel?: string
|
||||||
|
|
@ -39,6 +40,7 @@ export function CategorySelectFields({
|
||||||
subcategoryId,
|
subcategoryId,
|
||||||
onCategoryChange,
|
onCategoryChange,
|
||||||
onSubcategoryChange,
|
onSubcategoryChange,
|
||||||
|
onSecondaryOptionsChange,
|
||||||
autoSelectFirst = true,
|
autoSelectFirst = true,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
categoryLabel = "Primária",
|
categoryLabel = "Primária",
|
||||||
|
|
@ -72,10 +74,22 @@ export function CategorySelectFields({
|
||||||
const first = secondaryOptions[0]
|
const first = secondaryOptions[0]
|
||||||
if (first) {
|
if (first) {
|
||||||
onSubcategoryChange(first.id)
|
onSubcategoryChange(first.id)
|
||||||
|
} else {
|
||||||
|
onSubcategoryChange(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [categoryId, secondaryOptions, subcategoryId, onSubcategoryChange])
|
}, [categoryId, secondaryOptions, subcategoryId, onSubcategoryChange])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onSecondaryOptionsChange?.(secondaryOptions.length > 0)
|
||||||
|
}, [secondaryOptions, onSecondaryOptionsChange])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (secondaryOptions.length === 0 && subcategoryId) {
|
||||||
|
onSubcategoryChange(null)
|
||||||
|
}
|
||||||
|
}, [secondaryOptions, subcategoryId, onSubcategoryChange])
|
||||||
|
|
||||||
const containerClass = layout === "stacked" ? "flex flex-col gap-3" : "grid gap-3 sm:grid-cols-2"
|
const containerClass = layout === "stacked" ? "flex flex-col gap-3" : "grid gap-3 sm:grid-cols-2"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ export function NewTicketDialog() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubcategoryChange = (value: string) => {
|
const handleSubcategoryChange = (value: string | null) => {
|
||||||
const previous = form.getValues("subcategoryId") ?? ""
|
const previous = form.getValues("subcategoryId") ?? ""
|
||||||
const next = value ?? ""
|
const next = value ?? ""
|
||||||
form.setValue("subcategoryId", next, {
|
form.setValue("subcategoryId", next, {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue