From ef55d9bb24430610b60c1b77821307cd72c08c55 Mon Sep 17 00:00:00 2001 From: zwlucas Date: Sat, 25 Jul 2026 18:23:29 -0300 Subject: feat: upload of the project Signed-off-by: zwlucas --- web/src/pages/NewTicketPage.tsx | 203 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 web/src/pages/NewTicketPage.tsx (limited to 'web/src/pages/NewTicketPage.tsx') diff --git a/web/src/pages/NewTicketPage.tsx b/web/src/pages/NewTicketPage.tsx new file mode 100644 index 0000000..d716f1e --- /dev/null +++ b/web/src/pages/NewTicketPage.tsx @@ -0,0 +1,203 @@ +/** + * NewTicketPage.tsx + * + * Formulário para abertura de um novo chamado de TI. + * Visão do Solicitante — Fase 4, Passo 2. + */ + +import { useState, type FormEvent } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { api, type TicketPriority } from '@/lib/api'; +import { useAuth } from '@/context/AuthContext'; +import { useToast } from '@/context/ToastContext'; +import styles from './NewTicketPage.module.css'; + +// ─── Dados estáticos dos selects ────────────────────────────────────────────── + +const PRIORITIES: { value: TicketPriority; label: string; description: string }[] = [ + { value: 'low', label: '🟢 Baixa', description: 'Sem impacto imediato' }, + { value: 'medium', label: '🔵 Média', description: 'Impacto moderado' }, + { value: 'high', label: '🟡 Alta', description: 'Impacto significativo' }, + { value: 'critical', label: '🔴 Crítica', description: 'Sistema/serviço fora do ar' }, +]; + +// ─── Componente ─────────────────────────────────────────────────────────────── + +export default function NewTicketPage() { + const navigate = useNavigate(); + const queryClient = useQueryClient(); + const { user } = useAuth(); + const { addToast } = useToast(); + + // Estado do formulário + const [title, setTitle] = useState(''); + const [description, setDesc] = useState(''); + const [priority, setPriority] = useState('medium'); + const [categoryId, setCategoryId] = useState(undefined); + + // Query: categorias + const { data: categories = [], isLoading: isLoadingCategories } = useQuery({ + queryKey: ['categories'], + queryFn: () => api.categories.list(), + }); + + // Mutation: criação do ticket via API + const { mutate: createTicket, isPending } = useMutation({ + mutationFn: () => + api.tickets.create({ + title: title.trim(), + description: description.trim(), + priority, + categoryId, + creatorId: user!.id, + }), + onSuccess: (data) => { + void queryClient.invalidateQueries({ queryKey: ['tickets'] }); + addToast('Chamado criado com sucesso!', 'success'); + navigate(`/tickets/${data.id}`); + }, + onError: (err: any) => { + addToast(`Erro ao criar chamado: ${err.message}`, 'error'); + }, + }); + + function handleSubmit(e: FormEvent) { + e.preventDefault(); + createTicket(); + } + + return ( +
+ + {/* Cabeçalho da página */} +
+

Novo Chamado

+

+ Descreva o problema com o máximo de detalhes possível para agilizar o atendimento. +

+
+ + {/* Formulário */} +
+ + {/* Título */} +
+ + setTitle(e.target.value)} + required + minLength={5} + maxLength={150} + disabled={isPending} + aria-describedby="title-hint" + /> +

+ Seja objetivo — este é o resumo que aparece na fila dos agentes. +

+
+ + {/* Descrição */} +
+ +