Initial commit: HelpDesk ICHN - .NET 10 backend + Next.js 16 frontend

This commit is contained in:
Nurfog committed 2026-09-10 10:20:51 -03:00
commit 359f0caf85
90 files changed
+13395

No files matched your search

+252
View File
@@ -0,0 +1,252 @@
const API_BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:5000";
async function fetchApi<T>(
path: string,
options: RequestInit = {}
): Promise<T> {
const token =
typeof window !== "undefined" ? localStorage.getItem("helpdesk_token") : null;
const headers: Record<string, string> = {
"Content-Type": "application/json",
...((options.headers as Record<string, string>) || {}),
};
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
const res = await fetch(`${API_BASE}${path}`, {
...options,
headers,
});
if (!res.ok) {
const errorData = await res.json().catch(() => ({}));
throw new Error(errorData.message || `API Error: ${res.status}`);
}
if (res.status === 204) return undefined as 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),
});
}
export async function getMe() {
return fetchApi<User>("/api/auth/me");
}
// Tickets
export async function getTickets(params?: {
status?: string;
priority?: string;
assigneeId?: string;
requesterId?: string;
search?: string;
page?: number;
pageSize?: number;
}) {
const query = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== "") query.set(key, String(value));
});
}
return fetchApi<{ tickets: Ticket[]; total: number; page: number; pageSize: number }>(
`/api/tickets?${query.toString()}`
);
}
export async function getTicket(id: string) {
return fetchApi<Ticket>(`/api/tickets/${id}`);
}
export async function createTicket(data: {
subject: string;
description: string;
priority?: string;
category?: string;
assigneeId?: number;
}) {
return fetchApi<Ticket>("/api/tickets", {
method: "POST",
body: JSON.stringify(data),
});
}
export async function updateTicket(id: string, data: Partial<Ticket>) {
return fetchApi<Ticket>(`/api/tickets/${id}`, {
method: "PUT",
body: JSON.stringify(data),
});
}
export async function deleteTicket(id: string) {
return fetchApi<void>(`/api/tickets/${id}`, {
method: "DELETE",
});
}
export async function updateTicketStatus(id: string, status: number) {
return fetchApi<Ticket>(`/api/tickets/${id}/status`, {
method: "PUT",
body: JSON.stringify({ status }),
});
}
export async function assignTicket(id: string, assigneeId: number) {
return fetchApi<Ticket>(`/api/tickets/${id}/assign`, {
method: "PUT",
body: JSON.stringify({ assigneeId }),
});
}
export async function addComment(
ticketId: string,
content: string,
isInternal: boolean = false
) {
return fetchApi<Comment>(`/api/tickets/${ticketId}/comments`, {
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
export async function getKBArticles() {
return fetchApi<KBArticle[]>("/api/kb/articles/published");
}
export async function searchKB(query: string) {
return fetchApi<KBArticle[]>(`/api/kb/search?q=${encodeURIComponent(query)}`);
}
// Dashboard
export async function getDashboardStats() {
return fetchApi<DashboardStats>("/api/dashboard/stats");
}
// Groups & SLA
export async function getGroups() {
return fetchApi<Group[]>("/api/groups");
}
export async function getSLAPolicies() {
return fetchApi<SLAPolicy[]>("/api/sla");
}
// Types
export interface User {
id: number;
name: string;
email: string;
role: "admin" | "agent" | "customer";
createdAt?: string;
}
export interface Ticket {
id: number;
ticketNumber: string;
subject: string;
description: string;
status: string;
priority: string;
category: string;
requesterId: number;
requester?: User;
assigneeId: number | null;
assignee?: User;
groupId: number | null;
group?: Group;
comments?: Comment[];
createdAt: string;
updatedAt: string;
}
export interface Comment {
id: number;
ticketId: number;
userId: number;
user?: User;
content: string;
isInternal: boolean;
createdAt: string;
}
export interface Group {
id: number;
name: string;
description?: string;
}
export interface KBArticle {
id: number;
title: string;
content: string;
category: string;
author?: User;
publishedAt: string;
}
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>;
}
export interface DashboardStats {
totalTickets: number;
openTickets: number;
pendingTickets: number;
resolvedTickets: number;
closedTickets: number;
avgResponseTime: string;
ticketsByPriority: Record<string, number>;
recentTickets: Ticket[];
agentWorkload: { agent: string; count: number }[];
}