refactor: migrate CRM data layer from manual gqlFetch to Apollo Client
Replace custom gqlFetch() with proper Apollo useQuery/useMutation hooks powered by codegen-generated TypedDocumentNode types. Key changes: - Add GraphQL SDL schema file and codegen config for typescript-vue-apollo - Replace all 28 raw .graphql imports with generated typed documents - Add 12 useQuery() hooks with cache-and-network fetch policy - Add 17 useMutation() hooks with surgical refetchQueries per mutation - Optimistic cache update for setContactInboxHidden (instant archive UX) - Fix contact list subtitle: show lastText instead of channel name - Migrate login page from gqlFetch to useMutation - WebSocket realtime now calls Apollo refetch instead of full data reload Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1689
frontend/graphql/generated.ts
Normal file
1689
frontend/graphql/generated.ts
Normal file
File diff suppressed because it is too large
Load Diff
269
frontend/graphql/schema.graphql
Normal file
269
frontend/graphql/schema.graphql
Normal file
@@ -0,0 +1,269 @@
|
||||
type Query {
|
||||
me: MePayload!
|
||||
chatMessages: [PilotMessage!]!
|
||||
chatConversations: [Conversation!]!
|
||||
contacts: [Contact!]!
|
||||
communications: [CommItem!]!
|
||||
contactInboxes: [ContactInbox!]!
|
||||
calendar: [CalendarEvent!]!
|
||||
deals: [Deal!]!
|
||||
feed: [FeedCard!]!
|
||||
pins: [CommPin!]!
|
||||
documents: [WorkspaceDocument!]!
|
||||
getClientTimeline(contactId: ID!, limit: Int): [ClientTimelineItem!]!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
login(phone: String!, password: String!): MutationResult!
|
||||
logout: MutationResult!
|
||||
createChatConversation(title: String): Conversation!
|
||||
selectChatConversation(id: ID!): MutationResult!
|
||||
archiveChatConversation(id: ID!): MutationResult!
|
||||
sendPilotMessage(text: String!): MutationResult!
|
||||
confirmLatestChangeSet: MutationResult!
|
||||
rollbackLatestChangeSet: MutationResult!
|
||||
rollbackChangeSetItems(changeSetId: ID!, itemIds: [ID!]!): MutationResult!
|
||||
logPilotNote(text: String!): MutationResult!
|
||||
toggleContactPin(contact: String!, text: String!): PinToggleResult!
|
||||
createCalendarEvent(input: CreateCalendarEventInput!): CalendarEvent!
|
||||
archiveCalendarEvent(input: ArchiveCalendarEventInput!): CalendarEvent!
|
||||
createCommunication(input: CreateCommunicationInput!): MutationWithIdResult!
|
||||
createWorkspaceDocument(input: CreateWorkspaceDocumentInput!): WorkspaceDocument!
|
||||
deleteWorkspaceDocument(id: ID!): MutationWithIdResult!
|
||||
updateCommunicationTranscript(id: ID!, transcript: [String!]!): MutationWithIdResult!
|
||||
updateFeedDecision(id: ID!, decision: String!, decisionNote: String): MutationWithIdResult!
|
||||
setContactInboxHidden(inboxId: ID!, hidden: Boolean!): MutationResult!
|
||||
}
|
||||
|
||||
type MutationResult {
|
||||
ok: Boolean!
|
||||
}
|
||||
|
||||
type MutationWithIdResult {
|
||||
ok: Boolean!
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type PinToggleResult {
|
||||
ok: Boolean!
|
||||
pinned: Boolean!
|
||||
}
|
||||
|
||||
input CreateCalendarEventInput {
|
||||
title: String!
|
||||
start: String!
|
||||
end: String
|
||||
contact: String
|
||||
note: String
|
||||
archived: Boolean
|
||||
archiveNote: String
|
||||
}
|
||||
|
||||
input ArchiveCalendarEventInput {
|
||||
id: ID!
|
||||
archiveNote: String
|
||||
}
|
||||
|
||||
input CreateCommunicationInput {
|
||||
contact: String!
|
||||
channel: String
|
||||
kind: String
|
||||
direction: String
|
||||
text: String
|
||||
audioUrl: String
|
||||
at: String
|
||||
durationSec: Int
|
||||
transcript: [String!]
|
||||
}
|
||||
|
||||
input CreateWorkspaceDocumentInput {
|
||||
title: String!
|
||||
owner: String
|
||||
scope: String!
|
||||
summary: String!
|
||||
body: String
|
||||
}
|
||||
|
||||
type MePayload {
|
||||
user: MeUser!
|
||||
team: MeTeam!
|
||||
conversation: Conversation!
|
||||
}
|
||||
|
||||
type MeUser {
|
||||
id: ID!
|
||||
phone: String!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type MeTeam {
|
||||
id: ID!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Conversation {
|
||||
id: ID!
|
||||
title: String!
|
||||
createdAt: String!
|
||||
updatedAt: String!
|
||||
lastMessageAt: String
|
||||
lastMessageText: String
|
||||
}
|
||||
|
||||
type PilotMessage {
|
||||
id: ID!
|
||||
role: String!
|
||||
text: String!
|
||||
messageKind: String
|
||||
requestId: String
|
||||
eventType: String
|
||||
phase: String
|
||||
transient: Boolean
|
||||
thinking: [String!]!
|
||||
tools: [String!]!
|
||||
toolRuns: [PilotToolRun!]!
|
||||
changeSetId: String
|
||||
changeStatus: String
|
||||
changeSummary: String
|
||||
changeItems: [PilotChangeItem!]!
|
||||
createdAt: String!
|
||||
}
|
||||
|
||||
type PilotChangeItem {
|
||||
id: ID!
|
||||
entity: String!
|
||||
entityId: String
|
||||
action: String!
|
||||
title: String!
|
||||
before: String!
|
||||
after: String!
|
||||
rolledBack: Boolean!
|
||||
}
|
||||
|
||||
type PilotToolRun {
|
||||
name: String!
|
||||
status: String!
|
||||
input: String!
|
||||
output: String!
|
||||
at: String!
|
||||
}
|
||||
|
||||
type ClientTimelineItem {
|
||||
id: ID!
|
||||
contactId: String!
|
||||
contentType: String!
|
||||
contentId: String!
|
||||
datetime: String!
|
||||
message: CommItem
|
||||
calendarEvent: CalendarEvent
|
||||
recommendation: FeedCard
|
||||
document: WorkspaceDocument
|
||||
}
|
||||
|
||||
type Contact {
|
||||
id: ID!
|
||||
name: String!
|
||||
avatar: String!
|
||||
channels: [String!]!
|
||||
lastContactAt: String!
|
||||
description: String!
|
||||
}
|
||||
|
||||
type CommItem {
|
||||
id: ID!
|
||||
at: String!
|
||||
contactId: String!
|
||||
contact: String!
|
||||
contactInboxId: String!
|
||||
sourceExternalId: String!
|
||||
sourceTitle: String!
|
||||
channel: String!
|
||||
kind: String!
|
||||
direction: String!
|
||||
text: String!
|
||||
audioUrl: String!
|
||||
duration: String!
|
||||
waveform: [Float!]!
|
||||
transcript: [String!]!
|
||||
deliveryStatus: String
|
||||
}
|
||||
|
||||
type ContactInbox {
|
||||
id: ID!
|
||||
contactId: String!
|
||||
contactName: String!
|
||||
channel: String!
|
||||
sourceExternalId: String!
|
||||
title: String!
|
||||
isHidden: Boolean!
|
||||
lastMessageAt: String!
|
||||
updatedAt: String!
|
||||
}
|
||||
|
||||
type CalendarEvent {
|
||||
id: ID!
|
||||
title: String!
|
||||
start: String!
|
||||
end: String!
|
||||
contact: String!
|
||||
note: String!
|
||||
isArchived: Boolean!
|
||||
createdAt: String!
|
||||
archiveNote: String!
|
||||
archivedAt: String!
|
||||
}
|
||||
|
||||
type Deal {
|
||||
id: ID!
|
||||
contact: String!
|
||||
title: String!
|
||||
stage: String!
|
||||
amount: String!
|
||||
nextStep: String!
|
||||
summary: String!
|
||||
currentStepId: String!
|
||||
steps: [DealStep!]!
|
||||
}
|
||||
|
||||
type DealStep {
|
||||
id: ID!
|
||||
title: String!
|
||||
description: String!
|
||||
status: String!
|
||||
dueAt: String!
|
||||
order: Int!
|
||||
completedAt: String!
|
||||
}
|
||||
|
||||
type FeedCard {
|
||||
id: ID!
|
||||
at: String!
|
||||
contact: String!
|
||||
text: String!
|
||||
proposal: FeedProposal!
|
||||
decision: String!
|
||||
decisionNote: String!
|
||||
}
|
||||
|
||||
type FeedProposal {
|
||||
title: String!
|
||||
details: [String!]!
|
||||
key: String!
|
||||
}
|
||||
|
||||
type CommPin {
|
||||
id: ID!
|
||||
contact: String!
|
||||
text: String!
|
||||
}
|
||||
|
||||
type WorkspaceDocument {
|
||||
id: ID!
|
||||
title: String!
|
||||
type: String!
|
||||
owner: String!
|
||||
scope: String!
|
||||
updatedAt: String!
|
||||
summary: String!
|
||||
body: String!
|
||||
}
|
||||
Reference in New Issue
Block a user