Persist call transcripts and source call audio from DB
This commit is contained in:
@@ -8,6 +8,7 @@ import logoutMutation from "./graphql/operations/logout.graphql?raw";
|
|||||||
import logPilotNoteMutation from "./graphql/operations/log-pilot-note.graphql?raw";
|
import logPilotNoteMutation from "./graphql/operations/log-pilot-note.graphql?raw";
|
||||||
import createCalendarEventMutation from "./graphql/operations/create-calendar-event.graphql?raw";
|
import createCalendarEventMutation from "./graphql/operations/create-calendar-event.graphql?raw";
|
||||||
import createCommunicationMutation from "./graphql/operations/create-communication.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 updateFeedDecisionMutation from "./graphql/operations/update-feed-decision.graphql?raw";
|
||||||
import chatConversationsQuery from "./graphql/operations/chat-conversations.graphql?raw";
|
import chatConversationsQuery from "./graphql/operations/chat-conversations.graphql?raw";
|
||||||
import createChatConversationMutation from "./graphql/operations/create-chat-conversation.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;
|
const itemId = item.id;
|
||||||
if (callTranscriptLoading.value[itemId]) return;
|
if (callTranscriptLoading.value[itemId]) return;
|
||||||
if (callTranscriptText.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);
|
const audioUrl = getCallAudioUrl(item);
|
||||||
if (!audioUrl) {
|
if (!audioUrl) {
|
||||||
@@ -2003,6 +2011,11 @@ async function transcribeCallItem(item: CommItem) {
|
|||||||
});
|
});
|
||||||
const text = String(result?.text ?? "").trim();
|
const text = String(result?.text ?? "").trim();
|
||||||
callTranscriptText.value[itemId] = text || "(empty transcript)";
|
callTranscriptText.value[itemId] = text || "(empty transcript)";
|
||||||
|
await gqlFetch<{ updateCommunicationTranscript: { ok: boolean; id: string } }>(updateCommunicationTranscriptMutation, {
|
||||||
|
id: itemId,
|
||||||
|
transcript: text ? [text] : [],
|
||||||
|
});
|
||||||
|
await refreshCrmData();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
callTranscriptError.value[itemId] = String(error?.message ?? error ?? "Transcription failed");
|
callTranscriptError.value[itemId] = String(error?.message ?? error ?? "Transcription failed");
|
||||||
} finally {
|
} 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">
|
<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" />
|
<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>
|
</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>
|
<span class="shrink-0 text-xs text-base-content/75">{{ selectedCommPinnedStream.length }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -3231,8 +3245,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
>
|
>
|
||||||
<div v-if="entry.kind === 'pin'" class="flex justify-center">
|
<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">
|
<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="text-sm text-base-content/85">{{ entry.text }}</p>
|
||||||
<p class="mt-1 text-sm text-base-content/85">{{ entry.text }}</p>
|
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
mutation UpdateCommunicationTranscriptMutation($id: ID!, $transcript: [String!]!) {
|
||||||
|
updateCommunicationTranscript(id: $id, transcript: $transcript) {
|
||||||
|
ok
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -552,6 +552,29 @@ async function createCommunication(auth: AuthContext | null, input: {
|
|||||||
return { ok: true, id: created.id };
|
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) {
|
async function updateFeedDecision(auth: AuthContext | null, id: string, decision: "accepted" | "rejected" | "pending", decisionNote?: string) {
|
||||||
const ctx = requireAuth(auth);
|
const ctx = requireAuth(auth);
|
||||||
|
|
||||||
@@ -769,6 +792,7 @@ export const crmGraphqlSchema = buildSchema(`
|
|||||||
toggleContactPin(contact: String!, text: String!): PinToggleResult!
|
toggleContactPin(contact: String!, text: String!): PinToggleResult!
|
||||||
createCalendarEvent(input: CreateCalendarEventInput!): CalendarEvent!
|
createCalendarEvent(input: CreateCalendarEventInput!): CalendarEvent!
|
||||||
createCommunication(input: CreateCommunicationInput!): MutationWithIdResult!
|
createCommunication(input: CreateCommunicationInput!): MutationWithIdResult!
|
||||||
|
updateCommunicationTranscript(id: ID!, transcript: [String!]!): MutationWithIdResult!
|
||||||
updateFeedDecision(id: ID!, decision: String!, decisionNote: String): MutationWithIdResult!
|
updateFeedDecision(id: ID!, decision: String!, decisionNote: String): MutationWithIdResult!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1027,6 +1051,11 @@ export const crmGraphqlRoot = {
|
|||||||
context: GraphQLContext,
|
context: GraphQLContext,
|
||||||
) => createCommunication(context.auth, args.input),
|
) => createCommunication(context.auth, args.input),
|
||||||
|
|
||||||
|
updateCommunicationTranscript: async (
|
||||||
|
args: { id: string; transcript: string[] },
|
||||||
|
context: GraphQLContext,
|
||||||
|
) => updateCommunicationTranscript(context.auth, args.id, args.transcript),
|
||||||
|
|
||||||
updateFeedDecision: async (
|
updateFeedDecision: async (
|
||||||
args: { id: string; decision: "accepted" | "rejected" | "pending"; decisionNote?: string },
|
args: { id: string; decision: "accepted" | "rejected" | "pending"; decisionNote?: string },
|
||||||
context: GraphQLContext,
|
context: GraphQLContext,
|
||||||
|
|||||||
Reference in New Issue
Block a user