muitas melhorias pra descrever
This commit is contained in:
1 parent
89b08a19ef
commit
c8fba4d240
21 files changed
+971
-17
No files matched your search
@@ -32,6 +32,39 @@ export interface QuizCard {
|
||||
dueAt: string;
|
||||
}
|
||||
|
||||
export interface PageVisit {
|
||||
id: string;
|
||||
path: string;
|
||||
system: string | null;
|
||||
ip: string;
|
||||
port: number | null;
|
||||
country: string | null;
|
||||
city: string | null;
|
||||
userAgent: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface FeedbackEntry {
|
||||
id: string;
|
||||
message: string;
|
||||
email: string | null;
|
||||
rating: number | null;
|
||||
page: string | null;
|
||||
ip: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface AdminStats {
|
||||
onlineNow: number;
|
||||
totalVisits: number;
|
||||
visitsLast24h: number;
|
||||
visitsLast7d: number;
|
||||
visitsBySystem: { system: string | null; count: number }[];
|
||||
visitsByCountry: { country: string; count: number }[];
|
||||
recentVisits: PageVisit[];
|
||||
feedback: FeedbackEntry[];
|
||||
}
|
||||
|
||||
const BASE = "/api";
|
||||
|
||||
async function getJson<T>(url: string): Promise<T> {
|
||||
@@ -40,6 +73,22 @@ async function getJson<T>(url: string): Promise<T> {
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
async function postJson<T>(url: string, body: unknown, token?: string): Promise<T> {
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => null);
|
||||
throw new Error(data?.error ?? `POST ${url} failed: ${res.status}`);
|
||||
}
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
systems: () => getJson<AnatomicalSystem[]>(`${BASE}/systems`),
|
||||
|
||||
@@ -72,6 +121,28 @@ export const api = {
|
||||
if (!res.ok) throw new Error(`quizReview failed: ${res.status}`);
|
||||
return res.json();
|
||||
},
|
||||
|
||||
onlineCount: () => getJson<{ count: number }>(`${BASE}/analytics/online-count`),
|
||||
|
||||
trackVisit: (path: string, system?: string) =>
|
||||
postJson<{ ok: true }>(`${BASE}/analytics/visit`, { path, system }).catch(() => {}),
|
||||
|
||||
heartbeat: (sessionId: string) =>
|
||||
postJson<{ ok: true }>(`${BASE}/analytics/heartbeat`, { sessionId }).catch(() => {}),
|
||||
|
||||
sendFeedback: (data: { message: string; email?: string; rating?: number; page?: string }) =>
|
||||
postJson<{ ok: true }>(`${BASE}/feedback`, data),
|
||||
|
||||
adminLogin: (email: string, password: string) =>
|
||||
postJson<{ token: string }>(`${BASE}/admin/login`, { email, password }),
|
||||
|
||||
adminStats: async (token: string): Promise<AdminStats> => {
|
||||
const res = await fetch(`${BASE}/admin/stats`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
if (!res.ok) throw new Error(`GET admin/stats failed: ${res.status}`);
|
||||
return res.json();
|
||||
},
|
||||
};
|
||||
|
||||
export function modelUrl(modelFile: string): string {
|
||||
|
||||
Reference in new issue
Block a user