136 lines
4.5 KiB
TypeScript
136 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { createTicket, getGroups, type Group } from "@/lib/api";
|
|
import Link from "next/link";
|
|
|
|
export default function NewTicketPage() {
|
|
const router = useRouter();
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState("");
|
|
|
|
const [title, setTitle] = useState("");
|
|
const [description, setDescription] = useState("");
|
|
const [groups, setGroups] = useState<Group[]>([]);
|
|
const [groupId, setGroupId] = useState("");
|
|
|
|
useEffect(() => {
|
|
getGroups().then(setGroups).catch(() => []);
|
|
}, []);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError("");
|
|
if (!groupId) {
|
|
setError("Debes seleccionar un departamento");
|
|
return;
|
|
}
|
|
setLoading(true);
|
|
try {
|
|
// La prioridad siempre es Media al crear: la ajusta el equipo después.
|
|
const result = await createTicket({ title, description, priority: "Medium", groupId: Number(groupId) });
|
|
router.push(`/tickets/${result.id}`);
|
|
} catch (err: unknown) {
|
|
setError(err instanceof Error ? err.message : "Error al crear el ticket");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<div className="page-header">
|
|
<div>
|
|
<Link href="/tickets" className="text-decoration-none text-muted" style={{ fontSize: "0.85rem" }}>
|
|
<i className="bi bi-arrow-left me-1"></i>Volver a tickets
|
|
</Link>
|
|
<h1 className="mt-1">Nuevo Ticket</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="row">
|
|
<div className="col-12">
|
|
<div className="card">
|
|
<div className="card-body p-4">
|
|
{error && (
|
|
<div className="alert alert-danger py-2" role="alert">
|
|
<i className="bi bi-exclamation-circle me-2"></i>
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="mb-3">
|
|
<label className="form-label fw-semibold">Asunto</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="Describe brevemente el problema o solicitud"
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
required
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
|
|
<div className="mb-3">
|
|
<label className="form-label fw-semibold">Descripción</label>
|
|
<textarea
|
|
className="form-control"
|
|
rows={6}
|
|
placeholder="Proporciona detalles sobre el problema, pasos para reproducir, etc."
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
required
|
|
></textarea>
|
|
</div>
|
|
|
|
<div className="mb-4">
|
|
<label className="form-label fw-semibold">Departamento</label>
|
|
<select
|
|
className="form-select"
|
|
value={groupId}
|
|
onChange={(e) => setGroupId(e.target.value)}
|
|
required
|
|
>
|
|
<option value="">Seleccionar departamento...</option>
|
|
{groups.map((g) => (
|
|
<option key={g.id} value={g.id}>
|
|
{g.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="d-flex gap-2">
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary px-4"
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<>
|
|
<span className="spinner-border spinner-border-sm me-2" role="status"></span>
|
|
Creando...
|
|
</>
|
|
) : (
|
|
<>
|
|
<i className="bi bi-plus-lg me-2"></i>
|
|
Crear Ticket
|
|
</>
|
|
)}
|
|
</button>
|
|
<Link href="/tickets" className="btn btn-outline-secondary">
|
|
Cancelar
|
|
</Link>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|