Improve SSR auth bootstrap and align GraphQL communication fields
This commit is contained in:
@@ -486,8 +486,10 @@ const renderedPilotMessages = computed<PilotMessage[]>(() => {
|
||||
});
|
||||
|
||||
async function gqlFetch<TData>(query: string, variables?: Record<string, unknown>) {
|
||||
const headers = process.server ? useRequestHeaders(["cookie"]) : undefined;
|
||||
const result = await $fetch<{ data?: TData; errors?: Array<{ message: string }> }>("/api/graphql", {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: { query, variables },
|
||||
});
|
||||
|
||||
@@ -531,6 +533,26 @@ async function loadMe() {
|
||||
authMe.value = data.me;
|
||||
}
|
||||
|
||||
const authResolved = ref(false);
|
||||
|
||||
async function bootstrapSession() {
|
||||
try {
|
||||
await loadMe();
|
||||
if (!authMe.value) {
|
||||
pilotMessages.value = [];
|
||||
chatConversations.value = [];
|
||||
return;
|
||||
}
|
||||
await Promise.all([loadPilotMessages(), loadChatConversations(), refreshCrmData()]);
|
||||
} catch {
|
||||
authMe.value = null;
|
||||
pilotMessages.value = [];
|
||||
chatConversations.value = [];
|
||||
} finally {
|
||||
authResolved.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function createNewChatConversation() {
|
||||
if (chatCreating.value) return;
|
||||
chatThreadPickerOpen.value = false;
|
||||
@@ -1118,6 +1140,10 @@ async function rollbackLatestChangeSet() {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.server) {
|
||||
await bootstrapSession();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
pilotHeaderText.value = pilotHeaderPhrases[Math.floor(Math.random() * pilotHeaderPhrases.length)] ?? "Every step moves you forward";
|
||||
pilotMicSupported.value =
|
||||
@@ -1125,12 +1151,14 @@ onMounted(() => {
|
||||
typeof MediaRecorder !== "undefined" &&
|
||||
Boolean(navigator.mediaDevices?.getUserMedia);
|
||||
|
||||
loadMe()
|
||||
.then(() => {
|
||||
startPilotBackgroundPolling();
|
||||
return Promise.all([loadPilotMessages(), loadChatConversations(), refreshCrmData()]);
|
||||
})
|
||||
.catch(() => {});
|
||||
if (!authResolved.value) {
|
||||
void bootstrapSession().finally(() => {
|
||||
if (authMe.value) startPilotBackgroundPolling();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (authMe.value) startPilotBackgroundPolling();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -1764,7 +1792,7 @@ const latestPinnedItem = computed(() => selectedCommPinnedStream.value[0] ?? nul
|
||||
|
||||
const latestPinnedLabel = computed(() => {
|
||||
if (!latestPinnedItem.value) return "No pinned items yet";
|
||||
if (latestPinnedItem.value.kind === "pin") return latestPinnedItem.value.text;
|
||||
if (latestPinnedItem.value.kind === "pin") return stripPinnedPrefix(latestPinnedItem.value.text);
|
||||
return `${latestPinnedItem.value.event.title} · ${formatDay(latestPinnedItem.value.event.start)}`;
|
||||
});
|
||||
|
||||
@@ -1772,6 +1800,10 @@ function normalizePinText(value: string) {
|
||||
return String(value ?? "").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function stripPinnedPrefix(value: string) {
|
||||
return String(value ?? "").replace(/^\s*(закреплено|pinned)\s*:\s*/i, "").trim();
|
||||
}
|
||||
|
||||
function isPinnedText(contact: string, value: string) {
|
||||
const contactName = String(contact ?? "").trim();
|
||||
const text = normalizePinText(value);
|
||||
@@ -1781,7 +1813,7 @@ function isPinnedText(contact: string, value: string) {
|
||||
|
||||
function entryPinText(entry: any): string {
|
||||
if (!entry) return "";
|
||||
if (entry.kind === "pin") return normalizePinText(entry.text ?? "");
|
||||
if (entry.kind === "pin") return normalizePinText(stripPinnedPrefix(entry.text ?? ""));
|
||||
if (entry.kind === "recommendation") return normalizePinText(entry.card?.text ?? "");
|
||||
if (entry.kind === "event" || entry.kind === "eventAlert" || entry.kind === "eventLog") {
|
||||
return normalizePinText(entry.event?.note || entry.event?.title || "");
|
||||
@@ -2353,7 +2385,11 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
|
||||
<template>
|
||||
<div class="h-[100dvh] overflow-hidden bg-base-200/35">
|
||||
<div v-if="!authMe" class="flex h-full items-center justify-center px-3">
|
||||
<div v-if="!authResolved" class="flex h-full items-center justify-center">
|
||||
<span class="loading loading-spinner loading-md text-base-content/70" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="!authMe" class="flex h-full items-center justify-center px-3">
|
||||
<div class="card w-full max-w-sm border border-base-300 bg-base-100 shadow-sm">
|
||||
<div class="card-body p-5">
|
||||
<h1 class="text-lg font-semibold">Login</h1>
|
||||
@@ -3246,7 +3282,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
||||
>
|
||||
<div v-if="entry.kind === 'pin'" class="flex justify-center">
|
||||
<article class="w-full max-w-[460px] rounded-xl border border-base-300 bg-base-100 p-3">
|
||||
<p class="text-sm text-base-content/85">{{ entry.text }}</p>
|
||||
<p class="text-sm text-base-content/85">{{ stripPinnedPrefix(entry.text) }}</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -314,8 +314,8 @@ async function main() {
|
||||
contactId: c.id,
|
||||
text:
|
||||
idx % 3 === 0
|
||||
? "Закреплено: уточнить владельца ERP, владельца данных и целевой квартал запуска."
|
||||
: "Закреплено: держать коммуникацию вокруг одного KPI и следующего шага.",
|
||||
? "Уточнить владельца ERP, владельца данных и целевой квартал запуска."
|
||||
: "Держать коммуникацию вокруг одного KPI и следующего шага.",
|
||||
})),
|
||||
});
|
||||
|
||||
|
||||
@@ -388,7 +388,7 @@ async function getDashboard(auth: AuthContext | null) {
|
||||
kind: m.kind === "CALL" ? "call" : "message",
|
||||
direction: m.direction === "IN" ? "in" : "out",
|
||||
text: m.content,
|
||||
audioUrl: m.audioUrl ?? "",
|
||||
audioUrl: "",
|
||||
duration: m.durationSec ? new Date(m.durationSec * 1000).toISOString().slice(14, 19) : "",
|
||||
transcript: Array.isArray(m.transcriptJson) ? ((m.transcriptJson as any) as string[]) : [],
|
||||
}));
|
||||
@@ -542,7 +542,6 @@ async function createCommunication(auth: AuthContext | null, input: {
|
||||
direction: input?.direction === "in" ? "IN" : "OUT",
|
||||
channel: toDbChannel(input?.channel ?? "Phone") as any,
|
||||
content: (input?.text ?? "").trim(),
|
||||
audioUrl: (input?.audioUrl ?? "").trim() || null,
|
||||
durationSec: typeof input?.durationSec === "number" ? input.durationSec : null,
|
||||
transcriptJson: Array.isArray(input?.transcript) ? input.transcript : undefined,
|
||||
occurredAt,
|
||||
|
||||
Reference in New Issue
Block a user