Files
clube67_newwhats.local/newwhats.clube67.com/newwhats.local/frontend/store/secretariaStore.ts
T
VPS 4 Deploy Agent 2f8c04a0a7
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
chore(ops): restore all source files in newwhats.clube67.com
2026-05-18 03:28:29 +02:00

280 lines
11 KiB
TypeScript

import { create } from 'zustand'
import {
secAgentApi, secNodeApi, secConvApi, secCalApi, secNumberApi,
type SecAgent, type BrainNode, type SecConversation, type SecMessage, type CalendarSlot, type SecNumber,
} from '../services/secretariaApi'
interface SecretariaState {
// Data
agents: SecAgent[]
selectedAgent: SecAgent | null
nodes: BrainNode[]
conversations: SecConversation[]
selectedConversation: SecConversation | null
messages: SecMessage[]
calendarSlots: CalendarSlot[]
// Loading flags
loadingAgents: boolean
loadingNodes: boolean
loadingConversations: boolean
loadingMessages: boolean
sendingMessage: boolean
loadingCalendar: boolean
// ── Agents ────────────────────────────────────────────────────────────────
loadAgents: () => Promise<void>
selectAgent: (a: SecAgent | null) => void
createAgent: (d: Partial<SecAgent>) => Promise<SecAgent>
updateAgent: (id: string, d: Partial<SecAgent>) => Promise<void>
deleteAgent: (id: string) => Promise<void>
// ── Brain Nodes ───────────────────────────────────────────────────────────
loadNodes: (agentId: string) => Promise<void>
createNode: (agentId: string, d: Partial<BrainNode>) => Promise<void>
updateNode: (id: string, d: Partial<BrainNode>) => Promise<void>
deleteNode: (id: string) => Promise<void>
// ── Conversations ─────────────────────────────────────────────────────────
loadConversations: (agentId?: string) => Promise<void>
selectConversation: (c: SecConversation | null) => void
createConversation: (agentId: string, contactName: string) => Promise<SecConversation>
deleteConversation: (id: string) => Promise<void>
updateConversationStatus: (id: string, status: SecConversation['status']) => Promise<void>
// ── Messages ──────────────────────────────────────────────────────────────
loadMessages: (convId: string) => Promise<void>
sendMessage: (convId: string, text: string) => Promise<void>
// ── Calendar ──────────────────────────────────────────────────────────────
loadCalendar: (params?: { from?: string; to?: string; status?: string }) => Promise<void>
createCalendarSlot: (d: Partial<CalendarSlot>) => Promise<void>
updateCalendarSlot: (id: string, d: Partial<CalendarSlot>) => Promise<void>
deleteCalendarSlot: (id: string) => Promise<void>
// ── Numbers ───────────────────────────────────────────────────────────────
numbers: SecNumber[]
loadingNumbers: boolean
loadNumbers: () => Promise<void>
createNumber: (d: Partial<SecNumber>) => Promise<void>
updateNumber: (id: string, d: Partial<SecNumber>) => Promise<void>
deleteNumber: (id: string) => Promise<void>
}
export const useSecretariaStore = create<SecretariaState>((set, get) => ({
agents: [], selectedAgent: null, nodes: [],
conversations: [], selectedConversation: null, messages: [],
calendarSlots: [], numbers: [],
loadingAgents: false, loadingNodes: false, loadingConversations: false,
loadingMessages: false, sendingMessage: false, loadingCalendar: false, loadingNumbers: false,
// ── Agents ────────────────────────────────────────────────────────────────
loadAgents: async () => {
set({ loadingAgents: true })
try {
const agents = await secAgentApi.list()
set({ agents })
// Auto-seleciona o primeiro agente se nenhum selecionado
if (!get().selectedAgent && agents.length > 0) {
get().selectAgent(agents[0])
}
} finally {
set({ loadingAgents: false })
}
},
selectAgent: (a) => {
set({ selectedAgent: a, nodes: [], conversations: [], selectedConversation: null, messages: [] })
if (a) {
get().loadNodes(a.id)
get().loadConversations(a.id)
}
},
createAgent: async (d) => {
const agent = await secAgentApi.create(d)
set((s) => ({ agents: [...s.agents, agent] }))
return agent
},
updateAgent: async (id, d) => {
const updated = await secAgentApi.update(id, d)
set((s) => ({
agents: s.agents.map((a) => (a.id === id ? updated : a)),
selectedAgent: s.selectedAgent?.id === id ? updated : s.selectedAgent,
}))
},
deleteAgent: async (id) => {
await secAgentApi.delete(id)
set((s) => ({
agents: s.agents.filter((a) => a.id !== id),
selectedAgent: s.selectedAgent?.id === id ? null : s.selectedAgent,
}))
},
// ── Brain Nodes ───────────────────────────────────────────────────────────
loadNodes: async (agentId) => {
set({ loadingNodes: true })
try {
const nodes = await secNodeApi.list(agentId)
set({ nodes })
} finally {
set({ loadingNodes: false })
}
},
createNode: async (agentId, d) => {
const node = await secNodeApi.create(agentId, d)
set((s) => ({ nodes: [...s.nodes, node] }))
},
updateNode: async (id, d) => {
const updated = await secNodeApi.update(id, d)
set((s) => ({ nodes: s.nodes.map((n) => (n.id === id ? updated : n)) }))
},
deleteNode: async (id) => {
await secNodeApi.delete(id)
set((s) => ({ nodes: s.nodes.filter((n) => n.id !== id) }))
},
// ── Conversations ─────────────────────────────────────────────────────────
loadConversations: async (agentId) => {
set({ loadingConversations: true })
try {
const conversations = await secConvApi.list(agentId)
set({ conversations })
} finally {
set({ loadingConversations: false })
}
},
selectConversation: (c) => {
set({ selectedConversation: c, messages: [] })
if (c) get().loadMessages(c.id)
},
createConversation: async (agentId, contactName) => {
const conv = await secConvApi.create(agentId, contactName)
set((s) => ({ conversations: [conv, ...s.conversations] }))
return conv
},
deleteConversation: async (id) => {
await secConvApi.delete(id)
set((s) => ({
conversations: s.conversations.filter((c) => c.id !== id),
selectedConversation: s.selectedConversation?.id === id ? null : s.selectedConversation,
}))
},
updateConversationStatus: async (id, status) => {
const updated = await secConvApi.patch(id, { status })
set((s) => ({
conversations: s.conversations.map((c) => (c.id === id ? updated : c)),
selectedConversation: s.selectedConversation?.id === id ? updated : s.selectedConversation,
}))
},
// ── Messages ──────────────────────────────────────────────────────────────
loadMessages: async (convId) => {
set({ loadingMessages: true })
try {
const messages = await secConvApi.messages(convId)
set({ messages })
} finally {
set({ loadingMessages: false })
}
},
sendMessage: async (convId, text) => {
// Adiciona mensagem do usuário imediatamente (optimistic)
const tempId = `temp-${Date.now()}`
const userMsg: SecMessage = {
id: tempId, conversation_id: convId, role: 'user', content: text, created_at: new Date().toISOString(),
}
set((s) => ({ messages: [...s.messages, userMsg], sendingMessage: true }))
try {
const { reply } = await secConvApi.chat(convId, text)
const aiMsg: SecMessage = {
id: `ai-${Date.now()}`, conversation_id: convId, role: 'assistant', content: reply,
created_at: new Date().toISOString(),
}
set((s) => ({ messages: [...s.messages, aiMsg] }))
// Atualiza updated_at da conversa na lista
set((s) => ({
conversations: s.conversations.map((c) =>
c.id === convId ? { ...c, updated_at: new Date().toISOString() } : c,
),
}))
} finally {
set({ sendingMessage: false })
}
},
// ── Calendar ──────────────────────────────────────────────────────────────
loadCalendar: async (params) => {
set({ loadingCalendar: true })
try {
const calendarSlots = await secCalApi.list(params)
set({ calendarSlots })
} finally {
set({ loadingCalendar: false })
}
},
createCalendarSlot: async (d) => {
const slot = await secCalApi.create(d)
set((s) => ({ calendarSlots: [...s.calendarSlots, slot].sort((a, b) => a.date.localeCompare(b.date)) }))
},
updateCalendarSlot: async (id, d) => {
const updated = await secCalApi.update(id, d)
set((s) => ({ calendarSlots: s.calendarSlots.map((sl) => (sl.id === id ? updated : sl)) }))
},
deleteCalendarSlot: async (id) => {
await secCalApi.delete(id)
set((s) => ({ calendarSlots: s.calendarSlots.filter((sl) => sl.id !== id) }))
},
// ── Numbers ───────────────────────────────────────────────────────────────
loadNumbers: async () => {
set({ loadingNumbers: true })
try {
const numbers = await secNumberApi.list()
set({ numbers })
} finally {
set({ loadingNumbers: false })
}
},
createNumber: async (d) => {
const num = await secNumberApi.assign(d)
set((s) => ({
// upsert: substitui se já existia o mesmo instance_id
numbers: [...s.numbers.filter((n) => n.instance_id !== num.instance_id), num]
.sort((a, b) => a.priority - b.priority),
}))
},
updateNumber: async (id, d) => {
const updated = await secNumberApi.update(id, d)
set((s) => ({ numbers: s.numbers.map((n) => (n.id === id ? updated : n)).sort((a, b) => a.priority - b.priority) }))
},
deleteNumber: async (id) => {
await secNumberApi.delete(id)
set((s) => ({ numbers: s.numbers.filter((n) => n.id !== id) }))
},
}))