feat: web UI chat render, panels, presence + analytics

This commit is contained in:
h
2026-05-31 19:41:01 +02:00
parent 75425d1bee
commit ed469ba8dd
83 changed files with 6034 additions and 136 deletions
+131
View File
@@ -1,14 +1,25 @@
import { request } from "$lib/api/client";
import type {
Account,
CaptureToggles,
Chat,
Folder,
JobStatus,
JobView,
MediaVersion,
MediaView,
MessageVersion,
MessageView,
PeerView,
PinnedView,
PolicyChatKind,
PolicyCreate,
PolicyRecord,
PresenceHourly,
PresenceSample,
ResponseStats,
SearchHit,
VolumeBucket,
} from "$lib/api/types";
import { accounts } from "$lib/stores/accounts.svelte";
@@ -29,6 +40,43 @@ export function listFolders(): Promise<Folder[]> {
return request<Folder[]>("/folders", { account: true });
}
export function listPolicies(): Promise<PolicyRecord[]> {
return request<PolicyRecord[]>("/policy", { account: true });
}
export function createPolicy(body: PolicyCreate): Promise<PolicyRecord> {
return request<PolicyRecord>("/policy", {
method: "POST",
body: { ...body, account_id: accounts.selectedId },
});
}
export function updatePolicy(
id: number,
toggles: CaptureToggles
): Promise<PolicyRecord> {
return request<PolicyRecord>(`/policy/${id}`, {
method: "PUT",
body: toggles,
});
}
export function deletePolicy(id: number): Promise<void> {
return request<void>(`/policy/${id}`, { method: "DELETE" });
}
export function effectivePolicy(query: {
chat_id: number;
is_bot?: boolean;
is_contact?: boolean | null;
kind: PolicyChatKind;
}): Promise<CaptureToggles> {
return request<CaptureToggles>("/policy/effective", {
account: true,
query,
});
}
export function listMessages(
chatId: number,
options: Page & { include_deleted?: boolean } = {}
@@ -39,6 +87,55 @@ export function listMessages(
});
}
export function getCurrentPresence(
peerId: number
): Promise<PresenceSample | null> {
return request<PresenceSample | null>("/presence/current", {
account: true,
query: { peer_id: peerId },
});
}
export function getPresenceHistory(
peerId: number,
page: Page = {}
): Promise<PresenceSample[]> {
return request<PresenceSample[]>("/presence", {
account: true,
query: { peer_id: peerId, ...page },
});
}
export function getPresenceHourly(peerId: number): Promise<PresenceHourly[]> {
return request<PresenceHourly[]>("/presence/hourly", {
account: true,
query: { peer_id: peerId },
});
}
export function getMessageVolume(
chatId: number,
days = 90
): Promise<VolumeBucket[]> {
return request<VolumeBucket[]>("/analytics/volume", {
account: true,
query: { chat_id: chatId, days },
});
}
export function getResponseStats(chatId: number): Promise<ResponseStats> {
return request<ResponseStats>("/analytics/response-time", {
account: true,
query: { chat_id: chatId },
});
}
export function getPinned(chatId: number): Promise<PinnedView | null> {
return request<PinnedView | null>(`/chats/${chatId}/pinned`, {
account: true,
});
}
export function listMessageVersions(
chatId: number,
messageId: number
@@ -83,6 +180,30 @@ export function getJob(jobId: number): Promise<JobView> {
return request<JobView>(`/jobs/${jobId}`, { account: true });
}
export function listJobs(status?: JobStatus): Promise<JobView[]> {
return request<JobView[]>("/jobs", {
account: true,
query: status ? { status } : {},
});
}
export function enqueueBackfill(
chatId: number,
media: boolean
): Promise<{ job_id: number }> {
return request<{ job_id: number }>("/backfill", {
method: "POST",
body: { account_id: accounts.selectedId, chat_id: chatId, media },
});
}
export function syncDialogs(): Promise<{ job_id: number }> {
return request<{ job_id: number }>("/dialogs/sync", {
method: "POST",
body: { account_id: accounts.selectedId },
});
}
export function getMediaVersions(
chatId: number,
messageId: number
@@ -105,6 +226,16 @@ export function getMessageMedia(
});
}
export function searchMessages(
query: string,
options: Page & { chat_id?: number } = {}
): Promise<SearchHit[]> {
return request<SearchHit[]>("/search", {
account: true,
query: { query, ...options },
});
}
export function fetchMedia(
chatId: number,
messageId: number