Adapt frontend to SAM API: RUT auth, new types, helpdesk endpoints
- Login: RUT-based (SAM auth) instead of email - API client: updated all endpoints to /api/helpdesk/* - Types: match SAM response format (nombre, apPaterno, perfil) - Dashboard: uses new DashboardStats with dailyCounts - Tickets: title instead of subject, requesterName/assigneeName - KB: categoryName/authorName instead of nested objects - Removed register page (SAM manages users)
This commit is contained in:
1 parent
0cca57f21e
commit
f56f6365d2
11 files changed
+285
-718
No files matched your search
+107
-107
@@ -16,10 +16,7 @@ async function fetchApi<T>(
|
||||
headers["Authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const res = await fetch(`${API_BASE}${path}`, {
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
const res = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
||||
|
||||
if (!res.ok) {
|
||||
const errorData = await res.json().catch(() => ({}));
|
||||
@@ -30,36 +27,25 @@ async function fetchApi<T>(
|
||||
return res.json();
|
||||
}
|
||||
|
||||
// Auth
|
||||
export async function login(email: string, password: string) {
|
||||
return fetchApi<{ token: string; user: User }>("/api/auth/login", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function register(data: {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role?: string;
|
||||
}) {
|
||||
return fetchApi<{ token: string; user: User }>("/api/auth/register", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
// ==================== Auth ====================
|
||||
// SAM uses RUT-based auth — adapt as needed
|
||||
export async function login(rut: string, password: string) {
|
||||
return fetchApi<{ success: boolean; message?: string; token?: string; user?: User }>(
|
||||
"/api/auth/login",
|
||||
{ method: "POST", body: JSON.stringify({ rut, password }) }
|
||||
);
|
||||
}
|
||||
|
||||
export async function getMe() {
|
||||
return fetchApi<User>("/api/auth/me");
|
||||
}
|
||||
|
||||
// Tickets
|
||||
// ==================== Tickets ====================
|
||||
export async function getTickets(params?: {
|
||||
status?: string;
|
||||
priority?: string;
|
||||
assigneeId?: string;
|
||||
requesterId?: string;
|
||||
assigneeId?: number;
|
||||
groupId?: number;
|
||||
search?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
@@ -67,138 +53,151 @@ export async function getTickets(params?: {
|
||||
const query = new URLSearchParams();
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== "") query.set(key, String(value));
|
||||
if (value !== undefined && value !== "" && value !== null)
|
||||
query.set(key, String(value));
|
||||
});
|
||||
}
|
||||
return fetchApi<{ tickets: Ticket[]; total: number; page: number; pageSize: number }>(
|
||||
`/api/tickets?${query.toString()}`
|
||||
);
|
||||
return fetchApi<TicketListResult>(`/api/helpdesk/tickets?${query.toString()}`);
|
||||
}
|
||||
|
||||
export async function getTicket(id: string) {
|
||||
return fetchApi<Ticket>(`/api/tickets/${id}`);
|
||||
export async function getTicket(id: number) {
|
||||
return fetchApi<Ticket>(`/api/helpdesk/ticket/${id}`);
|
||||
}
|
||||
|
||||
export async function createTicket(data: {
|
||||
subject: string;
|
||||
title: string;
|
||||
description: string;
|
||||
priority?: string;
|
||||
category?: string;
|
||||
assigneeId?: number;
|
||||
groupId?: number;
|
||||
slaPolicyId?: number;
|
||||
}) {
|
||||
return fetchApi<Ticket>("/api/tickets", {
|
||||
return fetchApi<{ id: number }>("/api/helpdesk/tickets", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateTicket(id: string, data: Partial<Ticket>) {
|
||||
return fetchApi<Ticket>(`/api/tickets/${id}`, {
|
||||
export async function updateTicket(id: number, data: {
|
||||
title?: string;
|
||||
description?: string;
|
||||
priority?: string;
|
||||
assigneeId?: number;
|
||||
groupId?: number;
|
||||
}) {
|
||||
return fetchApi<void>(`/api/helpdesk/ticket/${id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteTicket(id: string) {
|
||||
return fetchApi<void>(`/api/tickets/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
export async function deleteTicket(id: number) {
|
||||
return fetchApi<void>(`/api/helpdesk/ticket/${id}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export async function updateTicketStatus(id: string, status: number) {
|
||||
return fetchApi<Ticket>(`/api/tickets/${id}/status`, {
|
||||
export async function updateTicketStatus(id: number, status: string) {
|
||||
return fetchApi<void>(`/api/helpdesk/ticket/${id}/status`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ status }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function assignTicket(id: string, assigneeId: number) {
|
||||
return fetchApi<Ticket>(`/api/tickets/${id}/assign`, {
|
||||
export async function assignTicket(id: number, assigneeId: number, groupId?: number) {
|
||||
return fetchApi<void>(`/api/helpdesk/ticket/${id}/assign`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ assigneeId }),
|
||||
body: JSON.stringify({ assigneeId, groupId }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getTicketStats() {
|
||||
return fetchApi<DashboardStats>("/api/helpdesk/stats");
|
||||
}
|
||||
|
||||
// ==================== Comments ====================
|
||||
export async function getComments(ticketId: number) {
|
||||
return fetchApi<Comment[]>(`/api/helpdesk/comments/${ticketId}`);
|
||||
}
|
||||
|
||||
export async function addComment(
|
||||
ticketId: string,
|
||||
ticketId: number,
|
||||
content: string,
|
||||
isInternal: boolean = false
|
||||
) {
|
||||
return fetchApi<Comment>(`/api/tickets/${ticketId}/comments`, {
|
||||
return fetchApi<{ id: number }>(`/api/helpdesk/comments/${ticketId}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ content, isInternal }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getTicketStats() {
|
||||
return fetchApi<TicketStats>("/api/tickets/stats");
|
||||
}
|
||||
|
||||
// Users
|
||||
export async function getUsers() {
|
||||
return fetchApi<User[]>("/api/users");
|
||||
}
|
||||
|
||||
export async function getAgents() {
|
||||
return fetchApi<User[]>("/api/users/agents");
|
||||
}
|
||||
|
||||
// Knowledge Base
|
||||
// ==================== Knowledge Base ====================
|
||||
export async function getKBArticles() {
|
||||
return fetchApi<KBArticle[]>("/api/kb/articles/published");
|
||||
return fetchApi<KBArticle[]>("/api/helpdesk/kb/published");
|
||||
}
|
||||
|
||||
export async function searchKB(query: string) {
|
||||
return fetchApi<KBArticle[]>(`/api/kb/search?q=${encodeURIComponent(query)}`);
|
||||
return fetchApi<KBArticle[]>(`/api/helpdesk/kb/search?q=${encodeURIComponent(query)}`);
|
||||
}
|
||||
|
||||
// Dashboard
|
||||
export async function getDashboardStats() {
|
||||
return fetchApi<DashboardStats>("/api/dashboard/stats");
|
||||
export async function getKBCategories() {
|
||||
return fetchApi<KBCategory[]>("/api/helpdesk/kb/categories");
|
||||
}
|
||||
|
||||
// Groups & SLA
|
||||
// ==================== Groups & SLA ====================
|
||||
export async function getGroups() {
|
||||
return fetchApi<Group[]>("/api/groups");
|
||||
return fetchApi<Group[]>("/api/helpdesk/groups");
|
||||
}
|
||||
|
||||
export async function getSLAPolicies() {
|
||||
return fetchApi<SLAPolicy[]>("/api/sla");
|
||||
return fetchApi<SLAPolicy[]>("/api/helpdesk/sla");
|
||||
}
|
||||
|
||||
// Types
|
||||
// ==================== Types ====================
|
||||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
usuarioId: number;
|
||||
nombre: string;
|
||||
apPaterno: string;
|
||||
apMaterno?: string;
|
||||
email: string;
|
||||
role: "admin" | "agent" | "customer";
|
||||
createdAt?: string;
|
||||
perfil: string;
|
||||
sedeId?: number;
|
||||
telefono?: string;
|
||||
}
|
||||
|
||||
export interface Ticket {
|
||||
id: number;
|
||||
ticketNumber: string;
|
||||
subject: string;
|
||||
title: string;
|
||||
description: string;
|
||||
status: string;
|
||||
priority: string;
|
||||
category: string;
|
||||
ticketNumber: string;
|
||||
requesterId: number;
|
||||
requester?: User;
|
||||
assigneeId: number | null;
|
||||
assignee?: User;
|
||||
groupId: number | null;
|
||||
group?: Group;
|
||||
comments?: Comment[];
|
||||
requesterName?: string;
|
||||
requesterEmail?: string;
|
||||
assigneeId?: number;
|
||||
assigneeName?: string;
|
||||
groupId?: number;
|
||||
groupName?: string;
|
||||
slaPolicyId?: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
firstResponseAt?: string;
|
||||
resolvedAt?: string;
|
||||
closedAt?: string;
|
||||
}
|
||||
|
||||
export interface TicketListResult {
|
||||
items: Ticket[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
id: number;
|
||||
ticketId: number;
|
||||
userId: number;
|
||||
user?: User;
|
||||
userName?: string;
|
||||
content: string;
|
||||
isInternal: boolean;
|
||||
createdAt: string;
|
||||
@@ -208,45 +207,46 @@ export interface Group {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface KBArticle {
|
||||
id: number;
|
||||
title: string;
|
||||
content: string;
|
||||
category: string;
|
||||
author?: User;
|
||||
publishedAt: string;
|
||||
categoryId: number;
|
||||
categoryName?: string;
|
||||
authorId: number;
|
||||
authorName?: string;
|
||||
isPublished: boolean;
|
||||
viewCount: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface KBCategory {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
sortOrder: number;
|
||||
}
|
||||
|
||||
export interface SLAPolicy {
|
||||
id: number;
|
||||
name: string;
|
||||
priority: string;
|
||||
responseTime: string;
|
||||
resolutionTime: string;
|
||||
}
|
||||
|
||||
export interface TicketStats {
|
||||
total: number;
|
||||
open: number;
|
||||
pending: number;
|
||||
resolved: number;
|
||||
closed: number;
|
||||
inProgress: number;
|
||||
avgResponseTime: string;
|
||||
byPriority: Record<string, number>;
|
||||
byCategory: Record<string, number>;
|
||||
description?: string;
|
||||
responseTimeHours: number;
|
||||
resolutionTimeHours: number;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface DashboardStats {
|
||||
totalTickets: number;
|
||||
openTickets: number;
|
||||
pendingTickets: number;
|
||||
inProgressTickets: number;
|
||||
resolvedTickets: number;
|
||||
closedTickets: number;
|
||||
avgResponseTime: string;
|
||||
ticketsByPriority: Record<string, number>;
|
||||
recentTickets: Ticket[];
|
||||
agentWorkload: { agent: string; count: number }[];
|
||||
unassignedTickets: number;
|
||||
criticalTickets: number;
|
||||
dailyCounts: { date: string; count: number }[];
|
||||
}
|
||||
Reference in new issue
Block a user