Log machine context errors in portal
This commit is contained in:
parent
545d5bea4b
commit
0fb95147f4
3 changed files with 82 additions and 5 deletions
|
|
@ -23,7 +23,7 @@ const navItems = [
|
||||||
export function PortalShell({ children }: PortalShellProps) {
|
export function PortalShell({ children }: PortalShellProps) {
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { session, machineContext } = useAuth()
|
const { session, machineContext, machineContextError, machineContextLoading } = useAuth()
|
||||||
const [isSigningOut, setIsSigningOut] = useState(false)
|
const [isSigningOut, setIsSigningOut] = useState(false)
|
||||||
|
|
||||||
const isMachineSession = session?.user.role === "machine"
|
const isMachineSession = session?.user.role === "machine"
|
||||||
|
|
@ -136,7 +136,25 @@ export function PortalShell({ children }: PortalShellProps) {
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-6 px-6 py-8">
|
<main className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-6 px-6 py-8">
|
||||||
{null}
|
{machineContextError ? (
|
||||||
|
<div className="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 shadow-sm">
|
||||||
|
<p className="font-semibold text-red-800">Falha ao carregar os dados do colaborador vinculado.</p>
|
||||||
|
<p className="mt-1 text-xs text-red-700/80">
|
||||||
|
{machineContextError.message}
|
||||||
|
{machineContextError.status ? ` (status ${machineContextError.status})` : null}
|
||||||
|
</p>
|
||||||
|
{machineContextError.details && Object.keys(machineContextError.details).length > 0 ? (
|
||||||
|
<pre className="mt-2 overflow-x-auto rounded-lg bg-red-100 px-3 py-2 text-[11px] leading-tight text-red-800">
|
||||||
|
{JSON.stringify(machineContextError.details, null, 2)}
|
||||||
|
</pre>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{!machineContextError && machineContextLoading ? (
|
||||||
|
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-neutral-600 shadow-sm">
|
||||||
|
Recuperando dados do colaborador vinculado...
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
<footer className="border-t border-slate-200 bg-white/70">
|
<footer className="border-t border-slate-200 bg-white/70">
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ function toHtml(text: string) {
|
||||||
|
|
||||||
export function PortalTicketForm() {
|
export function PortalTicketForm() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { convexUserId, session, machineContext } = useAuth()
|
const { convexUserId, session, machineContext, machineContextError, machineContextLoading } = useAuth()
|
||||||
const createTicket = useMutation(api.tickets.create)
|
const createTicket = useMutation(api.tickets.create)
|
||||||
const addComment = useMutation(api.tickets.addComment)
|
const addComment = useMutation(api.tickets.addComment)
|
||||||
|
|
||||||
|
|
@ -49,13 +49,19 @@ export function PortalTicketForm() {
|
||||||
return Boolean(subject.trim() && description.trim() && categoryId && subcategoryId)
|
return Boolean(subject.trim() && description.trim() && categoryId && subcategoryId)
|
||||||
}, [subject, description, categoryId, subcategoryId])
|
}, [subject, description, categoryId, subcategoryId])
|
||||||
const isViewerReady = Boolean(viewerId)
|
const isViewerReady = Boolean(viewerId)
|
||||||
|
const viewerErrorMessage = useMemo(() => {
|
||||||
|
if (!machineContextError) return null
|
||||||
|
const suffix = machineContextError.status ? ` (status ${machineContextError.status})` : ""
|
||||||
|
return `${machineContextError.message}${suffix}`
|
||||||
|
}, [machineContextError])
|
||||||
|
|
||||||
async function handleSubmit(event: React.FormEvent) {
|
async function handleSubmit(event: React.FormEvent) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (isSubmitting || !isFormValid) return
|
if (isSubmitting || !isFormValid) return
|
||||||
if (!viewerId) {
|
if (!viewerId) {
|
||||||
|
const detail = viewerErrorMessage ? ` Detalhes: ${viewerErrorMessage}` : ""
|
||||||
toast.error(
|
toast.error(
|
||||||
"Não foi possível identificar o colaborador vinculado a esta máquina. Tente abrir novamente o portal ou contate o suporte.",
|
`N<EFBFBD>o foi poss<73>vel identificar o colaborador vinculado a esta m<>quina. Tente abrir novamente o portal ou contate o suporte.${detail}`,
|
||||||
{ id: "portal-new-ticket" }
|
{ id: "portal-new-ticket" }
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
@ -124,6 +130,14 @@ export function PortalTicketForm() {
|
||||||
{!isViewerReady ? (
|
{!isViewerReady ? (
|
||||||
<div className="rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700">
|
<div className="rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700">
|
||||||
Vincule esta máquina a um colaborador na aplicação desktop para enviar chamados em nome dele.
|
Vincule esta máquina a um colaborador na aplicação desktop para enviar chamados em nome dele.
|
||||||
|
{machineContextLoading ? (
|
||||||
|
<p className="mt-2 text-xs text-amber-600">Carregando informa<EFBFBD><EFBFBD>es da m<EFBFBD>quina...</p>
|
||||||
|
) : null}
|
||||||
|
{viewerErrorMessage ? (
|
||||||
|
<p className="mt-2 text-xs text-amber-600">
|
||||||
|
Detalhes do erro: {viewerErrorMessage}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ type MachineContext = {
|
||||||
companyId: string | null
|
companyId: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MachineContextError = {
|
||||||
|
status: number
|
||||||
|
message: string
|
||||||
|
details?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
|
|
||||||
const authClient = createAuthClient({
|
const authClient = createAuthClient({
|
||||||
plugins: [customSessionClient<AppAuth>()],
|
plugins: [customSessionClient<AppAuth>()],
|
||||||
fetchOptions: {
|
fetchOptions: {
|
||||||
|
|
@ -53,6 +59,8 @@ type AuthContextValue = {
|
||||||
isStaff: boolean
|
isStaff: boolean
|
||||||
isCustomer: boolean
|
isCustomer: boolean
|
||||||
machineContext: MachineContext | null
|
machineContext: MachineContext | null
|
||||||
|
machineContextLoading: boolean
|
||||||
|
machineContextError: MachineContextError | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthContext = createContext<AuthContextValue>({
|
const AuthContext = createContext<AuthContextValue>({
|
||||||
|
|
@ -64,6 +72,8 @@ const AuthContext = createContext<AuthContextValue>({
|
||||||
isStaff: false,
|
isStaff: false,
|
||||||
isCustomer: false,
|
isCustomer: false,
|
||||||
machineContext: null,
|
machineContext: null,
|
||||||
|
machineContextLoading: false,
|
||||||
|
machineContextError: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
export function useAuth() {
|
export function useAuth() {
|
||||||
|
|
@ -77,6 +87,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||||
const ensureUser = useMutation(api.users.ensureUser)
|
const ensureUser = useMutation(api.users.ensureUser)
|
||||||
const [convexUserId, setConvexUserId] = useState<string | null>(null)
|
const [convexUserId, setConvexUserId] = useState<string | null>(null)
|
||||||
const [machineContext, setMachineContext] = useState<MachineContext | null>(null)
|
const [machineContext, setMachineContext] = useState<MachineContext | null>(null)
|
||||||
|
const [machineContextLoading, setMachineContextLoading] = useState(false)
|
||||||
|
const [machineContextError, setMachineContextError] = useState<MachineContextError | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!session?.user || session.user.role === "machine") {
|
if (!session?.user || session.user.role === "machine") {
|
||||||
|
|
@ -87,16 +99,37 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!session?.user || session.user.role !== "machine") {
|
if (!session?.user || session.user.role !== "machine") {
|
||||||
setMachineContext(null)
|
setMachineContext(null)
|
||||||
|
setMachineContextError(null)
|
||||||
|
setMachineContextLoading(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
setMachineContextLoading(true)
|
||||||
|
setMachineContextError(null)
|
||||||
;(async () => {
|
;(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/machines/session", { credentials: "include" })
|
const response = await fetch("/api/machines/session", { credentials: "include" })
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
let payload: Record<string, unknown> | null = null
|
||||||
|
try {
|
||||||
|
const parsed = await response.clone().json()
|
||||||
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||||
|
payload = parsed as Record<string, unknown>
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
payload = null
|
||||||
|
}
|
||||||
|
const fallbackMessage = "Falha ao carregar o contexto da m<>quina."
|
||||||
|
const message =
|
||||||
|
(payload && typeof payload.error === "string" && payload.error.trim()) || fallbackMessage
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setMachineContext(null)
|
setMachineContext(null)
|
||||||
|
setMachineContextError({
|
||||||
|
status: response.status,
|
||||||
|
message,
|
||||||
|
details: payload,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -122,11 +155,21 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||||
assignedUserRole: machine.assignedUserRole ?? null,
|
assignedUserRole: machine.assignedUserRole ?? null,
|
||||||
companyId: machine.companyId ?? null,
|
companyId: machine.companyId ?? null,
|
||||||
})
|
})
|
||||||
|
setMachineContextError(null)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load machine context", error)
|
console.error("Failed to load machine context", error)
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setMachineContext(null)
|
setMachineContext(null)
|
||||||
|
setMachineContextError({
|
||||||
|
status: 0,
|
||||||
|
message: "Erro ao carregar o contexto da m<>quina.",
|
||||||
|
details: error instanceof Error ? { message: error.message } : null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (!cancelled) {
|
||||||
|
setMachineContextLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
@ -184,8 +227,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||||
isStaff: isStaff(normalizedRole),
|
isStaff: isStaff(normalizedRole),
|
||||||
isCustomer: normalizedRole === "collaborator",
|
isCustomer: normalizedRole === "collaborator",
|
||||||
machineContext,
|
machineContext,
|
||||||
|
machineContextLoading,
|
||||||
|
machineContextError,
|
||||||
}),
|
}),
|
||||||
[session, isPending, effectiveConvexUserId, normalizedRole, machineContext]
|
[session, isPending, effectiveConvexUserId, normalizedRole, machineContext, machineContextLoading, machineContextError]
|
||||||
)
|
)
|
||||||
|
|
||||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue