Persist call transcripts and source call audio from DB

This commit is contained in:
Ruslan Bakiev
2026-02-19 14:05:54 +07:00
parent 23a4deba37
commit ddfb558948
3 changed files with 50 additions and 2 deletions

View File

@@ -8,6 +8,7 @@ import logoutMutation from "./graphql/operations/logout.graphql?raw";
import logPilotNoteMutation from "./graphql/operations/log-pilot-note.graphql?raw";
import createCalendarEventMutation from "./graphql/operations/create-calendar-event.graphql?raw";
import createCommunicationMutation from "./graphql/operations/create-communication.graphql?raw";
import updateCommunicationTranscriptMutation from "./graphql/operations/update-communication-transcript.graphql?raw";
import updateFeedDecisionMutation from "./graphql/operations/update-feed-decision.graphql?raw";
import chatConversationsQuery from "./graphql/operations/chat-conversations.graphql?raw";
import createChatConversationMutation from "./graphql/operations/create-chat-conversation.graphql?raw";
@@ -1982,6 +1983,13 @@ async function transcribeCallItem(item: CommItem) {
const itemId = item.id;
if (callTranscriptLoading.value[itemId]) return;
if (callTranscriptText.value[itemId]) return;
if (Array.isArray(item.transcript) && item.transcript.length) {
const persisted = item.transcript.map((line) => String(line ?? "").trim()).filter(Boolean).join("\n");
if (persisted) {
callTranscriptText.value[itemId] = persisted;
return;
}
}
const audioUrl = getCallAudioUrl(item);
if (!audioUrl) {
@@ -2003,6 +2011,11 @@ async function transcribeCallItem(item: CommItem) {
});
const text = String(result?.text ?? "").trim();
callTranscriptText.value[itemId] = text || "(empty transcript)";
await gqlFetch<{ updateCommunicationTranscript: { ok: boolean; id: string } }>(updateCommunicationTranscriptMutation, {
id: itemId,
transcript: text ? [text] : [],
});
await refreshCrmData();
} catch (error: any) {
callTranscriptError.value[itemId] = String(error?.message ?? error ?? "Transcription failed");
} finally {
@@ -3221,6 +3234,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
<svg viewBox="0 0 24 24" class="h-4 w-4 shrink-0 fill-current text-base-content/75">
<path d="M14 3a1 1 0 0 0-1 1v4.59l-1.7 1.7A2 2 0 0 0 10.7 12H8v2h2.7a2 2 0 0 0 .6 1.41L13 17.1V21l2-1.2v-2.7l1.7-1.7A2 2 0 0 0 17.3 14H20v-2h-2.7a2 2 0 0 0-.6-1.41L15 8.9V4a1 1 0 0 0-1-1Z" />
</svg>
<span class="min-w-0 flex-1 truncate text-xs text-base-content/80">{{ latestPinnedLabel }}</span>
<span class="shrink-0 text-xs text-base-content/75">{{ selectedCommPinnedStream.length }}</span>
</button>
@@ -3231,8 +3245,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-[11px] font-semibold uppercase tracking-wide text-base-content/65">Pinned note</p>
<p class="mt-1 text-sm text-base-content/85">{{ entry.text }}</p>
<p class="text-sm text-base-content/85">{{ entry.text }}</p>
</article>
</div>

View File

@@ -0,0 +1,6 @@
mutation UpdateCommunicationTranscriptMutation($id: ID!, $transcript: [String!]!) {
updateCommunicationTranscript(id: $id, transcript: $transcript) {
ok
id
}
}

View File

@@ -552,6 +552,29 @@ async function createCommunication(auth: AuthContext | null, input: {
return { ok: true, id: created.id };
}
async function updateCommunicationTranscript(auth: AuthContext | null, id: string, transcript: string[]) {
const ctx = requireAuth(auth);
const messageId = String(id ?? "").trim();
if (!messageId) throw new Error("id is required");
const lines = Array.isArray(transcript)
? transcript.map((line) => String(line ?? "").trim()).filter(Boolean)
: [];
const updated = await prisma.contactMessage.updateMany({
where: {
id: messageId,
contact: { teamId: ctx.teamId },
},
data: {
transcriptJson: lines,
},
});
if (!updated.count) throw new Error("communication not found");
return { ok: true, id: messageId };
}
async function updateFeedDecision(auth: AuthContext | null, id: string, decision: "accepted" | "rejected" | "pending", decisionNote?: string) {
const ctx = requireAuth(auth);
@@ -769,6 +792,7 @@ export const crmGraphqlSchema = buildSchema(`
toggleContactPin(contact: String!, text: String!): PinToggleResult!
createCalendarEvent(input: CreateCalendarEventInput!): CalendarEvent!
createCommunication(input: CreateCommunicationInput!): MutationWithIdResult!
updateCommunicationTranscript(id: ID!, transcript: [String!]!): MutationWithIdResult!
updateFeedDecision(id: ID!, decision: String!, decisionNote: String): MutationWithIdResult!
}
@@ -1027,6 +1051,11 @@ export const crmGraphqlRoot = {
context: GraphQLContext,
) => createCommunication(context.auth, args.input),
updateCommunicationTranscript: async (
args: { id: string; transcript: string[] },
context: GraphQLContext,
) => updateCommunicationTranscript(context.auth, args.id, args.transcript),
updateFeedDecision: async (
args: { id: string; decision: "accepted" | "rejected" | "pending"; decisionNote?: string },
context: GraphQLContext,