fix: corrige acentuacao e melhora UX de softwares
All checks were successful
All checks were successful
- Adiciona botao de limpar pesquisa em softwares - Corrige paginacao com historico de cursores - Corrige acentuacao em politicas de SLA (politica -> política) - Corrige acentuacao em varios textos do frontend 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cfb72358bc
commit
73de65bbaf
3 changed files with 68 additions and 41 deletions
|
|
@ -84,7 +84,8 @@
|
|||
"Bash(dir \"D:\\Projetos IA\\sistema-de-chamados\\src\\components\\ui\" /b)",
|
||||
"Bash(timeout 120 bun:*)",
|
||||
"Bash(bun run tauri:build:*)",
|
||||
"Bash(git remote:*)"
|
||||
"Bash(git remote:*)",
|
||||
"Bash(powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"D:/Projetos IA/sistema-de-chamados/scripts/test-windows-collection.ps1\")"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useState } from "react"
|
|||
import { useQuery } from "convex/react"
|
||||
import { formatDistanceToNow } from "date-fns"
|
||||
import { ptBR } from "date-fns/locale"
|
||||
import { Package, Search, ChevronLeft, ChevronRight } from "lucide-react"
|
||||
import { Package, Search, ChevronLeft, ChevronRight, Eraser } from "lucide-react"
|
||||
|
||||
import { api } from "@/convex/_generated/api"
|
||||
import type { Id } from "@/convex/_generated/dataModel"
|
||||
|
|
@ -25,7 +25,10 @@ export function DeviceSoftwareList({ machineId }: DeviceSoftwareListProps) {
|
|||
const viewerId = convexUserId as Id<"users"> | undefined
|
||||
|
||||
const [search, setSearch] = useState("")
|
||||
const [cursor, setCursor] = useState<string | null>(null)
|
||||
const [cursorHistory, setCursorHistory] = useState<(string | null)[]>([null])
|
||||
const [pageIndex, setPageIndex] = useState(0)
|
||||
|
||||
const currentCursor = cursorHistory[pageIndex] ?? null
|
||||
|
||||
const result = useQuery(
|
||||
api.machineSoftware.listByMachine,
|
||||
|
|
@ -36,7 +39,7 @@ export function DeviceSoftwareList({ machineId }: DeviceSoftwareListProps) {
|
|||
machineId,
|
||||
search: search.trim() || undefined,
|
||||
limit: 30,
|
||||
cursor: cursor ?? undefined,
|
||||
cursor: currentCursor ?? undefined,
|
||||
}
|
||||
: "skip"
|
||||
)
|
||||
|
|
@ -45,17 +48,26 @@ export function DeviceSoftwareList({ machineId }: DeviceSoftwareListProps) {
|
|||
|
||||
const handleSearch = (value: string) => {
|
||||
setSearch(value)
|
||||
setCursor(null)
|
||||
setCursorHistory([null])
|
||||
setPageIndex(0)
|
||||
}
|
||||
|
||||
const handleNextPage = () => {
|
||||
if (result?.nextCursor) {
|
||||
setCursor(result.nextCursor)
|
||||
const nextIndex = pageIndex + 1
|
||||
setCursorHistory((prev) => {
|
||||
const updated = [...prev]
|
||||
updated[nextIndex] = result.nextCursor
|
||||
return updated
|
||||
})
|
||||
setPageIndex(nextIndex)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePrevPage = () => {
|
||||
setCursor(null)
|
||||
if (pageIndex > 0) {
|
||||
setPageIndex(pageIndex - 1)
|
||||
}
|
||||
}
|
||||
|
||||
if (!viewerId) {
|
||||
|
|
@ -76,14 +88,28 @@ export function DeviceSoftwareList({ machineId }: DeviceSoftwareListProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-neutral-400" />
|
||||
<Input
|
||||
placeholder="Buscar por nome, versão ou fabricante..."
|
||||
value={search}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
className="pl-9"
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-neutral-400" />
|
||||
<Input
|
||||
placeholder="Buscar por nome, versão ou fabricante..."
|
||||
value={search}
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
className="pl-9"
|
||||
/>
|
||||
</div>
|
||||
{search && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => handleSearch("")}
|
||||
className="size-10 shrink-0"
|
||||
title="Limpar pesquisa"
|
||||
>
|
||||
<Eraser className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{result === undefined ? (
|
||||
|
|
@ -147,7 +173,7 @@ export function DeviceSoftwareList({ machineId }: DeviceSoftwareListProps) {
|
|||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handlePrevPage}
|
||||
disabled={!cursor}
|
||||
disabled={pageIndex === 0}
|
||||
className="h-8 w-8 p-0"
|
||||
>
|
||||
<ChevronLeft className="size-4" />
|
||||
|
|
|
|||
|
|
@ -138,11 +138,11 @@ export function SlasManager() {
|
|||
|
||||
const handleSave = async () => {
|
||||
if (!name.trim()) {
|
||||
toast.error("Informe um nome para a politica")
|
||||
toast.error("Informe um nome para a política")
|
||||
return
|
||||
}
|
||||
if (!convexUserId) {
|
||||
toast.error("Sessao nao sincronizada com o Convex")
|
||||
toast.error("Sessão não sincronizada com o Convex")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ export function SlasManager() {
|
|||
|
||||
setSaving(true)
|
||||
const toastId = editingSla ? "sla-edit" : "sla-create"
|
||||
toast.loading(editingSla ? "Salvando alteracoes..." : "Criando politica...", { id: toastId })
|
||||
toast.loading(editingSla ? "Salvando alterações..." : "Criando política...", { id: toastId })
|
||||
|
||||
try {
|
||||
if (editingSla) {
|
||||
|
|
@ -164,7 +164,7 @@ export function SlasManager() {
|
|||
timeToFirstResponse,
|
||||
timeToResolution,
|
||||
})
|
||||
toast.success("Politica atualizada", { id: toastId })
|
||||
toast.success("Política atualizada", { id: toastId })
|
||||
} else {
|
||||
await createSla({
|
||||
tenantId,
|
||||
|
|
@ -174,12 +174,12 @@ export function SlasManager() {
|
|||
timeToFirstResponse,
|
||||
timeToResolution,
|
||||
})
|
||||
toast.success("Politica criada", { id: toastId })
|
||||
toast.success("Política criada", { id: toastId })
|
||||
}
|
||||
closeDialog()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error(editingSla ? "Nao foi possivel atualizar a politica" : "Nao foi possivel criar a politica", { id: toastId })
|
||||
toast.error(editingSla ? "Não foi possível atualizar a política" : "Não foi possível criar a política", { id: toastId })
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
|
|
@ -213,7 +213,7 @@ export function SlasManager() {
|
|||
<Card className="border-slate-200">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-neutral-600">
|
||||
<IconTargetArrow className="size-4" /> Politicas globais
|
||||
<IconTargetArrow className="size-4" /> Políticas globais
|
||||
</CardTitle>
|
||||
<CardDescription>Regras que valem para todas as empresas.</CardDescription>
|
||||
</CardHeader>
|
||||
|
|
@ -245,23 +245,23 @@ export function SlasManager() {
|
|||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Politicas globais de SLA */}
|
||||
{/* Políticas globais de SLA */}
|
||||
<Card className="border-slate-200">
|
||||
<CardHeader>
|
||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div>
|
||||
<CardTitle className="flex items-center gap-2 text-lg font-semibold text-neutral-900">
|
||||
<Globe className="size-5" />
|
||||
Politicas globais de SLA
|
||||
Políticas globais de SLA
|
||||
</CardTitle>
|
||||
<CardDescription className="mt-1">
|
||||
Estas regras valem para todas as empresas e categorias. Sao sobrescritas por regras mais especificas
|
||||
Estas regras valem para todas as empresas e categorias. São sobrescritas por regras mais específicas
|
||||
(por empresa ou por categoria).
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button size="sm" onClick={openCreateDialog} disabled={!convexUserId} className="gap-2 shrink-0">
|
||||
<Plus className="size-4" />
|
||||
Nova politica
|
||||
Nova política
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
|
@ -275,9 +275,9 @@ export function SlasManager() {
|
|||
) : slas.length === 0 ? (
|
||||
<div className="rounded-xl border border-dashed border-slate-300 bg-slate-50/80 p-6 text-center">
|
||||
<Globe className="mx-auto size-8 text-slate-400" />
|
||||
<p className="mt-2 text-sm font-medium text-neutral-700">Nenhuma politica global cadastrada</p>
|
||||
<p className="mt-2 text-sm font-medium text-neutral-700">Nenhuma política global cadastrada</p>
|
||||
<p className="mt-1 text-xs text-neutral-500">
|
||||
Crie politicas de SLA para definir metas de resposta e resolucao para os chamados.
|
||||
Crie políticas de SLA para definir metas de resposta e resolução para os chamados.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -300,7 +300,7 @@ export function SlasManager() {
|
|||
)}
|
||||
<div className="flex gap-4 text-xs text-neutral-600">
|
||||
<span>Resposta: {formatMinutes(policy.timeToFirstResponse)}</span>
|
||||
<span>Resolucao: {formatMinutes(policy.timeToResolution)}</span>
|
||||
<span>Resolução: {formatMinutes(policy.timeToResolution)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -328,30 +328,30 @@ export function SlasManager() {
|
|||
<Dialog open={dialogOpen} onOpenChange={(open) => !open && closeDialog()}>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingSla ? "Editar politica de SLA" : "Nova politica de SLA"}</DialogTitle>
|
||||
<DialogTitle>{editingSla ? "Editar política de SLA" : "Nova política de SLA"}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{editingSla
|
||||
? "Altere os dados da politica. Ela continua valendo para todas as empresas e categorias."
|
||||
: "Crie uma politica global que vale para todas as empresas e categorias. Voce pode criar regras mais especificas depois."}
|
||||
? "Altere os dados da política. Ela continua valendo para todas as empresas e categorias."
|
||||
: "Crie uma política global que vale para todas as empresas e categorias. Você pode criar regras mais específicas depois."}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="sla-name">Nome da politica</Label>
|
||||
<Label htmlFor="sla-name">Nome da política</Label>
|
||||
<Input
|
||||
id="sla-name"
|
||||
placeholder="Ex.: Atendimento padrao, Premium, Urgente..."
|
||||
placeholder="Ex.: Atendimento padrão, Premium, Urgente..."
|
||||
value={name}
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="sla-description">Descricao (opcional)</Label>
|
||||
<Label htmlFor="sla-description">Descrição (opcional)</Label>
|
||||
<textarea
|
||||
id="sla-description"
|
||||
className="min-h-[80px] w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm text-neutral-700 shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900/10"
|
||||
placeholder="Quando esta politica deve ser usada, para quais tipos de chamado..."
|
||||
placeholder="Quando esta política deve ser usada, para quais tipos de chamado..."
|
||||
value={description}
|
||||
onChange={(event) => setDescription(event.target.value)}
|
||||
/>
|
||||
|
|
@ -384,7 +384,7 @@ export function SlasManager() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Tempo para resolucao</Label>
|
||||
<Label>Tempo para resolução</Label>
|
||||
<p className="text-xs text-neutral-500">Quanto tempo a equipe tem para resolver o chamado por completo.</p>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
|
|
@ -412,8 +412,8 @@ export function SlasManager() {
|
|||
</div>
|
||||
<div className="rounded-lg border border-blue-200 bg-blue-50 p-3">
|
||||
<p className="text-xs text-blue-800">
|
||||
<strong>Dica:</strong> Esta politica e global e sera aplicada a todos os chamados que nao tiverem uma
|
||||
regra mais especifica (por empresa ou categoria). Use as secoes abaixo para criar regras personalizadas.
|
||||
<strong>Dica:</strong> Esta política é global e será aplicada a todos os chamados que não tiverem uma
|
||||
regra mais específica (por empresa ou categoria). Use as seções abaixo para criar regras personalizadas.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -422,7 +422,7 @@ export function SlasManager() {
|
|||
Cancelar
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={saving || !name.trim()}>
|
||||
{saving ? "Salvando..." : editingSla ? "Salvar alteracoes" : "Criar politica"}
|
||||
{saving ? "Salvando..." : editingSla ? "Salvar alterações" : "Criar política"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue