feat: correo grupal por departamento para avisos de ticket nuevo (IT al grupo)

This commit is contained in:
Nurfog committed 2026-10-01 16:01:50 -03:00
1 parent 8b59680588
commit f9dc520f65
2 files changed
+52 -5

No files matched your search

@@ -20,6 +20,7 @@ export default function DepartmentsPage() {
const [editing, setEditing] = useState<Group | null>(null); const [editing, setEditing] = useState<Group | null>(null);
const [name, setName] = useState(""); const [name, setName] = useState("");
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [notificationEmail, setNotificationEmail] = useState("");
const [isActive, setIsActive] = useState(true); const [isActive, setIsActive] = useState(true);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [modalError, setModalError] = useState(""); const [modalError, setModalError] = useState("");
@@ -55,6 +56,7 @@ export default function DepartmentsPage() {
setEditing(null); setEditing(null);
setName(""); setName("");
setDescription(""); setDescription("");
setNotificationEmail("");
setIsActive(true); setIsActive(true);
setModalError(""); setModalError("");
setShowModal(true); setShowModal(true);
@@ -64,6 +66,7 @@ export default function DepartmentsPage() {
setEditing(g); setEditing(g);
setName(g.name); setName(g.name);
setDescription(g.description || ""); setDescription(g.description || "");
setNotificationEmail(g.notificationEmail || "");
setIsActive(g.isActive); setIsActive(g.isActive);
setModalError(""); setModalError("");
setShowModal(true); setShowModal(true);
@@ -74,17 +77,27 @@ export default function DepartmentsPage() {
setModalError("El nombre es requerido"); setModalError("El nombre es requerido");
return; return;
} }
if (notificationEmail.trim() && !notificationEmail.includes("@")) {
setModalError("Correo de aviso inválido");
return;
}
setSaving(true); setSaving(true);
setModalError(""); setModalError("");
try { try {
const notifyValue = notificationEmail.trim() || null;
if (editing) { if (editing) {
await updateGroup(editing.id, { await updateGroup(editing.id, {
name: name.trim(), name: name.trim(),
description: description.trim() || undefined, description: description.trim() || undefined,
isActive, isActive,
notificationEmail: notifyValue,
}); });
} else { } else {
await createGroup({ name: name.trim(), description: description.trim() || undefined }); await createGroup({
name: name.trim(),
description: description.trim() || undefined,
notificationEmail: notifyValue,
});
} }
setShowModal(false); setShowModal(false);
fetchGroups(); fetchGroups();
@@ -145,6 +158,7 @@ export default function DepartmentsPage() {
<tr> <tr>
<th>Nombre</th> <th>Nombre</th>
<th>Descripción</th> <th>Descripción</th>
<th>Correo de aviso</th>
<th>Estado</th> <th>Estado</th>
<th className="text-end">Acciones</th> <th className="text-end">Acciones</th>
</tr> </tr>
@@ -152,13 +166,13 @@ export default function DepartmentsPage() {
<tbody> <tbody>
{loading ? ( {loading ? (
<tr> <tr>
<td colSpan={4} className="text-center py-4"> <td colSpan={5} className="text-center py-4">
<div className="spinner-border spinner-border-sm text-primary" role="status"></div> <div className="spinner-border spinner-border-sm text-primary" role="status"></div>
</td> </td>
</tr> </tr>
) : groups.length === 0 ? ( ) : groups.length === 0 ? (
<tr> <tr>
<td colSpan={4} className="empty-state"> <td colSpan={5} className="empty-state">
<i className="bi bi-diagram-3 d-block"></i> <i className="bi bi-diagram-3 d-block"></i>
<h5>Sin departamentos</h5> <h5>Sin departamentos</h5>
<p className="text-muted mb-0">Crea el primero para organizar los tickets</p> <p className="text-muted mb-0">Crea el primero para organizar los tickets</p>
@@ -169,6 +183,16 @@ export default function DepartmentsPage() {
<tr key={g.id}> <tr key={g.id}>
<td className="fw-semibold">{g.name}</td> <td className="fw-semibold">{g.name}</td>
<td className="text-muted">{g.description || "—"}</td> <td className="text-muted">{g.description || "—"}</td>
<td className="text-muted" style={{ fontSize: "0.85rem" }}>
{g.notificationEmail ? (
<span title="Los avisos van a esta casilla y no a las personas">
<i className="bi bi-envelope-at me-1"></i>
{g.notificationEmail}
</span>
) : (
"Personas del depto."
)}
</td>
<td> <td>
{g.isActive ? ( {g.isActive ? (
<span className="badge bg-success">Activo</span> <span className="badge bg-success">Activo</span>
@@ -233,6 +257,19 @@ export default function DepartmentsPage() {
onChange={(e) => setDescription(e.target.value)} onChange={(e) => setDescription(e.target.value)}
/> />
</div> </div>
<div className="mb-3">
<label className="form-label">Correo de aviso (grupo)</label>
<input
type="email"
className="form-control"
value={notificationEmail}
onChange={(e) => setNotificationEmail(e.target.value)}
placeholder="ej: departamentodeinformatica@norteamericano.cl"
/>
<div className="form-text">
Si se define, los tickets nuevos avisan a esta casilla y no a las personas del departamento.
</div>
</div>
{editing && ( {editing && (
<div className="form-check form-switch"> <div className="form-check form-switch">
<input <input
+12 -2
View File
@@ -244,6 +244,7 @@ export interface Group {
name: string; name: string;
description?: string; description?: string;
isActive: boolean; isActive: boolean;
notificationEmail?: string | null;
} }
export interface KBArticle { export interface KBArticle {
@@ -288,7 +289,11 @@ export interface DashboardStats {
} }
// ==================== Departamentos ==================== // ==================== Departamentos ====================
export async function createGroup(data: { name: string; description?: string }) { export async function createGroup(data: {
name: string;
description?: string;
notificationEmail?: string | null;
}) {
return fetchApi<Group>("/api/helpdesk/groups", { return fetchApi<Group>("/api/helpdesk/groups", {
method: "POST", method: "POST",
body: JSON.stringify(data), body: JSON.stringify(data),
@@ -297,7 +302,12 @@ export async function createGroup(data: { name: string; description?: string })
export async function updateGroup( export async function updateGroup(
id: number, id: number,
data: { name?: string; description?: string; isActive?: boolean } data: {
name?: string;
description?: string;
isActive?: boolean;
notificationEmail?: string | null;
}
) { ) {
return fetchApi<void>(`/api/helpdesk/groups/${id}`, { return fetchApi<void>(`/api/helpdesk/groups/${id}`, {
method: "PUT", method: "PUT",